Read and write locks
ODABA differs between resource, read and write locks. In order to permit other applications reading an instance or collection, read lock should be used. Sometimes, other applications or threads may read, but not "use" the resource, in which case resource lock might be used. Both, read and resource lock are explicit request which have to be activated from within the application (or business rules).
In order to avoid asynchronous updates of an instance, write lock might be activated. Write locks are controlled by the system and need not to be activated by the application.
Specific locking feature provided by the system is key lock, which avoids long term locks for collections when being used in long transactions.
Resource locks are used in order to protect a resource from asyncronous use without blocking read requests. Resource lock provides a weak type of locking, since it still allows other threads or applications reading or updating the resource. Usually, read locks are called for instances, but one may also lock collection resources.
In order to lock an instance resource, the instance has to be selected in a property handle. Then, Property::lockInstance() can be called in order to lock the resource. Other threads or applications trying to lock the same resource will fail after 10 seconds time out, as long as the resource has not yet been unlocked again.
Resources are unlocked automatically, when being unselected in the property handle or when the property handle is going to be closed. Otherwise, the resource should be unlocked explicitly by calling Property::unlockInstance().
... fragment ( Property &person ) {
// pass true in order to allow read
person.get("Miller|Paul").lockInstance(true);
// ... do something
// do not forget to unlock or unselect the instance at the end
person.unlockInstance();
}
Read locks work similar to resource locks, but permit other threads or applications accessing the locked instance or collection. Similar to resource locks, read locks usually apply on instances rather than on collections. When locking a collection, no other user may search in the collection, which might block the system in a uncomfortable way.
... fragment ( Property &person ) {
// pass true in order to allow read
person.get("Miller|Paul").lockInstance();
// ... do something
// do not forget to unlock or unselect the instance at the end
person.unlockInstance();
}
Write locks are called in order to manage concurrency problems when updating instances or collections (database entries) in the database. In the worst case, database entries are locked, when being stored in a transaction. Since each modification goes through a transaction, database entries are always passed to a transaction after being modified, which automatically locks the instance or collection.
This is usually an internal transaction, which automatically starts, when calling a updating property handle function (e.g. Property::insert()). Practically, any property handle function may start an internal transaction when changing the instance selection for an updated instance. When updating values in an instance, changes are stored, when the instance is saved explicitly calling Property::save() or when the instance selection changes, e.g. when selecting another instance calling Property::get().
Since changing an instance may cause changes in a number of database entries, changes are stored to an internal transaction before being written to the database. In case of conflicts, the internal transaction aborts and changes are not stored to the database.
As long as database entries are part of a transaction, database entries are locked and cannot be updated by other processes or threads. After all database entries in a transaction have been written to the database, database entries will be unlocked.
This may cause a large number of instances locked when the application starts a transaction in order to perform a series of changes in one transaction. Hence, it is suggested to avoid long transactions in order to reduce lock conflicts.
When opening a property handle in write mode (Write), instances are automatically locked when being selected. Thus, using write property handles forces pessimistic locking. Pessimistic locking prevents other property handles from changing valued for the locked instance, which is saver than optimistic. When opening the property handle or changing the access mode to Update, values for the same instance might be changed in different property handles (or applications or threads). Detecting an update conflict usually causes a database error (SDB-Error 67) and refuses storing the latest update. In order to overwrite the updates made asynchroneously, one may call Property::save(true).
When changing indexes (Property::insert() or Property::erase()), or when updating key value attributes), the index will be locked (write lock) before being updated and stays locked until the modification is written to the database, i.e. until the end of the currently running top transaction. For global collections (extents) this may cause critical locking situations in case of application transactions, since no other process or thread is able tocreate or delete instances for such an extent as long as the transaction is running.
Hence, when an application transaction has been started, changes on global indexes are stored in difference lists instead of updating the index immediately. Indexes are updated in this case, when committing the top application transaction. In order to avoid key conflicts, keys created within an application transaction are stored to a key lock list. Key locks prevent other processes or threads from creating instances with the same key, which would cause the transaction to abort when being stored to the database.
The key lock feature is an implicit feature, which is activated automatically, when changes are made within a transaction. In case of long transactions, it may cause some confusion, since displaying instances in a collection one may find, that the key abc is not used, but one cannot create a new instance with key abc, since this is a key locked in a transaction of another application.