SQLite3::BusyException
-
09-06-2019 - |
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.
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:
- Inizia una transazione (entra in possesso di un CONDIVISA serratura)
- Leggere alcuni dati dal DB (usiamo ancora le CONDIVISA serratura)
- Nel frattempo, un altro processo inizia una transazione e la scrittura dei dati (acquisizione del RISERVATI lock).
- Quindi si tenta di scrivere, si sta ora cercando di richiedere il RISERVATI blocco
- 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?