m4_comment([$Id: intro.so,v 10.33 2006/11/13 18:05:00 bostic Exp $]) m4_ref_title(m4_cam Applications, Concurrent Data Store introduction, @Concurrent Data Store, env/faq, cam/fail) m4_p([dnl It is often desirable to have concurrent read-write access to a database when there is no need for full recoverability or transaction semantics. For this class of applications, m4_db provides interfaces supporting deadlock-free, multiple-reader/single writer access to the database. This means that at any instant in time, there may be either multiple readers accessing data or a single writer modifying data. The application is entirely unaware of which is happening, and m4_db implements the necessary locking and blocking to ensure this behavior.]) m4_p([dnl To create m4_cam applications, you must first initialize an environment by calling m4_ref(dbenv_open). You must specify the m4_ref(DB_INIT_CDB) and m4_ref(DB_INIT_MPOOL) flags to that method. It is an error to specify any of the other m4_ref(dbenv_open) subsystem or recovery configuration flags, for example, m4_ref(DB_INIT_LOCK), m4_ref(DB_INIT_TXN), or m4_ref(DB_RECOVER). All databases must, of course, be created in this environment by using the m4_ref(dbh_create) function or m4_refcxx(Db) constructor, and specifying the environment as an argument.]) m4_p([dnl m4_db performs appropriate locking so that safe enforcement of the deadlock-free, multiple-reader/single-writer semantic is transparent to the application. However, a basic understanding of m4_cam locking behavior is helpful when writing m4_cam applications.]) m4_p([dnl m4_cam avoids deadlocks without the need for a deadlock detector by performing all locking on an entire database at once (or on an entire environment in the case of the m4_ref(DB_CDB_ALLDB) flag), and by ensuring that at any given time only one thread of control is allowed to simultaneously hold a read (shared) lock and attempt to acquire a write (exclusive) lock.]) m4_p([dnl All open m4_db cursors hold a read lock, which serves as a guarantee that the database will not change beneath them; likewise, all non-cursor m4_ref(dbh_get) operations temporarily acquire and release a read lock that is held during the actual traversal of the database. Because read locks will not conflict with each other, any number of cursors in any number of threads of control may be open simultaneously, and any number of m4_ref(dbh_get) operations may be concurrently in progress.]) m4_p([dnl To enforce the rule that only one thread of control at a time can attempt to upgrade a read lock to a write lock, however, m4_db must forbid multiple cursors from attempting to write concurrently. This is done using the m4_ref(DB_WRITECURSOR) flag to the m4_refT(dbh_cursor). This is the only difference between access method calls in m4_cam and in the other m4_db products. The m4_ref(DB_WRITECURSOR) flag causes the newly created cursor to be a "write" cursor; that is, a cursor capable of performing writes as well as reads. Only cursors thus created are permitted to perform write operations (either deletes or puts), and only one such cursor can exist at any given time.]) m4_p([dnl Any attempt to create a second write cursor or to perform a non-cursor write operation while a write cursor is open will block until that write cursor is closed. Read cursors may open and perform reads without blocking while a write cursor is extant. However, any attempts to actually perform a write, either using the write cursor or directly using the m4_ref(dbh_put) or m4_ref(dbh_del) methods, will block until all read cursors are closed. This is how the multiple-reader/single-writer semantic is enforced, and prevents reads from seeing an inconsistent database state that may be an intermediate stage of a write operation.]) m4_p([dnl By default, m4_cam does locking on a per-database basis. For this reason, using cursors to access multiple databases in different orders in different threads or processes, or leaving cursors open on one database while accessing another database, can cause an application to hang. If this behavior is a requirement for the application, m4_db should be configured to do locking on an environment-wide basis. See the m4_ref(DB_CDB_ALLDB) flag of the m4_ref(dbenv_set_flags) function for more information.]) m4_p([dnl With these behaviors, m4_db can guarantee deadlock-free concurrent database access, so that multiple threads of control are free to perform reads and writes without needing to handle synchronization themselves or having to run a deadlock detector. m4_db has no direct knowledge of which cursors belong to which threads, so some care must be taken to ensure that applications do not inadvertently block themselves, causing the application to hang and be unable to proceed.]) m4_p([dnl As a consequence of the m4_cam locking model, the following sequences of operations will cause a thread to block itself indefinitely:]) m4_nlistbegin m4_nlist([dnl Keeping a cursor open while issuing a m4_ref(dbh_put) or m4_ref(dbh_del) access method call.]) m4_nlist([dnl Attempting to open a write cursor while another cursor is already being held open by the same thread of control. Note that it is correct operation for one thread of control to attempt to open a write cursor or to perform a non-cursor write (m4_ref(dbh_put) or m4_ref(dbh_del)) while a write cursor is already active in another thread. It is only a problem if these things are done within a single thread of control -- in which case that thread will block and never be able to release the lock that is blocking it.]) m4_nlist([dnl Not testing m4_db error return codes (if any cursor operation returns an unexpected error, that cursor must still be closed).]) m4_nlistend m4_p([dnl If the application needs to open multiple cursors in a single thread to perform an operation, it can indicate to m4_db that the cursor locks should not block each other by creating a m4_cam m4_bold(group), using m4_ref(cdsgroup_begin). This creates a locker ID that is shared by all cursors opened in the group.]) m4_p([dnl m4_cam groups use a m4_ref(DbTxn) handle to indicate the shared locker ID to m4_db calls, and call m4_ref(txn_commit) to end the group. This is a convenient way to pass the locked ID to the calls where it is needed, but should not be confused with the real transactional semantics provided by m4_tam. In particular, m4_cam groups do not provide any abort or recovery facilities, and have no impact on durability of operations.]) m4_page_footer