Domanda

L'esecuzione di una rotaie sito utilizza SQLite3.

Circa una volta ogni 500 richieste o così, ho un

ActiveRecord::StatementInvalid (SQLite3::BusyException:il database è bloccato:...

Qual è il modo per risolvere questo problema che sarebbe minimamente invasiva per il mio codice?

Sto usando SQLLite, perché al momento è possibile memorizzare il DB nel controllo del codice sorgente che rende il backup naturale e si può spingere cambia molto rapidamente.Tuttavia, è ovvio che non è proprio impostare per l'accesso simultaneo.Io migrare al MySQL domani mattina.

È stato utile?

Soluzione

Per impostazione predefinita, sqlite restituisce immediatamente con un bloccato, occupato errore se il database è occupato e bloccato.Si può chiedere che attendere e continuare a provare per un po', prima di rinunciare.Questo di solito risolve il problema, a meno che non si dispone di 1000 thread di accedere al tuo db, quando sono d'accordo sqlite sarebbe inopportuno.

    // set SQLite to wait and retry for up to 100ms if database locked
    sqlite3_busy_timeout( db, 100 );

Altri suggerimenti

Lei ha detto che questo è un Guide del sito.Guide consente di impostare la SQLite tentativi di timeout nel database.yml file di configurazione:

production:
  adapter: sqlite3
  database: db/mysite_prod.sqlite3
  timeout: 10000

Il valore di timeout specificato in millisecondi.Aumentando a 10 o 15 secondi dovrebbe diminuire il numero di BusyExceptions si vede nel log.

Questa è solo una soluzione temporanea, anche se.Se il tuo sito ha bisogno di vera concorrenza, allora si dovrà migrare ad un altro db motore.

Tutte queste cose sono vere, ma non risponde alla domanda, che probabilmente è:perché la mia applicazione Rails sollevarlo un SQLite3::BusyException in produzione?

@Shalmanese:che cosa è la produzione di un ambiente di hosting come?È su un host condiviso?È la directory che contiene il database sqlite una condivisione NFS?(Probabilmente, su un host condiviso).

Questo problema probabilmente ha a che fare con il fenomeno del file di blocco w/ condivisioni NFS e SQLite mancanza di concorrenza.

Solo per la cronaca.In una applicazione Rails 2.3.8 abbiamo scoperto che Rails ignorava l'opzione "timeout" Rifkin Asburgo suggerito.

Dopo alcuni ulteriori indagini abbiamo trovato un possibile bug correlati in Rails dev: http://dev.rubyonrails.org/ticket/8811.E dopo un po ' di più di indagine abbiamo trovato la soluzione (testato con Rails 2.3.8):

Modificare questo ActiveRecord file:activerecord-2.3.8/lib/active_record/connection_adapters/sqlite_adapter.rb

Sostituire questo:

  def begin_db_transaction #:nodoc:
    catch_schema_changes { @connection.transaction }
  end

con

  def begin_db_transaction #:nodoc:
    catch_schema_changes { @connection.transaction(:immediate) }
  end

E questo è tutto!Non abbiamo notato un calo di prestazioni e ora l'app supporta molte più richieste senza rompere (si attende il timeout).Sqlite è bello!

bundle exec rake db:reset

Ha funzionato per me si resetta e spettacolo in attesa di migrazione.

Fonte: questo link

- Open the database
db = sqlite3.open("filename")

-- Ten attempts are made to proceed, if the database is locked
function my_busy_handler(attempts_made)
  if attempts_made < 10 then
    return true
  else
    return false
  end
end

-- Set the new busy handler
db:set_busy_handler(my_busy_handler)

-- Use the database
db:exec(...)

Sqlite è possibile consentire ad altri processi di aspettare fino a quando la corrente finito.

Io uso questa linea per la connessione quando so che può avere più processi tentando di accedere al DB Sqlite:

conn = sqlite3.connect('filename', isolation_level = 'esclusivo')

Secondo il Python Sqlite Documentazione:

È possibile controllare il tipo di INIZIARE dichiarazioni pysqlite implicitamente esegue (o nessuno) via isolation_level parametro per la chiamata connect (), o tramite il isolation_level proprietà di le connessioni.

Ho avuto un problema simile con la rake db:migrate.Problema è che la directory di lavoro è stata una condivisione SMB.Ho risolto copiando la cartella sul mio computer locale.

Se si dispone di questo problema, ma aumentare il timeout non cambia nulla, si potrebbe avere un altro problema di concorrenza con le transazioni, qui è in sintesi:

  1. Inizia una transazione (entra in possesso di un CONDIVISA serratura)
  2. Leggere alcuni dati dal DB (usiamo ancora le CONDIVISA serratura)
  3. Nel frattempo, un altro processo inizia una transazione e la scrittura dei dati (acquisizione del RISERVATI lock).
  4. Quindi si tenta di scrivere, si sta ora cercando di richiedere il RISERVATI blocco
  5. SQLite solleva l'eccezione SQLITE_BUSY immediatamente (indenpendently del vostro timeout) perché il precedente legge non possono essere più preciso con il tempo si può ottenere il RISERVATI serratura.

Un modo per risolvere questo problema è per la patch active_record sqlite adattatore per acquisire un RISERVATI blocco direttamente all'inizio della transazione, l'imbottitura :immediate opzione per il driver.Questo farà diminuire le prestazioni un po', ma almeno tutte le transazioni onora il timeout e si verifica uno dopo l'altro.Ecco come eseguire questa operazione utilizzando prepend (Ruby 2.0+) mettere questo in un inizializzatore di:

module SqliteTransactionFix
  def begin_db_transaction
    log('begin immediate transaction', nil) { @connection.transaction(:immediate) }
  end
end

module ActiveRecord
  module ConnectionAdapters
    class SQLiteAdapter < AbstractAdapter
      prepend SqliteTransactionFix
    end
  end
end

Leggi di più qui: https://rails.lighthouseapp.com/projects/8994/tickets/5941-sqlite3busyexceptions-are-raised-immediately-in-some-cases-despite-setting-sqlite3_busy_timeout

La maggior parte delle risposte sono per Rotaie, piuttosto che rubino grezzo, e OPs domanda È per le guide, che è bene.:)

