I think the documentation's good. I think you're missing the whole point of a deferred transaction: that it doesn't start a "transaction" until it needs to. You can run "begin deferred transaction" then walk away for 3 months without upsetting anything. If you need the precise timing then why not just use "begin immediate"?
http://www.sqlite.org/lang_transaction.html A deferred transaction doesn't do anything until it first accesses the file. Once it does then it will lock out any writers. "Deferred means that no locks are acquired on the database until the database is first accessed. Thus with a deferred transaction, the BEGIN statement itself does nothing to the filesystem. Locks are not acquired until the first read or write operation. The first read operation against a database creates a SHARED lock and the first write operation creates a RESERVED lock." -----Original Message----- From: sqlite-users [mailto:sqlite-users-boun...@mailinglists.sqlite.org] On Behalf Of Gwendal Roué Sent: Tuesday, July 18, 2017 9:10 AM To: SQLite mailing list Subject: [sqlite] Possible inaccuracy in "Isolation In SQLite" paper Hello all, The following sentence in https://www.sqlite.org/isolation.html does not exactly describe the behavior of SQLite (since many versions): > In WAL mode, SQLite exhibits "snapshot isolation". When a read transaction > starts, that reader continues to see an unchanging "snapshot" of the database > file as it existed at the moment in time when the read transaction started. > Any write transactions that commit while the read transaction is active are > still invisible to the read transaction, because the reader is seeing a > snapshot of database file from a prior moment in time. I'll exhibit the inaccuracy below. Meanwhile, "snapshot isolation" is a tremendous feature of SQLite. Not only does it grant any read-only connection that opens a deferred transaction an immutable and consistent view of the database. But when you can guarantee that there is a single writer connection, snapshot isolation allows *precise scheduling*, such as blocking the writer connection until a reader has established snapshot isolation. With such precision, one can exactly control what's available to a reader, while not blocking the writer longer than necessary. And this is where the documentation paragraph starts becoming inaccurate. For the simplicity of the argument, I'll execute statements sequentially from two connections W and R. To reproduce, just open two shells, and execute statements in the following order: $ sqlite3 /tmp/snapshotisolation.sqlite SQLite version 3.16.0 2016-11-04 19:09:39 W> PRAGMA journal_mode=wal; W> CREATE TABLE t(a); R> BEGIN DEFERRED TRANSACTION; W> INSERT INTO t DEFAULT VALUES; R> SELECT COUNT(*) FROM t; 1 This is unexpected. After connection R has started a deferred transaction, it should continue to see an "unchanging snapshot of the database file as it existed at the moment in time when the read transaction started". Obviously, this is not the case, since the insertion performed by W is visible from R even though it has been performed *after* R has started its deferred transaction. The "Any write transactions that commit while the read transaction is active are still invisible to the read transaction" is also flat wrong here. If we continue, things behave as expected: W> INSERT INTO t DEFAULT VALUES; R> SELECT COUNT(*) FROM t; 1 R does not see the new insertion, which means that it indeed lives in an unchanging snapshot. It just happens that the snapshot was not established when the transaction has started, as documented, but *later*. But when? After a few experiments, it looks like the snapshot is established on the first select: W> PRAGMA journal_mode=wal; W> CREATE TABLE t(a); R> BEGIN DEFERRED TRANSACTION; R> SELECT * FROM sqlite_master LIMIT 1; -- good enough to start the snapshot W> INSERT INTO t DEFAULT VALUES; R> SELECT COUNT(*) FROM t; 0 -- as expected If now we stop entering commands by hand in the CLI, and start working with threads, the *precise scheduling* I mention at the beginning of the mail requires to block the writer connection W until the reader connection R has started a deferred transaction, and sqlite3_step() has been any executed once from any select statement. Only then can the writer connection be released, with a absolute control of the content of the reader's snapshot. I'm not sure the behavior I've just described can be called a bug. The snapshot is unchanging indeed. When there are several processes connected to the database, a reader can't know whether a change has been performed before its deferred transaction has started, or between the start of the deferred transaction and its first select statement, and therefore should not care at all. With this interpretation, there is nothing wrong in the current behavior of SQLite. However, when an application developper is sure that the database has a single writer connection, the question of the content of the snapshot suddenly becomes relevant. And the documentation becomes inaccurate. What do SQLite concurrency masters of this mailing list think? Besides that threads are evil, I mean ;-) Shouldn't the documentation be updated? Cheers to all, Gwendal PS: For the record, I've been talking about "precise scheduling" and "absolute control of the content of the snapshot" because they are the necessary conditions for some database observation features such as reloading fresh values from a request as soon as a transaction has modified its content. See https://github.com/RxSwiftCommunity/RxGRDB for some high-level implementations of such feature. _______________________________________________ sqlite-users mailing list sqlite-users@mailinglists.sqlite.org http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users _______________________________________________ sqlite-users mailing list sqlite-users@mailinglists.sqlite.org http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users