2.7. Transactions

A Transaction is used to interact with the data in a database. Whenever data is read or written to the database it is done by using a transaction.

Transactions offer some protection from application and system failures. A transaction may be used to store multiple data records or to conditionally modify certain data records. A transaction represents an atomic and durable set of data access and data mutation operations.

All transactions are created through a connection, which is the transaction’s connection.

A transaction has a scope that determines the object stores with which the transaction may interact. A transaction’s scope remains fixed for the lifetime of that transaction.

A transaction has a mode that determines which types of interactions can be performed upon that transaction. The mode is set when the transaction is created and remains fixed for the life of the transaction. A transaction‘s mode is one of the following:

["readonly"](#dom-idbtransactionmode-readonly)

The transaction is only allowed to read data. No modifications can be done by this type of transaction. This has the advantage that several read-only transactions can run at the same time even if their scopes are overlapping, i.e. if they are using the same object stores. This type of transaction can be created any time once a database has been opened.

["readwrite"](#dom-idbtransactionmode-readwrite)

The transaction is allowed to read, modify and delete data from existing object stores. However object stores and indexes can’t be added or removed. Multiple ["readwrite"](#dom-idbtransactionmode-readwrite) transactions can’t run at the same time if their scopes are overlapping since that would mean that they can modify each other’s data in the middle of the transaction. This type of transaction can be created any time once a database has been opened.

["versionchange"](#dom-idbtransactionmode-versionchange)

The transaction is allowed to read, modify and delete data from existing object stores, and can also create and remove object stores and indexes. It is the only type of transaction that can do so. This type of transaction can’t be manually created, but instead is created automatically when an [upgradeneeded](#request-upgradeneeded) event is fired.

A transaction has an active flag, which determines if new requests can be made against the transaction. A transaction is said to be active if its active flag is set.

A transaction optionally has a cleanup event loop which is an event loop.

A transaction has a request list of requests which have been made against the transaction.

A transaction has a error which is set if the transaction is aborted.

A transaction‘s get the parent algorithm returns the transaction’s connection.

A read-only transaction is a transaction with mode ["readonly"](#dom-idbtransactionmode-readonly).

A read/write transaction is a transaction with mode ["readwrite"](#dom-idbtransactionmode-readwrite).

2.7.1. Transaction Lifetime

Transactions are expected to be short lived. This is encouraged by the automatic committing functionality described below.

Authors can still cause transactions to run for a long time; however, this usage pattern is not advised as it can lead to a poor user experience.

The lifetime of a transaction is as follows:

  1. A transaction is created with a scope and a mode. When a transaction is created its active flag is initially set.

  2. The implementation must allow requests to be placed against the transaction whenever the active flag is set. This is the case even if the transaction has not yet been started. Until the transaction is started the implementation must not execute these requests; however, the implementation must keep track of the requests and their order. Requests may be placed against a transaction only while that transaction is active. If an attempt is made to place a request against a transaction when that transaction is not active, the implementation must reject the attempt by throwing a “[TransactionInactiveError](https://www.w3.org/TR/WebIDL-1/#transactioninactiveerror)[DOMException](https://www.w3.org/TR/WebIDL-1/#idl-DOMException).

  3. Once an implementation is able to enforce the constraints defined for the transaction scope and mode, defined below, the implementation must queue a task to start the transaction asynchronously.

  4. Once the transaction has been started the implementation can start executing the requests placed against the transaction. Unless otherwise defined, requests must be executed in the order in which they were made against the transaction. Likewise, their results must be returned in the order the requests were placed against a specific transaction. There is no guarantee about the order that results from requests in different transactions are returned. Similarly, the transaction modes ensure that two requests placed against different transactions can execute in any order without affecting what resulting data is stored in the database.

  5. A transaction can be aborted at any time before it is finished, even if the transaction isn’t currently active or hasn’t yet started. When a transaction is aborted the implementation must undo (roll back) any changes that were made to the database during that transaction. This includes both changes to the contents of object stores as well as additions and removals of object stores and indexes.

  6. A transaction can fail for reasons not tied to a particular request. For example due to IO errors when committing the transaction, or due to running into a quota limit where the implementation can’t tie exceeding the quota to a partcular request. In this case the implementation must run the steps to abort a transaction using the transaction as transaction and the appropriate error type as error. For example if quota was exceeded then a “[QuotaExceededError](https://www.w3.org/TR/WebIDL-1/#quotaexceedederror)[DOMException](https://www.w3.org/TR/WebIDL-1/#idl-DOMException) should be used as error, and if an IO error happened, an “[UnknownError](https://www.w3.org/TR/WebIDL-1/#unknownerror)[DOMException](https://www.w3.org/TR/WebIDL-1/#idl-DOMException) should be used as error.

  7. When a transaction has been started and it can no longer become active, the implementation must attempt to commit it, as long as the transaction has not been aborted. This usually happens after all requests placed against the transaction have been executed and their returned results handled, and no new requests have been placed against the transaction. When a transaction is committed, the implementation must atomically write any changes to the database made by requests placed against the transaction. That is, either all of the changes must be written, or if an error occurs, such as a disk write error, the implementation must not write any of the changes to the database. If such an error occurs, the implementation must abort the transaction by following the steps to abort a transaction, otherwise it must commit the transaction by following the steps to commit a transaction.

  8. When a transaction is committed or aborted, it is said to be finished. If a transaction can’t be finished, for example due to the implementation crashing or the user taking some explicit action to cancel it, the implementation must abort the transaction.

The following constraints define when a transaction can be started:

  • Any number of read-only transactions are allowed to run concurrently, even if the transaction’s scope overlap and include the same object stores. As long as a read-only transaction is running, the data that the implementation returns through requests created with that transaction must remain constant. That is, two requests to read the same piece of data must yield the same result both for the case when data is found and the result is that data, and for the case when data is not found and a lack of data is indicated.

    There are a number of ways that an implementation can ensure this. The implementation could prevent any read/write transaction, whose scope overlaps the scope of the read-only transaction, from starting until the read-only transaction finishes. Or the implementation could allow the read-only transaction to see a snapshot of the contents of the object stores which is taken when the read-only transaction started.

  • Similarly, implementations must ensure that a read/write transaction is only affected by changes to object stores that are made using the transaction itself. For example, the implementation must ensure that another transaction does not modify the contents of object stores in the read/write transaction’s scope. The implementation must also ensure that if the read/write transaction completes successfully, the changes written to object stores using the transaction can be committed to the database without merge conflicts. An implementation must not abort a transaction due to merge conflicts.

  • If multiple read/write transactions are attempting to access the same object store (i.e. if they have overlapping scope), the transaction that was created first must be the transaction which gets access to the object store first. Due to the requirements in the previous paragraph, this also means that it is the only transaction which has access to the object store until the transaction is finished.

  • Any transaction created after a read/write transaction must see the changes written by the read/write transaction. So if a read/write transaction, A, is created, and later another transaction B, is created, and the two transactions have overlapping scopes, then B must see any changes made to any object stores that are part of that overlapping scope. Due to the requirements in the previous paragraph, this also means that the B transaction does not have access to any object stores in that overlapping scope until the A transaction is finished.

    Generally speaking, the above requirements mean that any transaction which has an overlapping scope with a read/write transaction and which was created after that read/write transaction, can’t run in parallel with that read/write transaction.

  • User agents must ensure a reasonable level of fairness across transactions to prevent starvation. For example, if multiple read-only transactions are started one after another the implementation must not indefinitely prevent a pending read/write transaction from starting.

To cleanup Indexed Database transactions, run these steps for each transaction with cleanup event loop matching the current event loop.

  1. Unset the transaction‘s active flag.

  2. Clear the transaction‘s cleanup event loop.

This behavior is invoked by [HTML52]. It ensures that transactions created by a script call to [transaction()](#dom-idbdatabase-transaction) are deactivated once the task that invoked the script has completed. The steps are run at most once for each transaction.

2.7.2. Upgrade Transactions

An upgrade transaction is a transaction with mode ["versionchange"](#dom-idbtransactionmode-versionchange).

An upgrade transaction is automatically created when running the steps to run an upgrade transaction after a connection is opened to a database, if a version greater than the current version is specified. This transaction will be active inside the [upgradeneeded](#request-upgradeneeded) event handler.

An upgrade transaction enables the creation, renaming, and deletion of object stores and indexes in a database.

An upgrade transaction is exclusive. The steps to open a database ensure that only one connection to the database is open when an upgrade transaction is running. The [upgradeneeded](#request-upgradeneeded) event isn’t fired, and thus the upgrade transaction isn’t started, until all other connections to the same database are closed. This ensures that all previous transactions are finished. As long as an upgrade transaction is running, attempts to open more connections to the same database are delayed, and any attempts to use the same connection to start additional transactions by calling [transaction()](#dom-idbdatabase-transaction) will throw an exception. Thus upgrade transactions not only ensure that no other transactions are running concurrently, but also ensure that no new transactions are queued against the same database as long as the transaction is running.

This ensures that once an upgrade transaction is complete, the set of object stores and indexes in a database remain constant for the lifetime of all subsequent connections and transactions.