Quindi, voglio solo lasciare questa soluzione qui dovrebbe qualsiasi rubino grezzo utenti hanno questo problema, e non utilizzando un yml di configurazione.

Dopo instancing la connessione, è possibile impostare come questo:

db = SQLite3::Database.new "#{path_to_your_db}/your_file.db"
db.busy_timeout=(15000) # in ms, meaning it will retry for 15 seconds before it raises an exception.
#This can be any number you want. Default value is 0.

Ciò che la tabella è accessibile quando il blocco viene rilevato?

Avete transazioni di lunga durata?

Si può capire di che le richieste erano ancora in fase di elaborazione quando il blocco è stato rilevato?

Argh - la rovina della mia esistenza, la scorsa settimana.Sqlite3 blocca il file db quando qualsiasi processo scrive per il database.CIOÈ ad ogni AGGIORNAMENTO/INSERIMENTO tipo di query (anche select count(*) per qualche motivo).Tuttavia, gestisce più si legge bene.

Così, ho finalmente ottenuto frustrato abbastanza per scrivere il mio thread codice di blocco intorno le chiamate al database.Da garantire che l'applicazione può avere un solo thread di scrittura al database in qualsiasi punto, sono stato in grado di scalare a 1000 di thread.

E sì, il suo lento come l'inferno.Ma è anche abbastanza veloce e corretto, che è una bella proprietà di avere.

Ho trovato uno stallo sqlite3 ruby estensione e fix qui:avere un andare con esso e vedere se questo risolve ur problema.

    https://github.com/dxj19831029/sqlite3-ruby

Ho aperto una richiesta di pull, nessuna risposta da loro più.

In ogni caso, alcune occupato eccezione è prevista, come descritto in sqlite3 stesso.

Essere consapevoli con questa condizione: sqlite occupato

    The presence of a busy handler does not guarantee that it will be invoked when there is 
    lock contention. If SQLite determines that invoking the busy handler could result in a 
    deadlock, it will go ahead and return SQLITE_BUSY or SQLITE_IOERR_BLOCKED instead of 
    invoking the busy handler. Consider a scenario where one process is holding a read lock 
    that it is trying to promote to a reserved lock and a second process is holding a reserved 
    lock that it is trying to promote to an exclusive lock. The first process cannot proceed 
    because it is blocked by the second and the second process cannot proceed because it is 
    blocked by the first. If both processes invoke the busy handlers, neither will make any 
    progress. Therefore, SQLite returns SQLITE_BUSY for the first process, hoping that this 
    will induce the first process to release its read lock and allow the second process to 
    proceed.

Se si soddisfano queste condizioni, il tempo di attesa non è più valido.Per evitare questo, di non mettere selezionare all'interno di iniziare/commit.o utilizzare un blocco esclusivo per iniziare/commit.

Spero che questo aiuta.:)

questo è spesso un consecutivi di guasto di più processi, l'accesso al database stesso, cioèse il "consenti solo un'istanza di" bandiera non è stata impostata RubyMine

Provare a eseguire le seguenti, può essere d'aiuto:

ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;") 

Da: Ruby:SQLite3::BusyException:il database è bloccato:

Questo può chiarire qualsiasi transazione che sorregge il sistema

Credo che questo accade quando una transazione volte.Si dovrebbe davvero essere l'uso di un "reale" del database.Qualcosa di simile a Filo o MySQL.Qualsiasi motivo si preferisce SQLite oltre le due precedenti opzioni?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top