undici icon indicating copy to clipboard operation
undici copied to clipboard

database is locked

Open ronag opened this issue 8 months ago • 16 comments

Error: database is locked
    at get size (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/sqlite-cache-store.js:298:47)
    at #prune (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/sqlite-cache-store.js:270:14)
    at SqliteCacheStore.set (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/sqlite-cache-store.js:238:16)
    at CacheHandler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/interceptor/cache.js:159:19)
    at Handler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/utils.js:263:40)
    at Handler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/utils.js:263:40)
    at Handler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/interceptor/response-retry.js:202:20)
    at Handler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/utils.js:263:40)
    at Handler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/interceptor/dns.js:22:11)
    at Handler.onComplete (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/utils.js:263:40)

Something with the SQL database? I guess prune should at least catch and ignore the error?

ronag avatar Mar 28 '25 09:03 ronag

Not sure when/how this can occur.

ronag avatar Mar 28 '25 09:03 ronag

Might it be possible that you are running multiple servers connecting to the same SQLite instance? See https://www2.sqlite.org/cvstrac/wiki?p=DatabaseIsLocked for examples.

mcollina avatar Mar 28 '25 10:03 mcollina

Isn't the idea that the sql cache should work cross process? We should probably handle errors like these?

ronag avatar Mar 28 '25 10:03 ronag

Isn't the idea that the sql cache should work cross process? We should probably handle errors like these?

Yes! 100%. However I'm not really sure how to handle this (minus literally ignoring the error)

mcollina avatar Mar 28 '25 10:03 mcollina

I think this specific error can probably be ignored... though I'm not sure how/when this can otherwise also occur?

I'm still confused by this error in general. I thought sqlite was designed not to have this issue?

ronag avatar Mar 28 '25 10:03 ronag

I thought sqlite was designed not to have this issue?

Unfortunately no, you can have this issue in SQLite as the link above describe. However, this shouldn't really be possible with how we are using SQLite in the cache store.

mcollina avatar Mar 28 '25 11:03 mcollina

However, this shouldn't really be possible with how we are using SQLite in the cache store.

Question is how I manage to trigger this then :D. It was in production so I don't have a repro. Will try to find a way to get that somehow...

ronag avatar Mar 28 '25 11:03 ronag

In theory, it shouldn't be an issue, but it could be an overlap between writes and reads? (attempting to read to the table while other request handling attempts to write within the same connection).

We are not safe to such conditions in the current implementation (as well it is just a rough bet)

metcoder95 avatar Mar 28 '25 11:03 metcoder95

There can be an overlap between write and reads only if two happen in parallel, but this shouldn't be possible because the SQLite docs clearly states that the locked status happens on the same connection. The usual case for that error is:

  • begin a transaction
  • do something async
  • ... in the meanwhile, something else try to access the same table/rows etc.

mcollina avatar Mar 28 '25 13:03 mcollina

I'm getting a lot of these errors in production.

ronag avatar Apr 02 '25 13:04 ronag

It's usually #findValue or get size()


Error: database is locked     at get size (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/sqlite-cache-store.js:298:47)     at #prune (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/sqlite-cache-store.js:270:14)     at SqliteCacheStore.set (file:///usr/src/app/node_modules/@nxtedition/nxt-undici/lib/sqlite-cache-store.js:238:16)
--



ronag avatar Apr 02 '25 13:04 ronag

I suspect our pruning logic is causing contention which makes sqlite timeout certain queries which leads to "database is locked" errors.

I'm going to avoid using maxEntryCount in production.

ronag avatar Apr 02 '25 14:04 ronag

Seeing the commit you added @ronag I'm wondering if we should better change the way we prune; currently it is using a setInterval with an arbitrary 10s value.

What about pruning upon read (if time expired) and offer the chance the flag to customize it (along with maxEntryCount), as well offer returning expired records before pruning.

metcoder95 avatar Apr 03 '25 06:04 metcoder95

I don't have any good ideas unfortunately. We've added a max entry age option to our cache interceptor (i.e. make sure deleteAt is always withing a week or so) and do deleteExpiredValuesQuery on every set. That seems to work well. Anything related to counting seems to be super slow and problematic.

Here is out "optimized" version: https://github.com/nxtedition/nxt-undici/blob/main/lib/sqlite-cache-store.js

Basically we have removed:

  • updateValueQuery
  • deleteOldValuesQuery
  • countEntriesQuery

Which we see as problematic.

ronag avatar Apr 03 '25 07:04 ronag

I don't have any good ideas unfortunately. We've added a max entry age option to our cache interceptor (i.e. make sure deleteAt is always withing a week or so) and do deleteExpiredValuesQuery on every set. That seems to work well.

Yeah, this seems to match what I had in mind; let me see if I can find some time next week to work on this.

metcoder95 avatar Apr 04 '25 08:04 metcoder95

So from what I've understood this is normal behavior and we should either yield and try again or just ignore the error. I would recommend the latter.

We can reduce the occurence by making the database operations as efficient as possible.

ronag avatar Apr 04 '25 12:04 ronag