📄 put.so
字号:
m4_comment([$Id: put.so,v 1.20 2006/04/24 17:26:33 bostic Exp $])m4_ref_title(m4_tam Applications, Recoverability and deadlock handling,, transapp/data_open, transapp/atomicity)m4_p([dnlThe first reason listed for using transactions was recoverability. Anylogical change to a database may require multiple changes to underlyingdata structures. For example, modifying a record in a Btree may requireleaf and internal pages to split, so a single m4_ref(dbh_put) methodcall can potentially require that multiple physical database pages bewritten. If only some of those pages are written and then the systemor application fails, the database is left inconsistent and cannot beused until it has been recovered; that is, until the partially completedchanges have been undone.])m4_p([dnlm4_italic(Write-ahead-logging) is the term that describes the underlyingimplementation that m4_db uses to ensure recoverability. What it meansis that before any change is made to a database, information about thechange is written to a database log. During recovery, the log is read,and databases are checked to ensure that changes described in the logfor committed transactions appear in the database. Changes that appearin the database but are related to aborted or unfinished transactionsin the log are undone from the database.])m4_p([dnlFor recoverability after application or system failure, operations thatmodify the database must be protected by transactions. Morespecifically, operations are not recoverable unless a transaction isbegun and each operation is associated with the transaction via them4_db interfaces, and then the transaction successfully committed. Thisis true even if logging is turned on in the database environment.])m4_p([dnlHere is an example function that updates a record in a database in atransactionally protected manner. The function takes a key and dataitems as arguments and then attempts to store them into the database.])include(ref/transapp/put.cs)m4_p([dnlm4_db also uses transactions to recover from deadlock. Databaseoperations (that is, any call to a function underlying the handlesreturned by m4_ref(dbh_open) and m4_ref(dbh_cursor)) are usuallyperformed on behalf of a unique locker. Transactions can be used toperform multiple calls on behalf of the same locker within a singlethread of control. For example, consider the case in which anapplication uses a cursor scan to locate a record and then theapplication accesses another other item in the database, based on thekey returned by the cursor, without first closing the cursor. If theseoperations are done using default locker IDs, they may conflict. If thelocks are obtained on behalf of a transaction, using the transaction'slocker ID instead of the database handle's locker ID, the operationswill not conflict.])m4_p([dnlThere is a new error return in this function that you may not have seenbefore. In transactional (not Concurrent Data Store) applicationssupporting both readers and writers, or just multiple writers, m4_dbfunctions have an additional possible error return:m4_ref(DB_LOCK_DEADLOCK). This means two threads of control deadlocked,and the thread receiving the m4_ref(DB_LOCK_DEADLOCK) error return hasbeen selected to discard its locks in order to resolve the problem.When an application receives a m4_ref(DB_LOCK_DEADLOCK) return, thecorrect action is to close any cursors involved in the operation andabort any enclosing transaction. In the sample code, any time them4_refT(dbh_put) returns m4_ref(DB_LOCK_DEADLOCK), m4_ref(txn_abort) iscalled (which releases the transaction's m4_db resources and undoes anypartial changes to the databases), and then the transaction is retriedfrom the beginning.])m4_p([dnlThere is no requirement that the transaction be attempted again, butthat is a common course of action for applications. Applications maywant to set an upper bound on the number of times an operation will beretried because some operations on some data sets may simply be unableto succeed. For example, updating all of the pages on a large Web siteduring prime business hours may simply be impossible because of the highaccess rate to the database.])m4_p([dnlThe m4_refT(txn_abort) is called in error cases other than deadlock.Any time an error occurs, such that a transactionally protected set ofoperations cannot complete successfully, the transaction must beaborted. While deadlock is by far the most common of these errors,there are other possibilities; for example, running out of disk spacefor the filesystem. In m4_db transactional applications, there arethree classes of error returns: "expected" errors, "unexpected butrecoverable" errors, and a single "unrecoverable" error. Expectederrors are errors like m4_ref(DB_NOTFOUND), which indicates that asearched-for key item is not present in the database. Applications maywant to explicitly test for and handle this error, or, in the case wherethe absence of a key implies the enclosing transaction should fail,simply call m4_ref(txn_abort). Unexpected but recoverable errors areerrors like m4_ref(DB_LOCK_DEADLOCK), which indicates that an operationhas been selected to resolve a deadlock, or a system error such as EIO,which likely indicates that the filesystem has no available disk space.Applications must immediately call m4_ref(txn_abort) when these returnsoccur, as it is not possible to proceed otherwise. The onlyunrecoverable error is m4_ref(DB_RUNRECOVERY), which indicates that thesystem must stop and recovery must be run.])m4_p([dnlThe above code can be simplified in the case of a transaction comprisedentirely of a single database put or delete operation, as operationsoccurring in transactional databases are implicitly transactionprotected. For example, in a transactional database, the above codecould be more simply written as:])include(ref/transapp/put2.cs)m4_p([dnland the underlying transaction would be automatically handled by m4_db.])m4_p([dnlProgrammers should not attempt to enumerate all possible error returnsin their software. Instead, they should explicitly handle expectedreturns and default to aborting the transaction for the rest. It isentirely the choice of the programmer whether to check form4_ref(DB_RUNRECOVERY) explicitly or not -- attempting new m4_dboperations after m4_ref(DB_RUNRECOVERY) is returned does not worsen thesituation. Alternatively, using the m4_refT(dbenv_set_event_notify) tohandle an unrecoverable error and simply doing some number ofabort-and-retry cycles for any unexpected m4_db or system error in themainline code often results in the simplest and cleanest applicationcode.])m4_page_footer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -