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

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996-2002
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char copyright[] =
  10.     "Copyright (c) 1996-2002nSleepycat Software Inc.  All rights reserved.n";
  11. static const char revid[] =
  12.     "$Id: env_recover.c,v 11.97 2002/08/22 17:43:22 margo Exp $";
  13. #endif
  14. #ifndef NO_SYSTEM_INCLUDES
  15. #include <sys/types.h>
  16. #if TIME_WITH_SYS_TIME
  17. #include <sys/time.h>
  18. #include <time.h>
  19. #else
  20. #if HAVE_SYS_TIME_H
  21. #include <sys/time.h>
  22. #else
  23. #include <time.h>
  24. #endif
  25. #endif
  26. #include <string.h>
  27. #endif
  28. #include "db_int.h"
  29. #include "dbinc/db_page.h"
  30. #include "dbinc/db_shash.h"
  31. #include "dbinc/lock.h"
  32. #include "dbinc/log.h"
  33. #include "dbinc/rep.h"
  34. #include "dbinc/txn.h"
  35. #include "dbinc/db_am.h"
  36. static int    __log_backup __P((DB_ENV *, DB_LOGC *, DB_LSN *, DB_LSN *));
  37. static int    __log_earliest __P((DB_ENV *, DB_LOGC *, int32_t *, DB_LSN *));
  38. static double __lsn_diff __P((DB_LSN *, DB_LSN *, DB_LSN *, u_int32_t, int));
  39. /*
  40.  * __db_apprec --
  41.  * Perform recovery.  If max_lsn is non-NULL, then we are trying
  42.  * to synchronize this system up with another system that has a max
  43.  * LSN of max_lsn, so we need to roll back sufficiently far for that
  44.  * to work.  See __log_backup for details.
  45.  *
  46.  * PUBLIC: int __db_apprec __P((DB_ENV *, DB_LSN *, u_int32_t));
  47.  */
  48. int
  49. __db_apprec(dbenv, max_lsn, flags)
  50. DB_ENV *dbenv;
  51. DB_LSN *max_lsn;
  52. u_int32_t flags;
  53. {
  54. DBT data;
  55. DB_LOGC *logc;
  56. DB_LSN ckp_lsn, first_lsn, last_lsn, lowlsn, lsn, stop_lsn;
  57. DB_TXNREGION *region;
  58. __txn_ckp_args *ckp_args;
  59. time_t now, tlow;
  60. int32_t log_size, low;
  61. double nfiles;
  62. int have_rec, is_thread, progress, ret, t_ret;
  63. int (**dtab) __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
  64. size_t dtabsize;
  65. u_int32_t hi_txn, lockid, txnid;
  66. char *p, *pass, t1[60], t2[60];
  67. void *txninfo;
  68. COMPQUIET(nfiles, (double)0);
  69. logc = NULL;
  70. ckp_args = NULL;
  71. dtab = NULL;
  72. hi_txn = TXN_MAXIMUM;
  73. lockid = DB_LOCK_INVALIDID;
  74. txninfo = NULL;
  75. pass = "initial";
  76. /*
  77.  * XXX
  78.  * Get the log size.  No locking required because we're single-threaded
  79.  * during recovery.
  80.  */
  81. log_size =
  82.    ((LOG *)(((DB_LOG *)dbenv->lg_handle)->reginfo.primary))->log_size;
  83. /*
  84.  * Save the state of the thread flag -- we don't need it on at the
  85.  * moment because we're single-threaded until recovery is complete.
  86.  */
  87. is_thread = F_ISSET(dbenv, DB_ENV_THREAD) ? 1 : 0;
  88. F_CLR(dbenv, DB_ENV_THREAD);
  89. /* Set in-recovery flags. */
  90. F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  91. region = ((DB_TXNMGR *)dbenv->tx_handle)->reginfo.primary;
  92. F_SET(region, TXN_IN_RECOVERY);
  93. /* Allocate a cursor for the log. */
  94. if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0)
  95. goto err;
  96. /*
  97.  * If the user is specifying recovery to a particular point in time
  98.  * or to a particular LSN, find the point to start recovery from.
  99.  */
  100. ZERO_LSN(lowlsn);
  101. if (max_lsn != NULL) {
  102. if ((ret = __log_backup(dbenv, logc, max_lsn, &lowlsn)) != 0)
  103. goto err;
  104. } else if (dbenv->tx_timestamp != 0) {
  105. if ((ret = __log_earliest(dbenv, logc, &low, &lowlsn)) != 0)
  106. goto err;
  107. if ((int32_t)dbenv->tx_timestamp < low) {
  108. (void)snprintf(t1, sizeof(t1),
  109.     "%s", ctime(&dbenv->tx_timestamp));
  110. if ((p = strchr(t1, 'n')) != NULL)
  111. *p = '';
  112. tlow = (time_t)low;
  113. (void)snprintf(t2, sizeof(t2), "%s", ctime(&tlow));
  114. if ((p = strchr(t2, 'n')) != NULL)
  115. *p = '';
  116. __db_err(dbenv,
  117.     "Invalid recovery timestamp %s; earliest time is %s",
  118.     t1, t2);
  119. ret = EINVAL;
  120. goto err;
  121. }
  122. }
  123. /*
  124.  * Recovery is done in three passes:
  125.  * Pass #0:
  126.  * We need to find the position from which we will open files.
  127.  * We need to open files beginning with the earlier of the
  128.  * most recent checkpoint LSN and a checkpoint LSN before the
  129.  * recovery timestamp, if specified.  We need to be before the
  130.  * most recent checkpoint LSN because we are going to collect
  131.  * information about which transactions were begun before we
  132.  * start rolling forward.  Those that were should never be undone
  133.  * because queue cannot use LSNs to determine what operations can
  134.  * safely be aborted and it cannot rollback operations in
  135.  * transactions for which there may be records not processed
  136.  * during recovery.  We need to consider earlier points in time
  137.  * in case we are recovering to a particular timestamp.
  138.  *
  139.  * Pass #1:
  140.  * Read forward through the log from the position found in pass 0
  141.  * opening and closing files, and recording transactions for which
  142.  * we've seen their first record (the transaction's prev_lsn is
  143.  * 0,0).  At the end of this pass, we know all transactions for
  144.  * which we've seen begins and we have the "current" set of files
  145.  * open.
  146.  *
  147.  * Pass #2:
  148.  * Read backward through the log undoing any uncompleted TXNs.
  149.  * There are four cases:
  150.  *     1.  If doing catastrophic recovery, we read to the
  151.  * beginning of the log
  152.  *     2.  If we are doing normal reovery, then we have to roll
  153.  * back to the most recent checkpoint LSN.
  154.  *     3.  If we are recovering to a point in time, then we have
  155.  * to roll back to the checkpoint whose ckp_lsn is earlier
  156.  * than the specified time.  __log_earliest will figure
  157.  * this out for us.
  158.  *     4. If we are recovering back to a particular LSN, then
  159.  * we have to roll back to the checkpoint whose ckp_lsn
  160.  * is earlier than the max_lsn.  __log_backup will figure
  161.  * that out for us.
  162.  * In case 2, "uncompleted TXNs" include all those who commited
  163.  * after the user's specified timestamp.
  164.  *
  165.  * Pass #3:
  166.  * Read forward through the log from the LSN found in pass #2,
  167.  * redoing any committed TXNs (which commited after any user-
  168.  * specified rollback point).  During this pass, checkpoint
  169.  * file information is ignored, and file openings and closings
  170.  * are redone.
  171.  *
  172.  * ckp_lsn   -- lsn of the last checkpoint or the first in the log.
  173.  * first_lsn -- the lsn where the forward passes begin.
  174.  * last_lsn  -- the last lsn in the log, used for feedback
  175.  * lowlsn    -- the lsn we are rolling back to, if we are recovering
  176.  * to a point in time.
  177.  * lsn       -- temporary use lsn.
  178.  * stop_lsn  -- the point at which forward roll should stop
  179.  */
  180. /*
  181.  * Find out the last lsn, so that we can estimate how far along we
  182.  * are in recovery.  This will help us determine how much log there
  183.  * is between the first LSN that we're going to be working with and
  184.  * the last one.  We assume that each of the three phases takes the
  185.  * same amount of time (a false assumption) and then use the %-age
  186.  * of the amount of log traversed to figure out how much of the
  187.  * pass we've accomplished.
  188.  *
  189.  * If we can't find any log records, we're kind of done.
  190.  */
  191. #ifdef UMRW
  192. ZERO_LSN(last_lsn);
  193. #endif
  194. memset(&data, 0, sizeof(data));
  195. if ((ret = logc->get(logc, &last_lsn, &data, DB_LAST)) != 0) {
  196. if (ret == DB_NOTFOUND)
  197. ret = 0;
  198. else
  199. __db_err(dbenv, "Last log record not found");
  200. goto err;
  201. }
  202. do {
  203. /* txnid is after rectype, which is a u_int32. */
  204. memcpy(&txnid,
  205.     (u_int8_t *)data.data + sizeof(u_int32_t), sizeof(txnid));
  206. if (txnid != 0)
  207. break;
  208. } while ((ret = logc->get(logc, &lsn, &data, DB_PREV)) == 0);
  209. /*
  210.  * There are no transactions, so there is nothing to do unless
  211.  * we're recovering to an LSN.  If we are, we need to proceed since
  212.  * we'll still need to do a vtruncate based on information we haven't
  213.  * yet collected.
  214.  */
  215. if (ret == DB_NOTFOUND) {
  216. ret = 0;
  217. if (max_lsn == NULL)
  218. goto done;
  219. }
  220. if (ret != 0)
  221. goto err;
  222. hi_txn = txnid;
  223. /*
  224.  * Pass #0
  225.  * Find the LSN from which we begin OPENFILES.
  226.  *
  227.  * If this is a catastrophic recovery, or if no checkpoint exists
  228.  * in the log, the LSN is the first LSN in the log.
  229.  *
  230.  * Otherwise, it is the minimum of (1) the LSN in the last checkpoint
  231.  * and (2) the LSN in the checkpoint before any specified recovery
  232.  * timestamp or max_lsn.
  233.  */
  234. /*
  235.  * Get the first LSN in the log; it's an initial default
  236.  * even if this is not a catastrophic recovery.
  237.  */
  238. if ((ret = logc->get(logc, &ckp_lsn, &data, DB_FIRST)) != 0) {
  239. if (ret == DB_NOTFOUND)
  240. ret = 0;
  241. else
  242. __db_err(dbenv, "First log record not found");
  243. goto err;
  244. }
  245. first_lsn = ckp_lsn;
  246. have_rec = 1;
  247. if (!LF_ISSET(DB_RECOVER_FATAL)) {
  248. if ((ret = __txn_getckp(dbenv, &ckp_lsn)) == 0 &&
  249.     (ret = logc->get(logc, &ckp_lsn, &data, DB_SET)) == 0) {
  250. /* We have a recent checkpoint.  This is LSN (1). */
  251. if ((ret = __txn_ckp_read(dbenv,
  252.     data.data, &ckp_args)) != 0) {
  253. __db_err(dbenv,
  254.     "Invalid checkpoint record at [%ld][%ld]",
  255.     (u_long)ckp_lsn.file,
  256.     (u_long)ckp_lsn.offset);
  257. goto err;
  258. }
  259. first_lsn = ckp_args->ckp_lsn;
  260. have_rec = 0;
  261. }
  262. /*
  263.  * If LSN (2) exists, use it if it's before LSN (1).
  264.  * (If LSN (1) doesn't exist, first_lsn is the
  265.  * beginning of the log, so will "win" this check.)
  266.  *
  267.  * XXX
  268.  * In the recovery-to-a-timestamp case, lowlsn is chosen by
  269.  * __log_earliest, and is the checkpoint LSN of the
  270.  * *earliest* checkpoint in the unreclaimed log.  I
  271.  * (krinsky) believe that we could optimize this by looking
  272.  * instead for the LSN of the *latest* checkpoint before
  273.  * the timestamp of interest, but I'm not sure that this
  274.  * is worth doing right now.  (We have to look for lowlsn
  275.  * and low anyway, to make sure the requested timestamp is
  276.  * somewhere in the logs we have, and all that's required
  277.  * is that we pick *some* checkpoint after the beginning of
  278.  * the logs and before the timestamp.
  279.  */
  280. if ((dbenv->tx_timestamp != 0 || max_lsn != NULL) &&
  281.     log_compare(&lowlsn, &first_lsn) < 0) {
  282. DB_ASSERT(have_rec == 0);
  283. first_lsn = lowlsn;
  284. }
  285. }
  286. /* Get the record at first_lsn if we don't have it already. */
  287. if (!have_rec &&
  288.     (ret = logc->get(logc, &first_lsn, &data, DB_SET)) != 0) {
  289. __db_err(dbenv, "Checkpoint LSN record [%ld][%ld] not found",
  290.     (u_long)first_lsn.file, (u_long)first_lsn.offset);
  291. goto err;
  292. }
  293. if (dbenv->db_feedback != NULL) {
  294. if (last_lsn.file == first_lsn.file)
  295. nfiles = (double)
  296.     (last_lsn.offset - first_lsn.offset) / log_size;
  297. else
  298. nfiles = (double)(last_lsn.file - first_lsn.file) +
  299.     (double)(log_size - first_lsn.offset +
  300.     last_lsn.offset) / log_size;
  301. /* We are going to divide by nfiles; make sure it isn't 0. */
  302. if (nfiles == 0)
  303. nfiles = (double)0.001;
  304. }
  305. /* Find a low txnid. */
  306. ret = 0;
  307. do {
  308. /* txnid is after rectype, which is a u_int32. */
  309. memcpy(&txnid,
  310.     (u_int8_t *)data.data + sizeof(u_int32_t), sizeof(txnid));
  311. if (txnid != 0)
  312. break;
  313. } while ((ret = logc->get(logc, &lsn, &data, DB_NEXT)) == 0);
  314. /*
  315.  * There are no transactions and we're not recovering to an LSN (see
  316.  * above), so there is nothing to do.
  317.  */
  318. if (ret == DB_NOTFOUND) {
  319. ret = 0;
  320. if (max_lsn == NULL)
  321. goto done;
  322. }
  323. /* Reset to the first lsn. */
  324. if (ret != 0 || (ret = logc->get(logc, &first_lsn, &data, DB_SET)) != 0)
  325. goto err;
  326. /* Initialize the transaction list. */
  327. if ((ret =
  328.     __db_txnlist_init(dbenv, txnid, hi_txn, max_lsn, &txninfo)) != 0)
  329. goto err;
  330. /*
  331.  * Pass #1
  332.  * Run forward through the log starting at the first relevant lsn.
  333.  */
  334. if ((ret = __env_openfiles(dbenv, logc,
  335.     txninfo, &data, &first_lsn, &last_lsn, nfiles, 1)) != 0)
  336. goto err;
  337. /*
  338.  * Pass #2.
  339.  *
  340.  * We used first_lsn to tell us how far back we need to recover,
  341.  * use it here.
  342.  */
  343. if (FLD_ISSET(dbenv->verbose, DB_VERB_RECOVERY))
  344. __db_err(dbenv, "Recovery starting from [%lu][%lu]",
  345.     (u_long)first_lsn.file, (u_long)first_lsn.offset);
  346. /*
  347.  * If we are doing client recovery, then we need to allocate
  348.  * the page-info lock table.
  349.  */
  350. if (max_lsn != NULL) {
  351. if ((ret = __rep_lockpgno_init(dbenv, &dtab, &dtabsize)) != 0)
  352. goto err;
  353. if ((ret = dbenv->lock_id(dbenv, &lockid)) != 0)
  354. goto err;
  355. }
  356. pass = "backward";
  357. for (ret = logc->get(logc, &lsn, &data, DB_LAST);
  358.     ret == 0 && log_compare(&lsn, &first_lsn) >= 0;
  359.     ret = logc->get(logc, &lsn, &data, DB_PREV)) {
  360. if (dbenv->db_feedback != NULL) {
  361. progress = 34 + (int)(33 * (__lsn_diff(&first_lsn,
  362.     &last_lsn, &lsn, log_size, 0) / nfiles));
  363. dbenv->db_feedback(dbenv, DB_RECOVER, progress);
  364. }
  365. if (max_lsn != NULL && (ret = __rep_lockpages(dbenv,
  366.     dtab, dtabsize, &lsn, NULL, NULL, lockid)) != 0)
  367. continue;
  368. ret = __db_dispatch(dbenv, dbenv->recover_dtab,
  369.     dbenv->recover_dtab_size, &data, &lsn,
  370.     DB_TXN_BACKWARD_ROLL, txninfo);
  371. if (ret != 0) {
  372. if (ret != DB_TXN_CKP)
  373. goto msgerr;
  374. else
  375. ret = 0;
  376. }
  377. }
  378. if (ret != 0 && ret != DB_NOTFOUND)
  379. goto err;
  380. /*
  381.  * Pass #3.  If we are recovering to a timestamp or to an LSN,
  382.  * we need to make sure that we don't roll-forward beyond that
  383.  * point because there may be non-transactional operations (e.g.,
  384.  * closes that would fail).  The last_lsn variable is used for
  385.  * feedback calculations, but use it to set an initial stopping
  386.  * point for the forward pass, and then reset appropriately to
  387.  * derive a real stop_lsn that tells how far the forward pass
  388.  * should go.
  389.  */
  390. pass = "forward";
  391. stop_lsn = last_lsn;
  392. if (max_lsn != NULL || dbenv->tx_timestamp != 0)
  393. stop_lsn = ((DB_TXNHEAD *)txninfo)->maxlsn;
  394. for (ret = logc->get(logc, &lsn, &data, DB_NEXT);
  395.     ret == 0; ret = logc->get(logc, &lsn, &data, DB_NEXT)) {
  396. /*
  397.  * If we are recovering to a timestamp or an LSN,
  398.  * we need to make sure that we don't try to roll
  399.  * forward beyond the soon-to-be end of log.
  400.  */
  401. if (log_compare(&lsn, &stop_lsn) > 0)
  402. break;
  403. if (dbenv->db_feedback != NULL) {
  404. progress = 67 + (int)(33 * (__lsn_diff(&first_lsn,
  405.     &last_lsn, &lsn, log_size, 1) / nfiles));
  406. dbenv->db_feedback(dbenv, DB_RECOVER, progress);
  407. }
  408. ret = __db_dispatch(dbenv, dbenv->recover_dtab,
  409.     dbenv->recover_dtab_size, &data, &lsn,
  410.     DB_TXN_FORWARD_ROLL, txninfo);
  411. if (ret != 0) {
  412. if (ret != DB_TXN_CKP)
  413. goto msgerr;
  414. else
  415. ret = 0;
  416. }
  417. }
  418. if (ret != 0 && ret != DB_NOTFOUND)
  419. goto err;
  420. /*
  421.  * Process any pages that were on the limbo list and move them to
  422.  * the free list.  Do this before checkpointing the database.
  423.  */
  424.  if ((ret = __db_do_the_limbo(dbenv, NULL, NULL, txninfo)) != 0)
  425. goto err;
  426. if (max_lsn == NULL)
  427. region->last_txnid = ((DB_TXNHEAD *)txninfo)->maxid;
  428. /* Take a checkpoint here to force any dirty data pages to disk. */
  429. if (dbenv->tx_timestamp != 0) {
  430. region->last_ckp = ((DB_TXNHEAD *)txninfo)->ckplsn;
  431. __log_vtruncate(dbenv, &((DB_TXNHEAD *)txninfo)->maxlsn,
  432.     &((DB_TXNHEAD *)txninfo)->ckplsn);
  433. }
  434. if ((ret = dbenv->txn_checkpoint(dbenv, 0, 0, DB_FORCE)) != 0)
  435. goto err;
  436. /* Close all the db files that are open. */
  437. if ((ret = __dbreg_close_files(dbenv)) != 0)
  438. goto err;
  439. if (max_lsn != NULL) {
  440. region->last_ckp = ((DB_TXNHEAD *)txninfo)->ckplsn;
  441. /* We are going to truncate, so we'd best close the cursor. */
  442. if (logc != NULL && (ret = logc->close(logc, 0)) != 0)
  443. goto err;
  444. __log_vtruncate(dbenv,
  445.     max_lsn, &((DB_TXNHEAD *)txninfo)->ckplsn);
  446. /*
  447.  * Now we need to open files that should be open in order for
  448.  * client processing to continue.  However, since we've
  449.  * truncated the log, we need to recompute from where the
  450.  * openfiles pass should begin.
  451.  */
  452. if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0)
  453. goto err;
  454. if ((ret = logc->get(logc, &first_lsn, &data, DB_FIRST)) != 0) {
  455. if (ret == DB_NOTFOUND)
  456. ret = 0;
  457. else
  458. __db_err(dbenv, "First log record not found");
  459. goto err;
  460. }
  461. if ((ret = __txn_getckp(dbenv, &first_lsn)) == 0 &&
  462.     (ret = logc->get(logc, &first_lsn, &data, DB_SET)) == 0) {
  463. /* We have a recent checkpoint.  This is LSN (1). */
  464. if ((ret = __txn_ckp_read(dbenv,
  465.     data.data, &ckp_args)) != 0) {
  466. __db_err(dbenv,
  467.     "Invalid checkpoint record at [%ld][%ld]",
  468.     (u_long)first_lsn.file,
  469.     (u_long)first_lsn.offset);
  470. goto err;
  471. }
  472. first_lsn = ckp_args->ckp_lsn;
  473. }
  474. if ((ret = logc->get(logc, &first_lsn, &data, DB_SET)) != 0)
  475. goto err;
  476. if ((ret = __env_openfiles(dbenv, logc,
  477.     txninfo, &data, &first_lsn, NULL, nfiles, 1)) != 0)
  478. goto err;
  479. } else if (region->stat.st_nrestores == 0)
  480. /*
  481.  * If there are no prepared transactions that need resolution,
  482.  * we need to reset the transaction ID space and log this fact.
  483.  */
  484. if ((ret = __txn_reset(dbenv)) != 0)
  485. goto err;
  486. if (FLD_ISSET(dbenv->verbose, DB_VERB_RECOVERY)) {
  487. (void)time(&now);
  488. __db_err(dbenv, "Recovery complete at %.24s", ctime(&now));
  489. __db_err(dbenv, "%s %lx %s [%lu][%lu]",
  490.     "Maximum transaction ID",
  491.     ((DB_TXNHEAD *)txninfo)->maxid,
  492.     "Recovery checkpoint",
  493.     (u_long)region->last_ckp.file,
  494.     (u_long)region->last_ckp.offset);
  495. }
  496. if (0) {
  497. msgerr: __db_err(dbenv,
  498.     "Recovery function for LSN %lu %lu failed on %s pass",
  499.     (u_long)lsn.file, (u_long)lsn.offset, pass);
  500. }
  501. done:
  502. err: if (lockid != DB_LOCK_INVALIDID) {
  503. if ((t_ret = __rep_unlockpages(dbenv, lockid)) != 0 && ret == 0)
  504. ret = t_ret;
  505. if ((t_ret =
  506.     dbenv->lock_id_free(dbenv, lockid)) != 0 && ret == 0)
  507. ret = t_ret;
  508. }
  509. if (logc != NULL && (t_ret = logc->close(logc, 0)) != 0 && ret == 0)
  510. ret = t_ret;
  511. if (txninfo != NULL)
  512. __db_txnlist_end(dbenv, txninfo);
  513. if (dtab != NULL)
  514. __os_free(dbenv, dtab);
  515. if (ckp_args != NULL)
  516. __os_free(dbenv, ckp_args);
  517. dbenv->tx_timestamp = 0;
  518. /* Restore the state of the thread flag, clear in-recovery flags. */
  519. if (is_thread)
  520. F_SET(dbenv, DB_ENV_THREAD);
  521. F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  522. F_CLR(region, TXN_IN_RECOVERY);
  523. return (ret);
  524. }
  525. /*
  526.  * Figure out how many logfiles we have processed.  If we are moving
  527.  * forward (is_forward != 0), then we're computing current - low.  If
  528.  * we are moving backward, we are computing high - current.  max is
  529.  * the number of bytes per logfile.
  530.  */
  531. static double
  532. __lsn_diff(low, high, current, max, is_forward)
  533. DB_LSN *low, *high, *current;
  534. u_int32_t max;
  535. int is_forward;
  536. {
  537. double nf;
  538. /*
  539.  * There are three cases in each direction.  If you are in the
  540.  * same file, then all you need worry about is the difference in
  541.  * offsets.  If you are in different files, then either your offsets
  542.  * put you either more or less than the integral difference in the
  543.  * number of files -- we need to handle both of these.
  544.  */
  545. if (is_forward) {
  546. if (current->file == low->file)
  547. nf = (double)(current->offset - low->offset) / max;
  548. else if (current->offset < low->offset)
  549. nf = (double)(current->file - low->file - 1) +
  550.     (double)(max - low->offset + current->offset) / max;
  551. else
  552. nf = (double)(current->file - low->file) +
  553.     (double)(current->offset - low->offset) / max;
  554. } else {
  555. if (current->file == high->file)
  556. nf = (double)(high->offset - current->offset) / max;
  557. else if (current->offset > high->offset)
  558. nf = (double)(high->file - current->file - 1) +
  559.     (double)
  560.     (max - current->offset + high->offset) / max;
  561. else
  562. nf = (double)(high->file - current->file) +
  563.     (double)(high->offset - current->offset) / max;
  564. }
  565. return (nf);
  566. }
  567. /*
  568.  * __log_backup --
  569.  *
  570.  * This is used to find the earliest log record to process when a client
  571.  * is trying to sync up with a master whose max LSN is less than this
  572.  * client's max lsn; we want to roll back everything after that
  573.  *
  574.  * Find the latest checkpoint whose ckp_lsn is less than the max lsn.
  575.  */
  576. static int
  577. __log_backup(dbenv, logc, max_lsn, start_lsn)
  578. DB_ENV *dbenv;
  579. DB_LOGC *logc;
  580. DB_LSN *max_lsn, *start_lsn;
  581. {
  582. DB_LSN lsn;
  583. DBT data;
  584. __txn_ckp_args *ckp_args;
  585. int ret;
  586. memset(&data, 0, sizeof(data));
  587. ckp_args = NULL;
  588. /*
  589.  * Follow checkpoints through the log until we find one with
  590.  * a ckp_lsn less than max_lsn.
  591.  */
  592. if ((ret = __txn_getckp(dbenv, &lsn)) != 0)
  593. goto err;
  594. while ((ret = logc->get(logc, &lsn, &data, DB_SET)) == 0) {
  595. if ((ret = __txn_ckp_read(dbenv, data.data, &ckp_args)) != 0)
  596. return (ret);
  597. if (log_compare(&ckp_args->ckp_lsn, max_lsn) <= 0) {
  598. *start_lsn = ckp_args->ckp_lsn;
  599. break;
  600. }
  601. lsn = ckp_args->prev_lsn;
  602. if (IS_ZERO_LSN(lsn))
  603. break;
  604. __os_free(dbenv, ckp_args);
  605. }
  606. if (ckp_args != NULL)
  607. __os_free(dbenv, ckp_args);
  608. err: if (IS_ZERO_LSN(*start_lsn) && (ret == 0 || ret == DB_NOTFOUND))
  609. ret = logc->get(logc, start_lsn, &data, DB_FIRST);
  610. return (ret);
  611. }
  612. /*
  613.  * __log_earliest --
  614.  *
  615.  * Return the earliest recovery point for the log files present.  The
  616.  * earliest recovery time is the time stamp of the first checkpoint record
  617.  * whose checkpoint LSN is greater than the first LSN we process.
  618.  */
  619. static int
  620. __log_earliest(dbenv, logc, lowtime, lowlsn)
  621. DB_ENV *dbenv;
  622. DB_LOGC *logc;
  623. int32_t *lowtime;
  624. DB_LSN *lowlsn;
  625. {
  626. DB_LSN first_lsn, lsn;
  627. DBT data;
  628. __txn_ckp_args *ckpargs;
  629. u_int32_t rectype;
  630. int cmp, ret;
  631. memset(&data, 0, sizeof(data));
  632. /*
  633.  * Read forward through the log looking for the first checkpoint
  634.  * record whose ckp_lsn is greater than first_lsn.
  635.  */
  636. for (ret = logc->get(logc, &first_lsn, &data, DB_FIRST);
  637.     ret == 0; ret = logc->get(logc, &lsn, &data, DB_NEXT)) {
  638. memcpy(&rectype, data.data, sizeof(rectype));
  639. if (rectype != DB___txn_ckp)
  640. continue;
  641. if ((ret = __txn_ckp_read(dbenv, data.data, &ckpargs)) == 0) {
  642. cmp = log_compare(&ckpargs->ckp_lsn, &first_lsn);
  643. *lowlsn = ckpargs->ckp_lsn;
  644. *lowtime = ckpargs->timestamp;
  645. __os_free(dbenv, ckpargs);
  646. if (cmp >= 0)
  647. break;
  648. }
  649. }
  650. return (ret);
  651. }
  652. /*
  653.  * __env_openfiles --
  654.  * Perform the pass of recovery that opens files.  This is used
  655.  * both during regular recovery and an initial call to txn_recover (since
  656.  * we need files open in order to abort prepared, but not yet committed
  657.  * transactions).
  658.  *
  659.  * See the comments in db_apprec for a detailed description of the
  660.  * various recovery passes.
  661.  *
  662.  * If we are not doing feedback processing (i.e., we are doing txn_recover
  663.  * processing and in_recovery is zero), then last_lsn can be NULL.
  664.  *
  665.  * PUBLIC: int __env_openfiles __P((DB_ENV *, DB_LOGC *,
  666.  * PUBLIC:     void *, DBT *, DB_LSN *, DB_LSN *, double, int));
  667.  */
  668. int
  669. __env_openfiles(dbenv, logc, txninfo,
  670.     data, open_lsn, last_lsn, nfiles, in_recovery)
  671. DB_ENV *dbenv;
  672. DB_LOGC *logc;
  673. void *txninfo;
  674. DBT *data;
  675. DB_LSN *open_lsn, *last_lsn;
  676. int in_recovery;
  677. double nfiles;
  678. {
  679. DB_LSN lsn;
  680. u_int32_t log_size;
  681. int progress, ret;
  682. /*
  683.  * XXX
  684.  * Get the log size.  No locking required because we're single-threaded
  685.  * during recovery.
  686.  */
  687. log_size =
  688.    ((LOG *)(((DB_LOG *)dbenv->lg_handle)->reginfo.primary))->log_size;
  689. lsn = *open_lsn;
  690. for (;;) {
  691. if (in_recovery && dbenv->db_feedback != NULL) {
  692. DB_ASSERT(last_lsn != NULL);
  693. progress = (int)(33 * (__lsn_diff(open_lsn,
  694.    last_lsn, &lsn, log_size, 1) / nfiles));
  695. dbenv->db_feedback(dbenv, DB_RECOVER, progress);
  696. }
  697. ret = __db_dispatch(dbenv,
  698.     dbenv->recover_dtab, dbenv->recover_dtab_size, data, &lsn,
  699.     in_recovery ? DB_TXN_OPENFILES : DB_TXN_POPENFILES,
  700.     txninfo);
  701. if (ret != 0 && ret != DB_TXN_CKP) {
  702. __db_err(dbenv,
  703.     "Recovery function for LSN %lu %lu failed",
  704.     (u_long)lsn.file, (u_long)lsn.offset);
  705. break;
  706. }
  707. if ((ret = logc->get(logc, &lsn, data, DB_NEXT)) != 0) {
  708. if (ret == DB_NOTFOUND)
  709. ret = 0;
  710. break;
  711. }
  712. }
  713. return (ret);
  714. }