/ Check-in [41e742bd]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge documentation changes from branch "begin-concurrent" into this branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | begin-concurrent-wal2
Files: files | file ages | folders
SHA3-256: 41e742bd0f94894b096b30474fb548f58951756d1ea47d0afb717a54802888f4
User & Date: dan 2019-01-11 15:22:27
Wiki:begin-concurrent-wal2
Context
2019-01-11
15:26
Merge documentation changes from branch begin-concurrent-wal2 into this branch. check-in: cf8a0c71 user: dan tags: begin-concurrent-pnu-wal2
15:22
Merge documentation changes from branch "begin-concurrent" into this branch. Leaf check-in: 41e742bd user: dan tags: begin-concurrent-wal2
15:06
Add new documentation file begin_concurrent.md. check-in: fdbf97e6 user: dan tags: begin-concurrent
14:59
Merge latest wal2 changes (documentation only) into this branch. check-in: 820ba1cc user: dan tags: begin-concurrent-wal2
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Added doc/begin_concurrent.md.

            1  +
            2  +Begin Concurrent
            3  +================
            4  +
            5  +## Overview
            6  +
            7  +Usually, SQLite allows at most one writer to proceed concurrently. The
            8  +BEGIN CONCURRENT enhancement allows multiple writers to process write
            9  +transactions simultanously if the database is in "wal" or "wal2" mode,
           10  +although the system still serializes COMMIT commands.
           11  +
           12  +When a write-transaction is opened with "BEGIN CONCURRENT", actually 
           13  +locking the database is deferred until a COMMIT is executed. This means
           14  +that any number of transactions started with BEGIN CONCURRENT may proceed
           15  +concurrently. The system uses optimistic page-level-locking to prevent
           16  +conflicting concurrent transactions from being committed.
           17  +
           18  +When a BEGIN CONCURRENT transaction is committed, the system checks whether 
           19  +or not any of the database pages that the transaction has read have been
           20  +modified since the BEGIN CONCURRENT was opened. In other words - it asks 
           21  +if the transaction being committed operates on a different set of data than
           22  +all other concurrently executing transactions. If the answer is "yes, this
           23  +transaction did not read or modify any data modified by any concurrent
           24  +transaction", then the transaction is committed as normal. Otherwise, if the
           25  +transaction does conflict, it cannot be committed and an SQLITE_BUSY_SNAPSHOT
           26  +error is returned. At this point, all the client can do is ROLLBACK the
           27  +transaction.
           28  +
           29  +If SQLITE_BUSY_SNAPSHOT is returned, messages are output via the sqlite3_log
           30  +mechanism indicating the page and table or index on which the conflict
           31  +occurred. This can be useful when optimizing concurrency.
           32  +
           33  +## Application Programming Notes
           34  +
           35  +In order to serialize COMMIT processing, SQLite takes a lock on the database
           36  +as part of each COMMIT command and releases it before returning. At most one
           37  +writer may hold this lock at any one time. If a writer cannot obtain the lock,
           38  +it uses SQLite's busy-handler to pause and retry for a while:
           39  +
           40  +  <a href=https://www.sqlite.org/c3ref/busy_handler.html>
           41  +      https://www.sqlite.org/c3ref/busy_handler.html
           42  +  </a>
           43  +
           44  +If there is significant contention for the writer lock, this mechanism can be
           45  +inefficient. In this case it is better for the application to use a mutex or
           46  +some other mechanism that supports blocking to ensure that at most one writer
           47  +is attempting to COMMIT a BEGIN CONCURRENT transaction at a time. This is
           48  +usually easier if all writers are part of the same operating system process.
           49  +
           50  +If all database clients (readers and writers) are located in the same OS
           51  +process, and if that OS is a Unix variant, then it can be more efficient to
           52  +the built-in VFS "unix-excl" instead of the default "unix". This is because it
           53  +uses more efficient locking primitives.
           54  +
           55  +The key to maximizing concurrency using BEGIN CONCURRENT is to ensure that
           56  +there are a large number of non-conflicting transactions. In SQLite, each
           57  +table and each index is stored as a separate b-tree, each of which is
           58  +distributed over a discrete set of database pages. This means that:
           59  +
           60  +  * Two transactions that write to different sets of tables never 
           61  +    conflict, and that
           62  +
           63  +  * Two transactions that write to the same tables or indexes only 
           64  +    conflict if the values of the keys (either primary keys or indexed 
           65  +    rows) are fairly close together. For example, given a large 
           66  +    table with the schema:
           67  +
           68  +      <pre>     CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);</pre>
           69  +
           70  +    writing two rows with adjacent values for "a" probably will cause a
           71  +    conflict (as the two keys are stored on the same page), but writing two
           72  +    rows with vastly different values for "a" will not (as the keys will likly
           73  +    be stored on different pages).
           74  +
           75  +Note that, in SQLite, if values are not explicitly supplied for an INTEGER
           76  +PRIMARY KEY, as for example in:
           77  +
           78  +>
           79  +     INSERT INTO t1(b) VALUES(&lt;blob-value>);
           80  +
           81  +then monotonically increasing values are assigned automatically. This is
           82  +terrible for concurrency, as it all but ensures that all new rows are 
           83  +added to the same database page. In such situations, it is better to
           84  +explicitly assign random values to INTEGER PRIMARY KEY fields.
           85  +
           86  +This problem also comes up for non-WITHOUT ROWID tables that do not have an
           87  +explicit INTEGER PRIMARY KEY column. In these cases each table has an implicit
           88  +INTEGER PRIMARY KEY column that is assigned increasing values, leading to the
           89  +same problem as omitting to assign a value to an explicit INTEGER PRIMARY KEY
           90  +column.
           91  +
           92  +For both explicit and implicit INTEGER PRIMARY KEYs, it is possible to have
           93  +SQLite assign values at random (instead of the monotonically increasing
           94  +values) by writing a row with a rowid equal to the largest possible signed
           95  +64-bit integer to the table. For example:
           96  +
           97  +     INSERT INTO t1(a) VALUES(9223372036854775807);
           98  +
           99  +Applications should take care not to malfunction due to the presence of such
          100  +rows.
          101  +
          102  +The nature of some types of indexes, for example indexes on timestamp fields,
          103  +can also cause problems (as concurrent transactions may assign similar
          104  +timestamps that will be stored on the same db page to new records). In these
          105  +cases the database schema may need to be rethought to increase the concurrency
          106  +provided by page-level-locking.
          107  +

Changes to src/wal.h.

   155    155   
   156    156   /* Return the sqlite3_file object for the WAL file */
   157    157   sqlite3_file *sqlite3WalFile(Wal *pWal);
   158    158   
   159    159   /* Return the journal mode (WAL or WAL2) used by this Wal object. */
   160    160   int sqlite3WalJournalMode(Wal *pWal);
   161    161   
          162  +/* sqlite3_wal_info() data */
          163  +int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
          164  +
   162    165   /* sqlite3_wal_info() data */
   163    166   int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
   164    167   
   165    168   #endif /* ifndef SQLITE_OMIT_WAL */
   166    169   #endif /* SQLITE_WAL_H */