txn.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:21k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996, 1997, 1998, 1999, 2000
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. /*
  8.  * Copyright (c) 1995, 1996
  9.  * The President and Fellows of Harvard University.  All rights reserved.
  10.  *
  11.  * This code is derived from software contributed to Berkeley by
  12.  * Margo Seltzer.
  13.  *
  14.  * Redistribution and use in source and binary forms, with or without
  15.  * modification, are permitted provided that the following conditions
  16.  * are met:
  17.  * 1. Redistributions of source code must retain the above copyright
  18.  *    notice, this list of conditions and the following disclaimer.
  19.  * 2. Redistributions in binary form must reproduce the above copyright
  20.  *    notice, this list of conditions and the following disclaimer in the
  21.  *    documentation and/or other materials provided with the distribution.
  22.  * 3. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38. #include "db_config.h"
  39. #ifndef lint
  40. static const char revid[] = "$Id: txn.c,v 11.61 2001/01/10 18:18:52 bostic Exp $";
  41. #endif /* not lint */
  42. #ifndef NO_SYSTEM_INCLUDES
  43. #include <sys/types.h>
  44. #if TIME_WITH_SYS_TIME
  45. #include <sys/time.h>
  46. #include <time.h>
  47. #else
  48. #if HAVE_SYS_TIME_H
  49. #include <sys/time.h>
  50. #else
  51. #include <time.h>
  52. #endif
  53. #endif
  54. #include <string.h>
  55. #endif
  56. #ifdef  HAVE_RPC
  57. #include "db_server.h"
  58. #endif
  59. #include "db_int.h"
  60. #include "db_shash.h"
  61. #include "txn.h"
  62. #include "lock.h"
  63. #include "log.h"
  64. #include "db_dispatch.h"
  65. #include "db_page.h"
  66. #include "db_ext.h"
  67. #ifdef HAVE_RPC
  68. #include "gen_client_ext.h"
  69. #include "rpc_client_ext.h"
  70. #endif
  71. static int  __txn_begin __P((DB_TXN *));
  72. static int  __txn_isvalid __P((const DB_TXN *, TXN_DETAIL **, u_int32_t));
  73. static int  __txn_undo __P((DB_TXN *));
  74. /*
  75.  * txn_begin --
  76.  * This is a wrapper to the actual begin process.  Normal txn_begin()
  77.  * allocates a DB_TXN structure for the caller, while txn_xa_begin() does
  78.  * not.  Other than that, both call into the common __txn_begin code().
  79.  *
  80.  * Internally, we use TXN_DETAIL structures, but the DB_TXN structure
  81.  * provides access to the transaction ID and the offset in the transaction
  82.  * region of the TXN_DETAIL structure.
  83.  */
  84. int
  85. txn_begin(dbenv, parent, txnpp, flags)
  86. DB_ENV *dbenv;
  87. DB_TXN *parent, **txnpp;
  88. u_int32_t flags;
  89. {
  90. DB_TXN *txn;
  91. int ret;
  92. #ifdef HAVE_RPC
  93. if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
  94. return (__dbcl_txn_begin(dbenv, parent, txnpp, flags));
  95. #endif
  96. PANIC_CHECK(dbenv);
  97. ENV_REQUIRES_CONFIG(dbenv, dbenv->tx_handle, DB_INIT_TXN);
  98. if ((ret = __db_fchk(dbenv,
  99.     "txn_begin", flags,
  100.     DB_TXN_NOWAIT | DB_TXN_NOSYNC | DB_TXN_SYNC)) != 0)
  101. return (ret);
  102. if ((ret = __db_fcchk(dbenv,
  103.     "txn_begin", flags, DB_TXN_NOSYNC, DB_TXN_SYNC)) != 0)
  104. return (ret);
  105. if ((ret = __os_calloc(dbenv, 1, sizeof(DB_TXN), &txn)) != 0)
  106. return (ret);
  107. txn->mgrp = dbenv->tx_handle;
  108. txn->parent = parent;
  109. TAILQ_INIT(&txn->kids);
  110. txn->flags = TXN_MALLOC;
  111. if (LF_ISSET(DB_TXN_NOSYNC))
  112. F_SET(txn, TXN_NOSYNC);
  113. if (LF_ISSET(DB_TXN_SYNC))
  114. F_SET(txn, TXN_SYNC);
  115. if (LF_ISSET(DB_TXN_NOWAIT))
  116. F_SET(txn, TXN_NOWAIT);
  117. if ((ret = __txn_begin(txn)) != 0) {
  118. __os_free(txn, sizeof(DB_TXN));
  119. txn = NULL;
  120. }
  121. if (txn != NULL && parent != NULL)
  122. TAILQ_INSERT_HEAD(&parent->kids, txn, klinks);
  123. *txnpp = txn;
  124. return (ret);
  125. }
  126. /*
  127.  * __txn_xa_begin --
  128.  * XA version of txn_begin.
  129.  *
  130.  * PUBLIC: int __txn_xa_begin __P((DB_ENV *, DB_TXN *));
  131.  */
  132. int
  133. __txn_xa_begin(dbenv, txn)
  134. DB_ENV *dbenv;
  135. DB_TXN *txn;
  136. {
  137. PANIC_CHECK(dbenv);
  138. memset(txn, 0, sizeof(DB_TXN));
  139. txn->mgrp = dbenv->tx_handle;
  140. return (__txn_begin(txn));
  141. }
  142. /*
  143.  * __txn_begin --
  144.  * Normal DB version of txn_begin.
  145.  */
  146. static int
  147. __txn_begin(txn)
  148. DB_TXN *txn;
  149. {
  150. DB_ENV *dbenv;
  151. DB_LSN begin_lsn;
  152. DB_TXNMGR *mgr;
  153. DB_TXNREGION *region;
  154. TXN_DETAIL *td;
  155. size_t off;
  156. u_int32_t id;
  157. int ret;
  158. mgr = txn->mgrp;
  159. dbenv = mgr->dbenv;
  160. region = mgr->reginfo.primary;
  161. /*
  162.  * We do not have to write begin records (and if we do not, then we
  163.  * need never write records for read-only transactions).  However,
  164.  * we do need to find the current LSN so that we can store it in the
  165.  * transaction structure, so we can know where to take checkpoints.
  166.  */
  167. if (LOGGING_ON(dbenv) &&
  168.     (ret = log_put(dbenv, &begin_lsn, NULL, DB_CURLSN)) != 0)
  169. goto err2;
  170. R_LOCK(dbenv, &mgr->reginfo);
  171. /* Make sure that last_txnid is not going to wrap around. */
  172. if (region->last_txnid == TXN_INVALID) {
  173. __db_err(dbenv,
  174. "txn_begin: transaction ID wrapped.  Exit the database environmentnand restart the application as if application failure had occurred");
  175. ret = EINVAL;
  176. goto err1;
  177. }
  178. /* Allocate a new transaction detail structure. */
  179. if ((ret =
  180.     __db_shalloc(mgr->reginfo.addr, sizeof(TXN_DETAIL), 0, &td)) != 0) {
  181. __db_err(dbenv,
  182.      "Unable to allocate memory for transaction detail");
  183. goto err1;
  184. }
  185. /* Place transaction on active transaction list. */
  186. SH_TAILQ_INSERT_HEAD(&region->active_txn, td, links, __txn_detail);
  187. id = ++region->last_txnid;
  188. ++region->nbegins;
  189. if (++region->nactive > region->maxnactive)
  190. region->maxnactive = region->nactive;
  191. td->txnid = id;
  192. td->begin_lsn = begin_lsn;
  193. ZERO_LSN(td->last_lsn);
  194. td->status = TXN_RUNNING;
  195. if (txn->parent != NULL)
  196. td->parent = txn->parent->off;
  197. else
  198. td->parent = INVALID_ROFF;
  199. off = R_OFFSET(&mgr->reginfo, td);
  200. R_UNLOCK(dbenv, &mgr->reginfo);
  201. ZERO_LSN(txn->last_lsn);
  202. txn->txnid = id;
  203. txn->off = off;
  204. /*
  205.  * If this is a transaction family, we must link the child to the
  206.  * maximal grandparent in the lock table for deadlock detection.
  207.  */
  208. if (txn->parent != NULL && LOCKING_ON(dbenv))
  209. if ((ret = __lock_addfamilylocker(dbenv,
  210.     txn->parent->txnid, txn->txnid)) != 0)
  211. goto err2;
  212. if (F_ISSET(txn, TXN_MALLOC)) {
  213. MUTEX_THREAD_LOCK(dbenv, mgr->mutexp);
  214. TAILQ_INSERT_TAIL(&mgr->txn_chain, txn, links);
  215. MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp);
  216. }
  217. return (0);
  218. err1: R_UNLOCK(dbenv, &mgr->reginfo);
  219. err2: return (ret);
  220. }
  221. /*
  222.  * txn_commit --
  223.  * Commit a transaction.
  224.  */
  225. int
  226. txn_commit(txnp, flags)
  227. DB_TXN *txnp;
  228. u_int32_t flags;
  229. {
  230. DB_ENV *dbenv;
  231. DB_TXN *kid;
  232. int is_commit, ret, t_ret;
  233. dbenv = txnp->mgrp->dbenv;
  234. #ifdef HAVE_RPC
  235. if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
  236. return (__dbcl_txn_commit(txnp, flags));
  237. #endif
  238. PANIC_CHECK(dbenv);
  239. if ((ret = __txn_isvalid(txnp, NULL, TXN_COMMITTED)) != 0)
  240. return (ret);
  241. /*
  242.  * We clear flags that are incorrect, ignoring any flag errors, and
  243.  * default to synchronous operations.  By definition, transaction
  244.  * handles are dead when we return, and this error should never
  245.  * happen, but we don't want to fail in the field 'cause the app is
  246.  * specifying the wrong flag for some reason.
  247.  */
  248. if (__db_fchk(dbenv,
  249.     "txn_commit", flags, DB_TXN_NOSYNC | DB_TXN_SYNC) != 0)
  250. flags = DB_TXN_SYNC;
  251. if (__db_fcchk(dbenv,
  252.     "txn_commit", flags, DB_TXN_NOSYNC, DB_TXN_SYNC) != 0)
  253. flags = DB_TXN_SYNC;
  254. if (LF_ISSET(DB_TXN_NOSYNC)) {
  255. F_CLR(txnp, TXN_SYNC);
  256. F_SET(txnp, TXN_NOSYNC);
  257. }
  258. if (LF_ISSET(DB_TXN_SYNC)) {
  259. F_CLR(txnp, TXN_NOSYNC);
  260. F_SET(txnp, TXN_SYNC);
  261. }
  262. /*
  263.  * Commit any unresolved children.  If there's an error, abort any
  264.  * unresolved children and the parent.
  265.  */
  266. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  267. if ((ret = txn_commit(kid, flags)) != 0) {
  268. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  269. (void)txn_abort(kid);
  270. (void)txn_abort(txnp);
  271. goto err;
  272. }
  273. /*
  274.  * If there are any log records, write a log record and sync the log,
  275.  * else do no log writes.  If the commit is for a child transaction,
  276.  * we do not need to commit the child synchronously since it may still
  277.  * abort (if its parent aborts), and otherwise its parent or ultimate
  278.  * ancestor will write synchronously.
  279.  *
  280.  * I'd rather return a logging error than a flag-wrong error, so if
  281.  * the log routines fail, set "ret" without regard to previous value.
  282.  */
  283. if (LOGGING_ON(dbenv) && !IS_ZERO_LSN(txnp->last_lsn)) {
  284. if (txnp->parent == NULL) {
  285. if ((t_ret = __txn_regop_log(dbenv,
  286.     txnp, &txnp->last_lsn,
  287.     (F_ISSET(dbenv, DB_ENV_TXN_NOSYNC) &&
  288.     !F_ISSET(txnp, TXN_SYNC)) ||
  289.     F_ISSET(txnp, TXN_NOSYNC) ?  0 : DB_FLUSH,
  290.     TXN_COMMIT, (int32_t)time(NULL))) != 0) {
  291. ret = t_ret;
  292. goto err;
  293. }
  294. } else {
  295. /* Log the commit in the parent! */
  296. if ((t_ret = __txn_child_log(dbenv,
  297.     txnp->parent, &txnp->parent->last_lsn,
  298.     0, txnp->txnid, &txnp->last_lsn)) != 0) {
  299. ret = t_ret;
  300. goto err;
  301. }
  302. F_SET(txnp->parent, TXN_CHILDCOMMIT);
  303. }
  304. }
  305. is_commit = 1;
  306. if (0) {
  307. err: is_commit = 0;
  308. }
  309. if ((t_ret = __txn_end(txnp, is_commit)) != 0 && ret == 0)
  310. ret = t_ret;
  311. return (ret);
  312. }
  313. /*
  314.  * txn_abort --
  315.  * Abort a transaction.
  316.  */
  317. int
  318. txn_abort(txnp)
  319. DB_TXN *txnp;
  320. {
  321. DB_ENV *dbenv;
  322. DB_TXN *kid;
  323. int ret, t_ret;
  324. dbenv = txnp->mgrp->dbenv;
  325. #ifdef HAVE_RPC
  326. if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
  327. return (__dbcl_txn_abort(txnp));
  328. #endif
  329. PANIC_CHECK(dbenv);
  330. if ((ret = __txn_isvalid(txnp, NULL, TXN_ABORTED)) != 0)
  331. return (ret);
  332. /* Abort any unresolved children. */
  333. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  334. if ((t_ret = txn_abort(kid)) != 0 && ret == 0)
  335. ret = t_ret;
  336. if ((t_ret = __txn_undo(txnp)) != 0 && ret == 0)
  337. ret = t_ret;
  338. if ((t_ret = __txn_end(txnp, 0)) != 0 && ret == 0)
  339. ret = t_ret;
  340. return (ret);
  341. }
  342. /*
  343.  * txn_prepare --
  344.  * Flush the log so a future commit is guaranteed to succeed.
  345.  */
  346. int
  347. txn_prepare(txnp)
  348. DB_TXN *txnp;
  349. {
  350. DBT xid;
  351. DB_ENV *dbenv;
  352. DB_TXN *kid;
  353. TXN_DETAIL *td;
  354. int ret;
  355. dbenv = txnp->mgrp->dbenv;
  356. #ifdef HAVE_RPC
  357. if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
  358. return (__dbcl_txn_prepare(txnp));
  359. #endif
  360. PANIC_CHECK(dbenv);
  361. if ((ret = __txn_isvalid(txnp, &td, TXN_PREPARED)) != 0)
  362. return (ret);
  363. /* Prepare any unresolved children. */
  364. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  365. if ((ret = txn_prepare(kid)) != 0)
  366. return (ret);
  367. /*
  368.  * We indicate that a transaction is an XA transaction by putting
  369.  * a valid size in the xid.size fiels.  XA requires that the transaction
  370.  * be either ENDED or SUSPENDED when prepare is called, so we know
  371.  * that if the xa_status isn't in one of those states, but we are
  372.  * calling prepare that we are not an XA transaction.
  373.  */
  374. if (LOGGING_ON(dbenv)) {
  375. memset(&xid, 0, sizeof(xid));
  376. xid.data = td->xid;
  377. xid.size = td->xa_status != TXN_XA_ENDED &&
  378.     td->xa_status != TXN_XA_SUSPENDED ?  0 : sizeof(td->xid);
  379. if ((ret = __txn_xa_regop_log(dbenv, txnp, &txnp->last_lsn,
  380.     (F_ISSET(dbenv, DB_ENV_TXN_NOSYNC) &&
  381.     !F_ISSET(txnp, TXN_SYNC)) ||
  382.     F_ISSET(txnp, TXN_NOSYNC) ? 0 : DB_FLUSH, TXN_PREPARE,
  383.     &xid, td->format, td->gtrid, td->bqual,
  384.     &td->begin_lsn)) != 0) {
  385. __db_err(dbenv, "txn_prepare: log_write failed %s",
  386.     db_strerror(ret));
  387. return (ret);
  388. }
  389. if (txnp->parent != NULL)
  390. F_SET(txnp->parent, TXN_CHILDCOMMIT);
  391. }
  392. MUTEX_THREAD_LOCK(dbenv, txnp->mgrp->mutexp);
  393. td->status = TXN_PREPARED;
  394. MUTEX_THREAD_UNLOCK(dbenv, txnp->mgrp->mutexp);
  395. return (0);
  396. }
  397. /*
  398.  * txn_id --
  399.  * Return the transaction ID.
  400.  */
  401. u_int32_t
  402. txn_id(txnp)
  403. DB_TXN *txnp;
  404. {
  405. return (txnp->txnid);
  406. }
  407. /*
  408.  * __txn_isvalid --
  409.  * Return 0 if the txnp is reasonable, otherwise panic.
  410.  */
  411. static int
  412. __txn_isvalid(txnp, tdp, op)
  413. const DB_TXN *txnp;
  414. TXN_DETAIL **tdp;
  415. u_int32_t op;
  416. {
  417. DB_TXNMGR *mgrp;
  418. TXN_DETAIL *tp;
  419. mgrp = txnp->mgrp;
  420. /* Check for live cursors. */
  421. if (txnp->cursors != 0) {
  422. __db_err(mgrp->dbenv, "transaction has active cursors");
  423. goto err;
  424. }
  425. /* Check transaction's status. */
  426. tp = (TXN_DETAIL *)R_ADDR(&mgrp->reginfo, txnp->off);
  427. if (tdp != NULL)
  428. *tdp = tp;
  429. switch (tp->status) {
  430. case TXN_ABORTED:
  431. case TXN_COMMITTED:
  432. default:
  433. __db_err(mgrp->dbenv, "transaction already %s",
  434.     tp->status == TXN_COMMITTED ? "committed" : "aborted");
  435. goto err;
  436. case TXN_PREPARED:
  437. if (op == TXN_PREPARED) {
  438. __db_err(mgrp->dbenv, "transaction already prepared");
  439. goto err;
  440. }
  441. case TXN_RUNNING:
  442. break;
  443. }
  444. return (0);
  445. err: /*
  446.  * If there's a serious problem with the transaction, panic.  TXN
  447.  * handles are dead by definition when we return, and if you use
  448.  * a cursor you forgot to close, we have no idea what will happen.
  449.  */
  450. return (__db_panic(mgrp->dbenv, EINVAL));
  451. }
  452. /*
  453.  * __txn_end --
  454.  * Internal transaction end routine.
  455.  *
  456.  * PUBLIC: int __txn_end __P((DB_TXN *, int));
  457.  */
  458. int
  459. __txn_end(txnp, is_commit)
  460. DB_TXN *txnp;
  461. int is_commit;
  462. {
  463. DB_ENV *dbenv;
  464. DB_LOCKREQ request;
  465. DB_TXNMGR *mgr;
  466. DB_TXNREGION *region;
  467. TXN_DETAIL *tp;
  468. int ret;
  469. mgr = txnp->mgrp;
  470. dbenv = mgr->dbenv;
  471. region = mgr->reginfo.primary;
  472. /* Release the locks. */
  473. request.op = txnp->parent == NULL ||
  474.     is_commit == 0 ? DB_LOCK_PUT_ALL : DB_LOCK_INHERIT;
  475. if (LOCKING_ON(dbenv)) {
  476. ret = lock_vec(dbenv, txnp->txnid, 0, &request, 1, NULL);
  477. if (ret != 0 && (ret != DB_LOCK_DEADLOCK || is_commit)) {
  478. __db_err(dbenv, "%s: release locks failed %s",
  479.     is_commit ? "txn_commit" : "txn_abort",
  480.     db_strerror(ret));
  481. __db_panic(dbenv, ret);
  482. }
  483. }
  484. /* End the transaction. */
  485. R_LOCK(dbenv, &mgr->reginfo);
  486. tp = (TXN_DETAIL *)R_ADDR(&mgr->reginfo, txnp->off);
  487. SH_TAILQ_REMOVE(&region->active_txn, tp, links, __txn_detail);
  488. __db_shalloc_free(mgr->reginfo.addr, tp);
  489. if (is_commit)
  490. region->ncommits++;
  491. else
  492. region->naborts++;
  493. --region->nactive;
  494. R_UNLOCK(dbenv, &mgr->reginfo);
  495. /*
  496.  * The transaction cannot get more locks, remove its locker info.
  497.  */
  498. if (LOCKING_ON(dbenv))
  499. __lock_freefamilylocker(dbenv->lk_handle, txnp->txnid);
  500. if (txnp->parent != NULL)
  501. TAILQ_REMOVE(&txnp->parent->kids, txnp, klinks);
  502. /* Free the space. */
  503. if (F_ISSET(txnp, TXN_MALLOC)) {
  504. MUTEX_THREAD_LOCK(dbenv, mgr->mutexp);
  505. TAILQ_REMOVE(&mgr->txn_chain, txnp, links);
  506. MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp);
  507. __os_free(txnp, sizeof(*txnp));
  508. }
  509. return (0);
  510. }
  511. /*
  512.  * __txn_undo --
  513.  * Undo the transaction with id txnid.  Returns 0 on success and
  514.  * errno on failure.
  515.  */
  516. static int
  517. __txn_undo(txnp)
  518. DB_TXN *txnp;
  519. {
  520. DBT rdbt;
  521. DB_ENV *dbenv;
  522. DB_LSN key_lsn;
  523. DB_TXNMGR *mgr;
  524. void *txnlist;
  525. int ret, threaded;
  526. mgr = txnp->mgrp;
  527. dbenv = mgr->dbenv;
  528. txnlist = NULL;
  529. if (!LOGGING_ON(dbenv))
  530. return (0);
  531. /*
  532.  * This is the simplest way to code this, but if the mallocs during
  533.  * recovery turn out to be a performance issue, we can do the
  534.  * allocation here and use DB_DBT_USERMEM.
  535.  */
  536. memset(&rdbt, 0, sizeof(rdbt));
  537. threaded = F_ISSET(dbenv, DB_ENV_THREAD) ? 1 : 0;
  538. if (threaded)
  539. F_SET(&rdbt, DB_DBT_MALLOC);
  540. key_lsn = txnp->last_lsn;
  541. /* Allocate a transaction list for children or aborted page creates. */
  542. if ((ret = __db_txnlist_init(dbenv, &txnlist)) != 0)
  543. return (ret);
  544. if (F_ISSET(txnp, TXN_CHILDCOMMIT) &&
  545.     (ret = __db_txnlist_lsninit(dbenv,
  546.     txnlist, &txnp->last_lsn)) != 0)
  547. return (ret);
  548. for (ret = 0; ret == 0 && !IS_ZERO_LSN(key_lsn);) {
  549. /*
  550.  * The dispatch routine returns the lsn of the record
  551.  * before the current one in the key_lsn argument.
  552.  */
  553. if ((ret = log_get(dbenv, &key_lsn, &rdbt, DB_SET)) == 0) {
  554. ret = __db_dispatch(dbenv,
  555.     &rdbt, &key_lsn, DB_TXN_ABORT, txnlist);
  556. if (threaded && rdbt.data != NULL) {
  557. __os_free(rdbt.data, rdbt.size);
  558. rdbt.data = NULL;
  559. }
  560. if (F_ISSET(txnp, TXN_CHILDCOMMIT))
  561. (void)__db_txnlist_lsnadd(dbenv,
  562.     txnlist, &key_lsn, 0);
  563. }
  564. if (ret != 0) {
  565. __db_err(txnp->mgrp->dbenv,
  566.     "txn_abort: Log undo failed for LSN: %lu %lu: %s",
  567.     (u_long)key_lsn.file, (u_long)key_lsn.offset,
  568.     db_strerror(ret));
  569. if (txnlist != NULL)
  570. __db_txnlist_end(dbenv, txnlist);
  571. return (ret);
  572. }
  573. }
  574. if (txnlist != NULL) {
  575. __db_do_the_limbo(dbenv, txnlist);
  576. __db_txnlist_end(dbenv, txnlist);
  577. }
  578. return (ret);
  579. }
  580. /*
  581.  * Transaction checkpoint.
  582.  * If either kbytes or minutes is non-zero, then we only take the checkpoint
  583.  * more than "minutes" minutes have passed since the last checkpoint or if
  584.  * more than "kbytes" of log data have been written since the last checkpoint.
  585.  * When taking a checkpoint, find the oldest active transaction and figure out
  586.  * its first LSN.  This is the lowest LSN we can checkpoint, since any record
  587.  * written after since that point may be involved in a transaction and may
  588.  * therefore need to be undone in the case of an abort.
  589.  */
  590. int
  591. txn_checkpoint(dbenv, kbytes, minutes, flags)
  592. DB_ENV *dbenv;
  593. u_int32_t kbytes, minutes, flags;
  594. {
  595. DB_LOG *dblp;
  596. DB_LSN ckp_lsn, sync_lsn, last_ckp;
  597. DB_TXNMGR *mgr;
  598. DB_TXNREGION *region;
  599. LOG *lp;
  600. TXN_DETAIL *txnp;
  601. time_t last_ckp_time, now;
  602. u_int32_t bytes, mbytes;
  603. int interval, ret;
  604. #ifdef HAVE_RPC
  605. if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
  606. return (__dbcl_txn_checkpoint(dbenv, kbytes, minutes));
  607. #endif
  608. PANIC_CHECK(dbenv);
  609. ENV_REQUIRES_CONFIG(dbenv, dbenv->tx_handle, DB_INIT_TXN);
  610. mgr = dbenv->tx_handle;
  611. region = mgr->reginfo.primary;
  612. dblp = dbenv->lg_handle;
  613. lp = dblp->reginfo.primary;
  614. /*
  615.  * Check if we need to checkpoint.
  616.  */
  617. ZERO_LSN(ckp_lsn);
  618. if (LF_ISSET(DB_FORCE))
  619. goto do_ckp;
  620. R_LOCK(dbenv, &dblp->reginfo);
  621. mbytes = lp->stat.st_wc_mbytes;
  622. /*
  623.  * We add the current buffer offset so as to count bytes that
  624.  * have not yet been written, but are sitting in the log buffer.
  625.  */
  626. bytes = lp->stat.st_wc_bytes + lp->b_off;
  627. ckp_lsn = lp->lsn;
  628. R_UNLOCK(dbenv, &dblp->reginfo);
  629. /* Don't checkpoint a quiescent database. */
  630. if (bytes == 0 && mbytes == 0)
  631. return (0);
  632. if (kbytes != 0 && mbytes * 1024 + bytes / 1024 >= (u_int32_t)kbytes)
  633. goto do_ckp;
  634. if (minutes != 0) {
  635. (void)time(&now);
  636. R_LOCK(dbenv, &mgr->reginfo);
  637. last_ckp_time = region->time_ckp;
  638. R_UNLOCK(dbenv, &mgr->reginfo);
  639. if (now - last_ckp_time >= (time_t)(minutes * 60))
  640. goto do_ckp;
  641. }
  642. /*
  643.  * If we checked time and data and didn't go to checkpoint,
  644.  * we're done.
  645.  */
  646. if (minutes != 0 || kbytes != 0)
  647. return (0);
  648. do_ckp:
  649. if (IS_ZERO_LSN(ckp_lsn)) {
  650. R_LOCK(dbenv, &dblp->reginfo);
  651. ckp_lsn = lp->lsn;
  652. R_UNLOCK(dbenv, &dblp->reginfo);
  653. }
  654. /*
  655.  * We have to find an LSN such that all transactions begun
  656.  * before that LSN are complete.
  657.  */
  658. R_LOCK(dbenv, &mgr->reginfo);
  659. if (IS_ZERO_LSN(region->pending_ckp)) {
  660. for (txnp =
  661.     SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
  662.     txnp != NULL;
  663.     txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) {
  664. /*
  665.  * Look through the active transactions for the
  666.  * lowest begin lsn.
  667.  */
  668. if (!IS_ZERO_LSN(txnp->begin_lsn) &&
  669.     log_compare(&txnp->begin_lsn, &ckp_lsn) < 0)
  670. ckp_lsn = txnp->begin_lsn;
  671. }
  672. region->pending_ckp = ckp_lsn;
  673. } else
  674. ckp_lsn = region->pending_ckp;
  675. R_UNLOCK(dbenv, &mgr->reginfo);
  676. /*
  677.  * Try three times to sync the mpool buffers up to the specified LSN,
  678.  * sleeping 1, 2 and 4 seconds between attempts.
  679.  */
  680. if (MPOOL_ON(dbenv))
  681. for (interval = 1;;) {
  682. /*
  683.  * memp_sync may change the lsn you pass it, so don't
  684.  * pass it the actual ckp_lsn, pass it a local instead.
  685.  */
  686. sync_lsn = ckp_lsn;
  687. if ((ret = memp_sync(dbenv, &sync_lsn)) == 0)
  688. break;
  689. /*
  690.  * ret == DB_INCOMPLETE means there are still buffers
  691.  * to flush, the checkpoint is not complete.
  692.  */
  693. if (ret == DB_INCOMPLETE) {
  694. if (interval > 4)
  695. return (ret);
  696. (void)__os_sleep(dbenv, interval, 0);
  697. interval *= 2;
  698. } else {
  699. __db_err(dbenv,
  700.     "txn_checkpoint: failure in memp_sync %s",
  701.     db_strerror(ret));
  702. return (ret);
  703. }
  704. }
  705. if (LOGGING_ON(dbenv)) {
  706. R_LOCK(dbenv, &mgr->reginfo);
  707. last_ckp = region->last_ckp;
  708. ZERO_LSN(region->pending_ckp);
  709. R_UNLOCK(dbenv, &mgr->reginfo);
  710. if ((ret = __txn_ckp_log(dbenv,
  711.     NULL, &ckp_lsn, DB_CHECKPOINT, &ckp_lsn,
  712.     &last_ckp, (int32_t)time(NULL))) != 0) {
  713. __db_err(dbenv,
  714.     "txn_checkpoint: log failed at LSN [%ld %ld] %s",
  715.     (long)ckp_lsn.file, (long)ckp_lsn.offset,
  716.     db_strerror(ret));
  717. return (ret);
  718. }
  719. R_LOCK(dbenv, &mgr->reginfo);
  720. region->last_ckp = ckp_lsn;
  721. (void)time(&region->time_ckp);
  722. R_UNLOCK(dbenv, &mgr->reginfo);
  723. }
  724. return (0);
  725. }
  726. /*
  727.  * __txn_activekids --
  728.  * Return if this transaction has any active children.
  729.  *
  730.  * PUBLIC: int __txn_activekids __P((DB_ENV *, u_int32_t, DB_TXN *));
  731.  */
  732. int
  733. __txn_activekids(dbenv, rectype, txnp)
  734. DB_ENV *dbenv;
  735. u_int32_t rectype;
  736. DB_TXN *txnp;
  737. {
  738. /*
  739.  * On a child commit, we know that there are children (i.e., the
  740.  * commiting child at the least.  In that case, skip this check.
  741.  */
  742. if (rectype == DB_txn_child)
  743. return (0);
  744. if (TAILQ_FIRST(&txnp->kids) != NULL) {
  745. __db_err(dbenv, "Child transaction is active");
  746. return (EPERM);
  747. }
  748. return (0);
  749. }