-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
database is locked #274
Comments
did you try _busy_timeout ? |
I wasn't aware of this, so no I hadn't. I would however has expected busy_timeout to only effect table locks but the error says "database is locked" is the error misleading? |
Just chiming in here, because I keep running into the same problem. I created a gist that can pretty reliably demonstrate the "database is locked" error. In this program I open 1 goroutine that writes 5000 times to one table (
What helped was using I really want to understand what's going on here and what's possible with sqlite3 in Go and what's not. My guess is that the |
Try to use transaction. https://gist.github.com/392999fb7472906afec8 |
That still results in |
Yer, I think the issue is with multiple threads and this message is equivalent to MySQL's deadlock detected, at least in our case, just MySQL's message is much clearer as it doesn't have multiple meanings which seems to be the case with sqlite. As none of the suggestions resolved the issue we've had to move away from sqlite unfortunately. I'll leave this bug open for others to contribute to, as its clearly wider spread. |
After some more reading and investigating, I think there is no proper solution where we can use multiple
(Highlight by me) The difference between sqlite and client/server databases (PostgreSQL, MySQL, ...) is that sqlite3 synchronizes access to the database file on the client-side, which is only possible if multiple threads/goroutines use the same client. If multiple clients (processes, goroutines with their own connection) use the same database file, the synchronization has to happen through the database file, in which case it's locked completely for every write action. So I'd say that the only solution is to use If anything of what I just said is wrong, please feel free to point it out. EDIT: Basically everything I said above is wrong. Apparently what I said about "thread-safety guarantees" only in "shared db instance across threads" is wrong. I found some sample code that shows usage of multiple db instances across multiple threads. Apparently that is the recommended way to go. This sqlite wiki entry also shows how sqlite behaves when used in multiple threads with multiple database instances: a lot of
Using one connection across multiple goroutines by using To update the conclusion at the top of the original version of this comment: I think there is no proper solution where we can use multiple |
The reason for this commit is this issue: #35 We ran into "database is locked" errors because of webhooks trying to read from the database on every log entry they received -- all the while are log entries being saved to the database. This change increases the busy timeout of the 5sec default value to 30sec. We'll see how that plays out. First tests confirm that it might slow the deployment process down but the errors disappear since the queries are retried. For more information, start reading here: * #35 * mattn/go-sqlite3#274 * https://gist.github.com/mrnugget/0eda3b2b53a70fa4a894
sqlite's "database is locked" error doesn't have multiple meanings. It means "I tried to obtain a lock on the database but failed because a separate process/connection held a conflicting lock". It's certainly not a deadlock, and is 100% expected in code creating multiple connections to the same DB.
To be more specific, sqlite's locking protocol allows for many readers and exactly one writer at any given time. ie. if you attempt to start two write transactions concurrently, the second will encounter SQLITE_BUSY. In rollback-journal mode, a read transaction will also encounter SQLITE_BUSY if it starts just as a write transaction enters its COMMIT phase - as hinted at in previous comments sqlite locks the entire database for the duration of COMMIT. This doesn't happen in WAL mode, because COMMIT doesn't need to update the main database file (just the write ahead journal). Anyway I think you came to the same conclusion, that SQLITE_BUSY is unavoidable when using multiple connections to the same database. Shared cache mode helps by introducing finer locks (table-level instead of database-level), but still if you have two transactions trying to update the same table at once the second will get SQLITE_BUSY. Setting the busy timeout is one way to cope with the situation - if your timeout is longer than the longest write transaction performed by your application that will mitigate many of the errors. But even then, you can still easily get SQLITE_BUSY via a transaction along the lines of:
If there is already a write transaction in progress on In rollback-journal mode, this is because sqlite knows it has a read-lock (from the earlier SELECT), so waiting is just going to hold up the existing write transaction. In WAL mode, this is because the earlier SELECT has the effect of pinning this transaction's view of the database to that point in time. sqlite knows the existing write transaction is going to update database potentially invalidating the SELECT results, and doesn't let you write to the database from this view of the past. For this conflicting writer case, the proper course of action is for the application to ROLLBACK the transaction, and potentially try again once the other write transaction has completed. I don't think there is any issue with go-sqlite3 here. |
fwiw, I stumbled across this ticket and subsequently found http://stackoverflow.com/questions/32479071/sqlite3-error-database-is-locked-in-golang After the addition |
Still experiencing this issue atm..
|
@jrots sqlite allows multiple readers but only a single writer at any one time. If you have multiple connections to the same DB, you will inevitably run into
Also note that |
update: ok reread your answer & you probably don't use that, just one initialisation and let it underlying create multiple connections without that setting |
I'm confused. I can't find the offical http://go-database-sql.org/modifying.html
Which implies that the goroutine which calls the If it is true, how does this behavior affectes other groutines? |
at windows 10, it can run very well, but in centos7.3 |
@mattn You should change this line in sqlite3.go:
as the following:
It fixes the problem. SQLITE_THREADSAFE must be 1 because a single DB connection can be used from multiple threads when using it from multiple goroutines. See https://www.sqlite.org/threadsafe.html and https://sqlite.org/compile.html#threadsafe https://golang.org/src/database/sql/doc.txt says:
|
@sqlitefan Thank you! If you still have issue, please reopen this. 👍 |
It seems the problem isn't fixed yet... 😢 |
Well, I don't make sure what is your issue but you can't avoid busy timeout. SQLite is not provide infinite blocking. |
Not sure if this is documented elsewhere, but I don't think the busy timeout makes any difference in my application unless I set the transaction lock parameter to "immediate" (so that the BEGIN statements are BEGIN IMMEDIATE, I think). Once that's set, the busy timeout is in effect, and things work much better. Maybe that's obvious to others, but it wasn't to me. So I thought I'd mention it here in case anybody else comes looking for it. Also possible I'm misunderstanding what I'm seeing. |
@runderwood That is not the case. With a rollback journal, the busy handler will be invoked if you attempt to read while another connection is writing, or vice versa. With WAL, those situations are not generally blocking so you won't normally observe the handler getting invoked. In both modes, starting an immediate transaction while another connection holds a write lock will invoke the busy handler. Note that there are some cases that can arise where SQLite will detect that invoking the busy handler is pointless and thus returns |
I feel like we're saying the same thing, at least mostly. But I can confidently say that, in my application, in WAL journal mode, if my transactions are initiated with the default (plain BEGIN), then the busy timeout seems to never be used. I get many immediate "database is locked" errors with concurrent write attempts. When I set this library's "_tx_lock" option to "immediate" (which initiates transactions with BEGIN IMMEDIATE), the busy timeout is observed and I see far, far fewer if any "database is locked" errors. As I read your comment, particularly the last sentence, I feel like you're confirming that this is the behavior one ought to expect. Of special interest is the scenario you describe -- which perfectly matches the situation that led me to here:
I believe this is what was happening to trigger immediate "database is locked" responses in my application. And, as you note, reconfiguring this to use BEGIN IMMEDIATE improves the outcome immensely (insofar as the busy timeout is actually in play). My purpose in posting here is this: reading through the docs and the comments here, it was not apparent to me that setting the busy timeout without changing the _tx_lock parameter to "immediate" would not make an appreciable difference. |
This is usually true, because WAL lifts the restrictions on concurrent reads/writes. However, if you use In other words, it is not technically the case that the busy handler only has an effect when you use Now, to be clear, there has been a suggestion in the past to use shared cache mode to avoid some "database is locked" issues, such as the aforementioned one. However, this is bad advice. Do not use shared cache mode. It only pushes the problem around, and can lead to similar looking "database table is locked" errors. The proper fix is (1) to use |
So, just to understand more clearly: why do concurrent transactions initiated with plain BEGIN and which first read then write not invoke the busy handler? |
The reason is that SQLite (the C library) is acting on limited information and making some pessimistic assumptions:
Consequently it cannot allow the transaction to succeed, since doing so would violate ACID (namely, isolation). Thus there is no point in invoking the busy handler, since the result will always be the same. |
That makes sense and is consistent with my experience. Had I read the SQLite transaction docs more closely, I would have sooner realized that the default transaction mode is DEFERRED and that the behavior I've observed (and which you've described) is exactly as one should expect:
👍 |
According to an old upstream issue [1]: "If the first statement after BEGIN DEFERRED is a SELECT, then a read transaction is started. Subsequent write statements will upgrade the transaction to a write transaction if possible, or return SQLITE_BUSY." So let's move the first SELECT under the same transaction as the table initialization. [NO NEW TESTS NEEDED] as it's a hard to cause race. [1] mattn/go-sqlite3#274 (comment) Fixes: containers#17859 Signed-off-by: Valentin Rothberg <[email protected]>
As shown in containers#17831, WAL mode plays a role in causing `database is locked` errors. Those are errors, in theory, should not happen as the DB should busy wait. mattn/go-sqlite3/issues/274 has some comments indicating that the busy handler behaves differently in WAL mode which may be an explanation to the error. For now, let's disable WAL mode and only re-enable it when we have clearer understanding of what's going on. The upstream issue along with the SQLite documentation do not give me the clear guidance that I would need. [NO NEW TESTS NEEDED] - flake is only reproducible in CI. Fixes: containers#18356 Signed-off-by: Valentin Rothberg <[email protected]>
@sqweek not solve it since sqlite3 run in serialised mode when THREADSAFE=1 is used
|
It seems the issue is with using double quotes instead of single quotes when providing string values (in your example you used I was getting the same error even without any routines or threads. Using single-quotes fixed the issue. |
Thank you so much for your comment, Klowner. You saved me. I was considering moving off of sqlite because of this. After swapping most of my queries away from using rows (I am fine with fetching all records at once) and auditing the stragglers to make sure they get speculation: I think I remember now that sqlite can fetch row-by-row in ways that traditional databases cannot. More specifically, because sqlite can fetch a row at a time from the disk, it doesn't fetch every row from the disk unless required. So essentially, if you do a query that returns |
We have a little app which has multiple goroutines all running queries and it often fails with "database is locked" :(
we're running with:
sqlite3 -version
3.10.2 2016-01-20 15:27:19 17efb4209f97fb4971656086b138599a91a75ff9
Is this a known issue?
The text was updated successfully, but these errors were encountered: