txn_recover.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:8k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 2001-2002
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: txn_recover.c,v 1.36 2002/08/19 16:59:15 bostic Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #include <string.h>
  14. #endif
  15. #include "db_int.h"
  16. #include "dbinc/txn.h"
  17. #include "dbinc/db_page.h"
  18. #include "dbinc/log.h"
  19. #include "dbinc_auto/db_auto.h"
  20. #include "dbinc_auto/crdel_auto.h"
  21. #include "dbinc_auto/db_ext.h"
  22. /*
  23.  * __txn_continue
  24.  * Fill in the fields of the local transaction structure given
  25.  * the detail transaction structure.
  26.  *
  27.  * XXX
  28.  * I'm not sure that we work correctly with nested txns.
  29.  *
  30.  * PUBLIC: void __txn_continue __P((DB_ENV *, DB_TXN *, TXN_DETAIL *, size_t));
  31.  */
  32. void
  33. __txn_continue(env, txnp, td, off)
  34. DB_ENV *env;
  35. DB_TXN *txnp;
  36. TXN_DETAIL *td;
  37. size_t off;
  38. {
  39. txnp->mgrp = env->tx_handle;
  40. txnp->parent = NULL;
  41. txnp->last_lsn = td->last_lsn;
  42. txnp->txnid = td->txnid;
  43. txnp->off = (roff_t)off;
  44. txnp->abort = __txn_abort;
  45. txnp->commit = __txn_commit;
  46. txnp->discard = __txn_discard;
  47. txnp->id = __txn_id;
  48. txnp->prepare = __txn_prepare;
  49. txnp->flags = 0;
  50. }
  51. /*
  52.  * __txn_map_gid
  53.  * Return the txn that corresponds to this global ID.
  54.  *
  55.  * PUBLIC: int __txn_map_gid __P((DB_ENV *,
  56.  * PUBLIC:     u_int8_t *, TXN_DETAIL **, size_t *));
  57.  */
  58. int
  59. __txn_map_gid(dbenv, gid, tdp, offp)
  60. DB_ENV *dbenv;
  61. u_int8_t *gid;
  62. TXN_DETAIL **tdp;
  63. size_t *offp;
  64. {
  65. DB_TXNMGR *mgr;
  66. DB_TXNREGION *tmr;
  67. mgr = dbenv->tx_handle;
  68. tmr = mgr->reginfo.primary;
  69. /*
  70.  * Search the internal active transaction table to find the
  71.  * matching xid.  If this is a performance hit, then we
  72.  * can create a hash table, but I doubt it's worth it.
  73.  */
  74. R_LOCK(dbenv, &mgr->reginfo);
  75. for (*tdp = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail);
  76.     *tdp != NULL;
  77.     *tdp = SH_TAILQ_NEXT(*tdp, links, __txn_detail))
  78. if (memcmp(gid, (*tdp)->xid, sizeof((*tdp)->xid)) == 0)
  79. break;
  80. R_UNLOCK(dbenv, &mgr->reginfo);
  81. if (*tdp == NULL)
  82. return (EINVAL);
  83. *offp = R_OFFSET(&mgr->reginfo, *tdp);
  84. return (0);
  85. }
  86. /*
  87.  * __txn_recover --
  88.  * Public interface to retrieve the list of prepared, but not yet
  89.  * commited transactions.  See __txn_get_prepared for details.  This
  90.  * function and __db_xa_recover both wrap that one.
  91.  *
  92.  * PUBLIC: int __txn_recover
  93.  * PUBLIC:     __P((DB_ENV *, DB_PREPLIST *, long, long *, u_int32_t));
  94.  */
  95. int
  96. __txn_recover(dbenv, preplist, count, retp, flags)
  97. DB_ENV *dbenv;
  98. DB_PREPLIST *preplist;
  99. long count, *retp;
  100. u_int32_t flags;
  101. {
  102. PANIC_CHECK(dbenv);
  103. ENV_REQUIRES_CONFIG(
  104.     dbenv, dbenv->tx_handle, "txn_recover", DB_INIT_TXN);
  105. if (F_ISSET((DB_TXNREGION *)
  106.     ((DB_TXNMGR *)dbenv->tx_handle)->reginfo.primary,
  107.     TXN_IN_RECOVERY)) {
  108. __db_err(dbenv, "operation not permitted while in recovery");
  109. return (EINVAL);
  110. }
  111. return (__txn_get_prepared(dbenv, NULL, preplist, count, retp, flags));
  112. }
  113. /*
  114.  * __txn_get_prepared --
  115.  * Returns a list of prepared (and for XA, heuristically completed)
  116.  * transactions (less than or equal to the count parameter).  One of
  117.  * xids or txns must be set to point to an array of the appropriate type.
  118.  * The count parameter indicates the number of entries in the xids and/or
  119.  * txns array. The retp parameter will be set to indicate the number of
  120.  * entries returned in the xids/txns array.  Flags indicates the operation,
  121.  * one of DB_FIRST or DB_NEXT.
  122.  *
  123.  * PUBLIC: int __txn_get_prepared __P((DB_ENV *,
  124.  * PUBLIC:     XID *, DB_PREPLIST *, long, long *, u_int32_t));
  125.  */
  126. int
  127. __txn_get_prepared(dbenv, xids, txns, count, retp, flags)
  128. DB_ENV *dbenv;
  129. XID *xids;
  130. DB_PREPLIST *txns;
  131. long count; /* This is long for XA compatibility. */
  132. long  *retp;
  133. u_int32_t flags;
  134. {
  135. DBT data;
  136. DB_LOGC *logc;
  137. DB_LSN min, open_lsn;
  138. DB_PREPLIST *prepp;
  139. DB_TXNMGR *mgr;
  140. DB_TXNREGION *tmr;
  141. TXN_DETAIL *td;
  142. XID *xidp;
  143. __txn_ckp_args *ckp_args;
  144. long i;
  145. int nrestores, open_files, ret, t_ret;
  146. void *txninfo;
  147. *retp = 0;
  148. logc = NULL;
  149. MAX_LSN(min);
  150. prepp = txns;
  151. xidp = xids;
  152. nrestores = ret = 0;
  153. open_files = 1;
  154. /*
  155.  * If we are starting a scan, then we traverse the active transaction
  156.  * list once making sure that all transactions are marked as not having
  157.  * been collected.  Then on each pass, we mark the ones we collected
  158.  * so that if we cannot collect them all at once, we can finish up
  159.  * next time with a continue.
  160.  */
  161. mgr = dbenv->tx_handle;
  162. tmr = mgr->reginfo.primary;
  163. /*
  164.  * During this pass we need to figure out if we are going to need
  165.  * to open files.  We need to open files if we've never collected
  166.  * before (in which case, none of the COLLECTED bits will be set)
  167.  * and the ones that we are collecting are restored (if they aren't
  168.  * restored, then we never crashed; just the main server did).
  169.  */
  170. R_LOCK(dbenv, &mgr->reginfo);
  171. if (flags == DB_FIRST) {
  172. for (td = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail);
  173.     td != NULL;
  174.     td = SH_TAILQ_NEXT(td, links, __txn_detail)) {
  175. if (F_ISSET(td, TXN_RESTORED))
  176. nrestores++;
  177. if (F_ISSET(td, TXN_COLLECTED))
  178. open_files = 0;
  179. F_CLR(td, TXN_COLLECTED);
  180. }
  181. mgr->n_discards = 0;
  182. } else
  183. open_files = 0;
  184. /* Now begin collecting active transactions. */
  185. for (td = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail);
  186.     td != NULL && *retp < count;
  187.     td = SH_TAILQ_NEXT(td, links, __txn_detail)) {
  188. if (td->status != TXN_PREPARED || F_ISSET(td, TXN_COLLECTED))
  189. continue;
  190. if (xids != NULL) {
  191. xidp->formatID = td->format;
  192. xidp->gtrid_length = td->gtrid;
  193. xidp->bqual_length = td->bqual;
  194. memcpy(xidp->data, td->xid, sizeof(td->xid));
  195. xidp++;
  196. }
  197. if (txns != NULL) {
  198. if ((ret = __os_calloc(dbenv,
  199.     1, sizeof(DB_TXN), &prepp->txn)) != 0)
  200. goto err;
  201. __txn_continue(dbenv,
  202.     prepp->txn, td, R_OFFSET(&mgr->reginfo, td));
  203. F_SET(prepp->txn, TXN_MALLOC);
  204. memcpy(prepp->gid, td->xid, sizeof(td->xid));
  205. prepp++;
  206. }
  207. if (log_compare(&td->begin_lsn, &min) < 0)
  208. min = td->begin_lsn;
  209. (*retp)++;
  210. F_SET(td, TXN_COLLECTED);
  211. }
  212. R_UNLOCK(dbenv, &mgr->reginfo);
  213. /*
  214.  * Now link all the transactions into the transaction manager's list.
  215.  */
  216. if (txns != NULL) {
  217. MUTEX_THREAD_LOCK(dbenv, mgr->mutexp);
  218. for (i = 0; i < *retp; i++)
  219. TAILQ_INSERT_TAIL(&mgr->txn_chain, txns[i].txn, links);
  220. MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp);
  221. }
  222. if (open_files && nrestores && *retp != 0 && !IS_MAX_LSN(min)) {
  223. /*
  224.  * Figure out the last checkpoint before the smallest
  225.  * start_lsn in the region.
  226.  */
  227. F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  228. if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0)
  229. goto err;
  230. memset(&data, 0, sizeof(data));
  231. if ((ret = __txn_getckp(dbenv, &open_lsn)) == 0)
  232. while (!IS_ZERO_LSN(open_lsn) && (ret =
  233.     logc->get(logc, &open_lsn, &data, DB_SET)) == 0 &&
  234.     log_compare(&min, &open_lsn) < 0) {
  235. /* Format the log record. */
  236. if ((ret = __txn_ckp_read(dbenv,
  237.     data.data, &ckp_args)) != 0) {
  238. __db_err(dbenv,
  239.     "Invalid checkpoint record at [%lu][%lu]",
  240.     (u_long)open_lsn.file,
  241.     (u_long)open_lsn.offset);
  242. goto err;
  243. }
  244. open_lsn = ckp_args->last_ckp;
  245. __os_free(dbenv, ckp_args);
  246. }
  247. /*
  248.  * There are three ways by which we may have gotten here.
  249.  * - We got a DB_NOTFOUND -- we need to read the first
  250.  * log record.
  251.  * - We found a checkpoint before min.  We're done.
  252.  * - We found a checkpoint after min who's last_ckp is 0.  We
  253.  * need to start at the beginning of the log.
  254.  */
  255. if ((ret == DB_NOTFOUND || IS_ZERO_LSN(open_lsn)) &&
  256.     (ret = logc->get(logc, &open_lsn, &data, DB_FIRST)) != 0) {
  257. __db_err(dbenv, "No log records");
  258. goto err;
  259. }
  260. if ((ret = __db_txnlist_init(dbenv, 0, 0, NULL, &txninfo)) != 0)
  261. goto err;
  262. ret = __env_openfiles(dbenv, logc,
  263.     txninfo, &data, &open_lsn, NULL, 0, 0);
  264. if (txninfo != NULL)
  265. __db_txnlist_end(dbenv, txninfo);
  266. }
  267. err: F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  268. if (logc != NULL && (t_ret = logc->close(logc, 0)) != 0 && ret == 0)
  269. ret = t_ret;
  270. return (ret);
  271. }