Skip to content

Add immediate transaction lock mode to SQLite backend#450

Merged
cschleiden merged 2 commits intomainfrom
copilot/adjust-sqlite-settings
Oct 24, 2025
Merged

Add immediate transaction lock mode to SQLite backend#450
cschleiden merged 2 commits intomainfrom
copilot/adjust-sqlite-settings

Conversation

Copy link
Contributor

Copilot AI commented Oct 24, 2025

Problem

Since enabling WAL mode, the SQLite backend can handle multiple connections, but this often leads to "database locked" errors under concurrent access. The root cause is that SQLite uses deferred transactions by default, which don't acquire write locks until the first write statement executes. By that time, there may be another non-reconcilable transaction in progress, and no retry is possible.

Solution

This PR adds the _txlock=immediate parameter to all SQLite DSN connection strings. With immediate transaction locking, write locks are acquired immediately when a transaction begins (BEGIN), rather than being deferred until the first write operation.

Combined with the existing busy_timeout=5000 pragma, this allows for proper serialization of concurrent writes. When a transaction attempts to acquire a lock and finds one already held, it will wait up to 5 seconds (the busy timeout period) for the lock to become available, significantly reducing database locked errors.

Changes

  • Modified NewInMemoryBackend() to include &_txlock=immediate in the in-memory database DSN
  • Modified NewSqliteBackend() to include ?_txlock=immediate in the file-based database DSN

Testing

  • All existing SQLite backend tests pass (100+ tests including comprehensive end-to-end scenarios)
  • Tests pass with the race detector enabled
  • Both file-based and in-memory backends verified with sample applications
  • No security issues detected (CodeQL scan clean)

References

This approach was validated in production use by the ConduitIO/conduit project.

Original prompt

This section details on the original issue you should resolve

<issue_title>adjust sqlite settings</issue_title>
<issue_description>Since the addition of WAL, sqlite can handle multiple connections however this can often spur database locked errors.
As part of the https://github.com/ConduitIO/conduit project we ran some tests and found that tx locking is more tolerable when transactions are not deferred, which is the default for sqlite thus setting busy_timeout=N[1] and _txlock=immediate[2] allows for concurrent access.

By allowing the tx lock to take place immediately, this allows for a form of serialization of writes in the span of the busy timeout, until when the error will return. In comparison a deferred tx will not trigger a lock until it reaches a statement which requires it, but by that time there may be a non-reconcilable tx in progress or data change and no retry is possible.

  1. https://www.sqlite.org/pragma.html#pragma_busy_timeout
  2. See https://pkg.go.dev/modernc.org/sqlite#Driver.Open, https://www.sqlite.org/lang_transaction.html</issue_description>

<agent_instructions>The busy timeout is already set, see if you can set the transaction lock to immediate mode like suggested. </agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Adjust SQLite settings for enhanced connection handling Add immediate transaction lock mode to SQLite backend Oct 24, 2025
Copilot AI requested a review from cschleiden October 24, 2025 16:43
@cschleiden
Copy link
Owner

!bench !large

@github-actions
Copy link

Benchmark Results

SQLite run

Command Mean [ms] Min [ms] Max [ms] Relative
sqlite-main 224.0 ± 20.8 192.0 246.6 1.00
sqlite-pr 511.3 ± 911.1 200.0 3256.7 2.28 ± 4.07

Large SQLite payload run (1MB)

Command Mean [s] Min [s] Max [s] Relative
sqlite-main 5.267 ± 0.286 4.835 5.716 1.00
sqlite-pr 5.287 ± 0.443 4.736 6.071 1.00 ± 0.10

Redis run

Command Mean [ms] Min [ms] Max [ms] Relative
redis-main 149.6 ± 24.1 112.5 207.9 1.02 ± 0.21
redis-pr 146.0 ± 17.7 119.9 188.8 1.00

Large Redis payload run (1MB)

Command Mean [s] Min [s] Max [s] Relative
redis-main 4.294 ± 0.464 3.550 5.232 1.07 ± 0.14
redis-pr 4.014 ± 0.308 3.491 4.415 1.00

@cschleiden
Copy link
Owner

!bench !large !verylarge !skipmysql !skipredis

@cschleiden cschleiden marked this pull request as ready for review October 24, 2025 17:10
@github-actions
Copy link

Benchmark Results

SQLite run

Command Mean [ms] Min [ms] Max [ms] Relative
sqlite-main 385.3 ± 436.5 202.5 1894.1 1.57 ± 1.79
sqlite-pr 245.1 ± 22.3 222.8 301.6 1.00

Large SQLite payload run (1MB)

Command Mean [s] Min [s] Max [s] Relative
sqlite-main 5.372 ± 0.407 4.730 5.925 1.00
sqlite-pr 5.636 ± 0.287 5.187 6.144 1.05 ± 0.10

Very Large SQLite payload run (5MB)

Command Mean [s] Min [s] Max [s] Relative
sqlite-main 4.880 ± 0.388 4.405 5.422 1.00
sqlite-pr 4.900 ± 0.285 4.561 5.409 1.00 ± 0.10

Copilot AI and others added 2 commits October 24, 2025 10:21
Co-authored-by: cschleiden <2201819+cschleiden@users.noreply.github.com>
Co-authored-by: cschleiden <2201819+cschleiden@users.noreply.github.com>
@cschleiden cschleiden force-pushed the copilot/adjust-sqlite-settings branch from 2148a51 to 92af8d8 Compare October 24, 2025 17:21
@cschleiden cschleiden merged commit c5acff6 into main Oct 24, 2025
1 check passed
@cschleiden cschleiden deleted the copilot/adjust-sqlite-settings branch October 24, 2025 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

adjust sqlite settings

2 participants