Conversation
|
If I understand it correctly, Rails already enables WAL mode by default via #49349 Then setting
|
491304e to
f5dc85e
Compare
SQLite3 supports three different transaction modes - DEFERRED, IMMEDIATE and EXCLUSIVE, see https://www.sqlite.org/lang_transaction.html. Rails currently uses the default which is DEFERRED. While this is best for read concurrency, it can lead to `SQLite3::BusyException`s when you read first and then write in the transaction. If you only create transactions when you are planning to write, then IMMEDIATE may be a better choice, as it will block all other write transactions and prevent stale reads. Add a `transaction_mode` option to the configuration to allow the transaction mode to be changed: ```yaml development: adapter: sqlite3 database: storage/development.sqlite3 transaction_mode: immediate ```
f5dc85e to
086f80a
Compare
|
Thanks @yahonda! I've updated that now. I figured that anyone changing the transaction mode should be aware of how they work, but you are right that it's a good idea to point out how the Rails defaults effect them. |
|
FYI there is a open pull request #49691 to make |
|
Some additional context on transaction mode, why it's important we're able to change this. There is a documented deadlock condition that will lead to
The two solutions are to not select from the database during transaction (difficult to adhere to) or to use an exclusive (or immediate) lock to throw early SQL_BUSY errors which the BUSY_HANDLER can take care of. There is a big stack overflow discussion on this issue related to rails that started 15! years ago. We've seen these intermittent busy exceptions errors under load which were a result of having I'd suggest we consider changing the Rails default to use For this PR, I think we should first determine where we want SQLite specific configuration to be specified in the database config yaml. There is some conversation in #49691 on whether they should live in a |
|
I'm so surprised because I have submitted a very similar pull request to sqlite3-ruby as PR426 coincidentally. With my pull request, we can specify the default transaction mode in config/database.yml. Regards, |
|
Excellent @masamitsu-murase I agree, with the change upstream this PR is no longer necessary. ty |
|
Hi @masamitsu-murase - that's a much better solution, thank you! I'll close this now since we don't need it any more 👍 |
|
For reference, let me share the configuration to set the default transaction mode for SQLite3 in # config/database.yml
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
default_transaction_mode: immediate |
Motivation / Background
SQLite3 supports three different transaction modes - DEFERRED, IMMEDIATE and EXCLUSIVE, see
https://www.sqlite.org/lang_transaction.html.
Rails currently uses the default which is DEFERRED. While this is best for read concurrency, it can lead to
SQLite3::BusyExceptions when you read first and then write in the transaction.If you only create transactions when you are planning to write, then IMMEDIATE may be a better choice, as it will block all other write transactions and prevent stale reads.
Detail
Add a
transaction_modeoption to the SQLite database configuration to allow the transaction mode to be changed:Additional information
SQLite3 transaction docs: https://www.sqlite.org/lang_transaction.html
SQLIte3 gem transaction method: https://github.com/sparklemotion/sqlite3-ruby/blob/e60035e8f01c13ee986ddfee501a93bd6d6394a2/lib/sqlite3/database.rb#L637-L653
Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]