Resources | developer.brewmp.com Resources | developer.brewmp.com

Developer

resources

Concurrency considerations

Database concurrency is a tricky problem, and the "correct" solution depends strongly on the use cases of a particular application. Because Gallery is a framework component used by many other applications, including background daemons that need to update the database, a complete solution requires a number of cooperating features. The following sections describe specific use cases and the corresponding features that enable that level of concurrency.

Reading During Background Synchronization

Example: Running periodic syncs while using applications.

Solution:

  • Gallery uses an explicit transaction held open for a fixed, relatively short period of time (currently 5 seconds). This means that the app running the sync holds a RESERVED lock on the database until the end of the commit batch, at which point it obtains an EXCLUSIVE lock and writes the changes to the disk. While it holds the RESERVED lock, other apps can continue to read from the database, but other apps cannot attempt to write to the database.
  • If the commit batch is too large, then it is possible that SQLite's memory cache may fill, at which point SQLite will need to obtain an EXCLUSIVE lock in order to write changes to disk. In our testing, the cache fills after ~2600 inserts into the Gallery database with a cache size of 2000 pages at 1KB each. This is undesirable, because the app will hold the EXCLUSIVE lock until the transaction officially commits, preventing other apps from reading the database.

Updating During Background Synchronization

Examples: Updating rights, incrementing play count, and deleting a file.

Solution:

  • While Gallery syncs, the transaction holds a RESERVED lock preventing other connections from updating the database. To work around this, Gallery emits an "on writable" global notify signal between batches. Other apps can subscribe to this event and perform any updates in the event callback function before the sync resumes.
  • Because database updates are now effectively asynchronous (they may or may not be able to occur at the desired moment), apps must manage a queue of all desired updates and wait for the appropriate signal to write changes to the database. Of course, if no sync is running, the updates can immediately be written to the database.

Starting Synchronization During Background Synchronization

Example: Taking a picture and adding a file to database.

Solution:

  • The solution to this problem is the same as the updating during background sync use case.
  • When the sync process sends out the "on writable" signal, another sync may take the opportunity to acquire a RESERVED lock through a transaction, thus preventing the original sync from continuing. In this case, the original sync must now wait for a signal from the second sync to continue. In this way, syncs are essentially scheduled round-robin style, swapping database control back-and-forth.
  • Single database updates (as in the updating during background sync use case) can also be interspersed between multiple syncs in a similar way.

Reading During Background Updates

Example: Updating rights.

Solution:

  • Gallery's caching mechanism prevents queries from holding SHARED locks after returning control to BREW, thus allowing other apps to update the database by obtaining an EXCLUSIVE lock (see Caching).
  • Long, explicit transactions should be avoided to minimize the length of time that the updating app holds an EXCLUSIVE lock on the database, preventing other apps from reading the database, as described in the reading during background sync use case. However, because Gallery's interface does not support explicit transactions, this situation cannot occur (syncing transactions are handled as described in the updating during background sync use case).

Reading and Updating From the Same Connection

Examples: Incrementing play count and deleting a file.

Solution:

  • SQLite natively supports updates to database with concurrent reads using a single database instance.
  • The exception to this rule is when using explicit transactions, in which case, all queries must first be closed. This situation is similar to how updates are handled with multiple database instances, so this case is also supported through Gallery's caching mechanism (see the reading during background updates use case).