Central Repository Transactions

This topic describes the concepts of Central Repository transactions.

Overview of transactions

In a typical use case, a repository is accessed by several different applications for different purposes such as reading, updating, caching and backing up. These applications are regarded as having a client-server relationship with the repository. When multiple applications can access a repository there is a danger of concurrent write operations corrupting the data. To prevent this, applications access repositories within sessions and perform operations on a repository within subdivisions of a session called transactions. The concept of a transaction is borrowed from database programming and is designed to ensure that only one application can modify a repository at any one time. Operations performed within a transaction are virtual, operating on a copy of the repository, until the transaction is successfully committed and the actual repository is modified. Transactions conform to a model which prevents them from being committed concurrently, so maintaining data integity.

Only the following operations are permitted within a transaction:

  • Find()

  • Get()

  • Set()

  • Create()

  • Delete()

  • Move()

Transactions are either synchronous or asynchronous. As the programmer you are responsible for avoiding errors such as beginning a transaction within a transaction which will cause a leave. You do this by conforming to a transaction model.

The recommended transaction model is the optimistic non-serialised transaction model. It works on the principle that any number of clients may start a transaction at the same time, but as soon as one transaction is committed, any other transactions fail and must be started again.

A session is in various states:

  • Not in transaction

  • Active

  • Failed

An asynchronous transaction involves two other states:

  • Pending start

  • Pending commit

Synchronous transactions

A synchronous transaction has the following structure.

  • A session is initially in the state Not in transaction.

  • Call the synchronous form of StartTransaction() with the single parameter EConcurrentReadWriteTransaction.

  • The session changes state to Active.

  • Manipulate the repository with the functions Get() Set() Find() etc covered above.

  • The session continues in the Active state. The changes to the repository are cached but not actually applied during the Active state. You then either revoke or persist the changes.

  • To revoke the changes, call CancelTransaction()

  • The session returns to Not in transaction.

  • To persist the changes, call the synchronous form of CommitTransaction() with the parameter aKeyInfo of type TUint32 This function returns success or an error message and its parameter holds information about the transaction. On success, the parameter holds the number of settings which were changed. On failure, the parameter holds the settings which caused failure in the form of a key or partial key.

  • On success the session returns to Not in transaction. On failure it enters the Failed state. When a transaction fails, the operations already performed are discarded and no subsequent operations can reverse the failure.

  • If the reason for failure was the success of another transaction, the error message is KErrLocked and the session is Not in transaction. You try the same sequence of function calls until you get a successful commit.

    If there was some other reason for failure you must close the transaction yourself. To do this, either call CancelTransaction() or else call CommitTransaction() a second time.

    There is sometimes reason to fail the transaction deliberately by calling FailTransaction() For instance you might want to generate failure after an unsuccessful Get() operation and then cancel the transaction.

Figure 1. Synchronous transaction state diagram

Asynchronous transactions

Synchronous transactions have the disadvantage that a busy server may block the client thread before any action takes place. To avoid this problem you can use the slightly more complicated asynchronous transactions. An asynchronous transaction has the same structure as a synchronous one except that there are two new states Pending Start and Pending Commit where a transaction waits until the server is ready to communicate with it. The transition from Not in transaction to Active now proceeds like this.

  • A session is initially in the state Not in transaction.

  • Call the asynchronous form of StartTransaction() with the parameters EConcurrentReadWriteTransaction and aStatus of type TRequestStatus This function returns void: success or the reasons for failure are determined from the state of aStatus after execution.

  • The session changes state to Pending Start. Activity on the server will eventually promote the session to Active.

  • Manipulate the repository with the functions Get(), Set(), Find() etc covered above.

  • The session continues in the Active state. The changes to the repository are cached but not actually applied during the Active state. You then either revoke or persist the changes.

  • To revoke the changes, call CancelTransaction() This call has different effects depending on whether it was called in the Pending Start state or the Active state. If it was called in the Pending Start state it sets aStatus to KErrCancel

  • The session returns to Not in transaction.

  • To persist the changes, call the asynchronous form of CommitTransaction() with the parameters aKeyInfo of type TUint32 and aStatus of type TRequestStatus This function returns void. Its first parameter holds information about the transaction.

  • On success, the aStatus parameter holds the number of settings which were changed. On failure, the parameter holds the settings which caused failure in the form of a key or partial key. The aStatus parameter is set to success or failure.

  • If the reason for failure was the success of another transaction, aStatus is set to KErrLocked and the session is Not in transaction. You try the same sequence of function calls until you get a successful commit.

    If there was some other reason for failure you must close the transaction yourself. To do this, either call CancelTransaction() or else call CommitTransaction() a second time.

    There is sometimes reason to fail the transaction deliberately by calling FailTransaction() For instance you might want to generate failure after an unsuccessful Get() operation and then cancel the transaction.

Figure 2. Asynchronous Transaction State Diagram

Cleanup

There is a danger that a transaction might remain open for ever if it is opened by code which subsequently leaves. To avoid this possibility, the CCentralRepository class has two functions which cause the cleanup stack to end the transaction in the event of a leave. CleanupCancelTransactionPushL(), also named CleanupRollbackTransactionPushL(), causes a leave to be followed by a call to CancelTransaction(). CleanupFailTransactionPushL() causes a leave to be followed by a call to FailTransaction().