Update.
[glibc.git] / db2 / common / db_apprec.c
blob7f0cb3a212bb551aa3403bd78277fd1286275dcc
1 /*-
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
6 */
8 #include "config.h"
10 #ifndef lint
11 static const char copyright[] =
12 "@(#) Copyright (c) 1996, 1997, 1998\n\
13 Sleepycat Software Inc. All rights reserved.\n";
14 static const char sccsid[] = "@(#)db_apprec.c 10.30 (Sleepycat) 5/3/98";
15 #endif
17 #ifndef NO_SYSTEM_INCLUDES
18 #include <sys/types.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <time.h>
23 #endif
25 #include "db_int.h"
26 #include "shqueue.h"
27 #include "db_page.h"
28 #include "db_dispatch.h"
29 #include "db_am.h"
30 #include "log.h"
31 #include "txn.h"
32 #include "common_ext.h"
35 * __db_apprec --
36 * Perform recovery.
38 * PUBLIC: int __db_apprec __P((DB_ENV *, u_int32_t));
40 int
41 __db_apprec(dbenv, flags)
42 DB_ENV *dbenv;
43 u_int32_t flags;
45 DBT data;
46 DB_LOG *lp;
47 DB_LSN ckp_lsn, first_lsn, lsn;
48 time_t now;
49 u_int32_t is_thread;
50 int ret;
51 void *txninfo;
53 lp = dbenv->lg_info;
55 /* Initialize the transaction list. */
56 if ((ret = __db_txnlist_init(&txninfo)) != 0)
57 return (ret);
60 * Save the state of the thread flag -- we don't need it on at the
61 * moment because we're single-threaded until recovery is complete.
63 is_thread = F_ISSET(lp, DB_AM_THREAD);
64 F_CLR(lp, DB_AM_THREAD);
67 * Recovery is done in three passes:
68 * Pass #1:
69 * Read forward through the log from the last checkpoint to the
70 * end of the log, opening and closing files so that at the end
71 * of the log we have the "current" set of files open.
72 * Pass #2:
73 * Read backward through the log undoing any uncompleted TXNs.
74 * If doing catastrophic recovery, we read to the beginning of
75 * the log, otherwise, to the most recent checkpoint that occurs
76 * before the most recent checkpoint LSN, which is returned by
77 * __log_findckp(). During this pass, checkpoint file information
78 * is ignored, and file openings and closings are undone.
79 * Pass #3:
80 * Read forward through the log from the LSN found in pass #2,
81 * redoing any committed TXNs. During this pass, checkpoint
82 * file information is ignored, and file openings and closings
83 * are redone.
87 * Find the last checkpoint in the log. This is the point from which
88 * we want to begin pass #1 (the TXN_OPENFILES pass).
90 memset(&data, 0, sizeof(data));
91 if ((ret = log_get(lp, &ckp_lsn, &data, DB_CHECKPOINT)) != 0) {
93 * If we don't find a checkpoint, start from the beginning.
94 * If that fails, we're done. Note, we do not require that
95 * there be log records if we're performing recovery.
97 if ((ret = log_get(lp, &ckp_lsn, &data, DB_FIRST)) != 0) {
98 if (ret == DB_NOTFOUND)
99 ret = 0;
100 else
101 __db_err(dbenv, "First log record not found");
102 goto out;
107 * Now, ckp_lsn is either the lsn of the last checkpoint or the lsn
108 * of the first record in the log. Begin the TXN_OPENFILES pass from
109 * that lsn, and proceed to the end of the log.
111 lsn = ckp_lsn;
112 for (;;) {
113 ret = __db_dispatch(lp, &data, &lsn, TXN_OPENFILES, txninfo);
114 if (ret != 0 && ret != DB_TXN_CKP)
115 goto msgerr;
116 if ((ret = log_get(lp, &lsn, &data, DB_NEXT)) != 0) {
117 if (ret == DB_NOTFOUND)
118 break;
119 goto out;
124 * Pass #2.
126 * Before we can begin pass #2, backward roll phase, we determine how
127 * far back in the log to recover. If we are doing catastrophic
128 * recovery, then we go as far back as we have files. If we are
129 * doing normal recovery, we go as back to the most recent checkpoint
130 * that occurs before the most recent checkpoint LSN.
132 if (LF_ISSET(DB_RECOVER_FATAL)) {
133 ZERO_LSN(first_lsn);
134 } else
135 if ((ret = __log_findckp(lp, &first_lsn)) == DB_NOTFOUND) {
137 * We don't require that log files exist if recovery
138 * was specified.
140 ret = 0;
141 goto out;
144 if (dbenv->db_verbose)
145 __db_err(lp->dbenv, "Recovery starting from [%lu][%lu]",
146 (u_long)first_lsn.file, (u_long)first_lsn.offset);
148 for (ret = log_get(lp, &lsn, &data, DB_LAST);
149 ret == 0 && log_compare(&lsn, &first_lsn) > 0;
150 ret = log_get(lp, &lsn, &data, DB_PREV)) {
151 ret = __db_dispatch(lp,
152 &data, &lsn, TXN_BACKWARD_ROLL, txninfo);
153 if (ret != 0) {
154 if (ret != DB_TXN_CKP)
155 goto msgerr;
156 else
157 ret = 0;
160 if (ret != 0 && ret != DB_NOTFOUND)
161 goto out;
164 * Pass #3.
166 for (ret = log_get(lp, &lsn, &data, DB_NEXT);
167 ret == 0; ret = log_get(lp, &lsn, &data, DB_NEXT)) {
168 ret = __db_dispatch(lp, &data, &lsn, TXN_FORWARD_ROLL, txninfo);
169 if (ret != 0) {
170 if (ret != DB_TXN_CKP)
171 goto msgerr;
172 else
173 ret = 0;
176 if (ret != DB_NOTFOUND)
177 goto out;
179 /* Now close all the db files that are open. */
180 __log_close_files(lp);
183 * Now set the last checkpoint lsn and the current time,
184 * take a checkpoint, and reset the txnid.
186 (void)time(&now);
187 dbenv->tx_info->region->last_ckp = ckp_lsn;
188 dbenv->tx_info->region->time_ckp = (u_int32_t)now;
189 if ((ret = txn_checkpoint(dbenv->tx_info, 0, 0)) != 0)
190 goto out;
191 dbenv->tx_info->region->last_txnid = TXN_MINIMUM;
193 if (dbenv->db_verbose) {
194 __db_err(lp->dbenv, "Recovery complete at %.24s", ctime(&now));
195 __db_err(lp->dbenv, "%s %lx %s [%lu][%lu]",
196 "Maximum transaction id",
197 ((DB_TXNHEAD *)txninfo)->maxid,
198 "Recovery checkpoint",
199 (u_long)dbenv->tx_info->region->last_ckp.file,
200 (u_long)dbenv->tx_info->region->last_ckp.offset);
203 if (0) {
204 msgerr: __db_err(dbenv, "Recovery function for LSN %lu %lu failed",
205 (u_long)lsn.file, (u_long)lsn.offset);
208 out: F_SET(lp, is_thread);
209 __db_txnlist_end(txninfo);
211 return (ret);