2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 2001-2009 Oracle. All rights reserved.
12 #include "dbinc/txn.h"
13 #include "dbinc/db_page.h"
14 #include "dbinc/db_dispatch.h"
15 #include "dbinc/log.h"
16 #include "dbinc_auto/db_auto.h"
17 #include "dbinc_auto/crdel_auto.h"
18 #include "dbinc_auto/db_ext.h"
22 * Return the txn that corresponds to this global ID.
24 * PUBLIC: int __txn_map_gid __P((ENV *,
25 * PUBLIC: u_int8_t *, TXN_DETAIL **, roff_t *));
28 __txn_map_gid(env, gid, tdp, offp)
38 region = mgr->reginfo.primary;
41 * Search the internal active transaction table to find the
42 * matching xid. If this is a performance hit, then we
43 * can create a hash table, but I doubt it's worth it.
46 SH_TAILQ_FOREACH(*tdp, ®ion->active_txn, links, __txn_detail)
47 if (memcmp(gid, (*tdp)->gid, sizeof((*tdp)->gid)) == 0)
49 TXN_SYSTEM_UNLOCK(env);
54 *offp = R_OFFSET(&mgr->reginfo, *tdp);
60 * ENV->txn_recover pre/post processing.
62 * PUBLIC: int __txn_recover_pp __P((DB_ENV *,
63 * PUBLIC: DB_PREPLIST *, u_int32_t, u_int32_t *, u_int32_t));
66 __txn_recover_pp(dbenv, preplist, count, retp, flags)
68 DB_PREPLIST *preplist;
69 u_int32_t count, *retp;
79 env, env->tx_handle, "txn_recover", DB_INIT_TXN);
81 if (F_ISSET((DB_TXNREGION *)env->tx_handle->reginfo.primary,
83 __db_errx(env, "operation not permitted while in recovery");
87 if (flags != DB_FIRST && flags != DB_NEXT)
88 return (__db_ferr(env, "DB_ENV->txn_recover", 0));
92 (__txn_recover(env, preplist, count, retp, flags)), 0, ret);
101 * PUBLIC: int __txn_recover __P((ENV *,
102 * PUBLIC: DB_PREPLIST *, u_int32_t, u_int32_t *, u_int32_t));
105 __txn_recover(env, txns, count, retp, flags)
108 u_int32_t count, *retp;
115 DB_TXNREGION *region;
126 DB_ASSERT(env, txns != NULL);
128 * If we are starting a scan, then we traverse the active transaction
129 * list once making sure that all transactions are marked as not having
130 * been collected. Then on each pass, we mark the ones we collected
131 * so that if we cannot collect them all at once, we can finish up
132 * next time with a continue.
135 mgr = env->tx_handle;
136 region = mgr->reginfo.primary;
139 * During this pass we need to figure out if we are going to need
140 * to open files. We need to open files if we've never collected
141 * before (in which case, none of the COLLECTED bits will be set)
142 * and the ones that we are collecting are restored (if they aren't
143 * restored, then we never crashed; just the main server did).
145 TXN_SYSTEM_LOCK(env);
147 /* Now begin collecting active transactions. */
148 for (td = SH_TAILQ_FIRST(®ion->active_txn, __txn_detail);
149 td != NULL && *retp < count;
150 td = SH_TAILQ_NEXT(td, links, __txn_detail)) {
151 if (td->status != TXN_PREPARED ||
152 (flags != DB_FIRST && F_ISSET(td, TXN_DTL_COLLECTED)))
155 if (F_ISSET(td, TXN_DTL_RESTORED))
158 if ((ret = __os_calloc(env,
159 1, sizeof(DB_TXN), &prepp->txn)) != 0) {
160 TXN_SYSTEM_UNLOCK(env);
163 if ((ret = __txn_continue(env, prepp->txn, td)) != 0)
165 F_SET(prepp->txn, TXN_MALLOC);
166 if (F_ISSET(env->dbenv, DB_ENV_TXN_NOSYNC))
167 F_SET(prepp->txn, TXN_NOSYNC);
168 else if (F_ISSET(env->dbenv, DB_ENV_TXN_WRITE_NOSYNC))
169 F_SET(prepp->txn, TXN_WRITE_NOSYNC);
171 F_SET(prepp->txn, TXN_SYNC);
172 memcpy(prepp->gid, td->gid, sizeof(td->gid));
175 if (!IS_ZERO_LSN(td->begin_lsn) &&
176 LOG_COMPARE(&td->begin_lsn, &min) < 0)
180 F_SET(td, TXN_DTL_COLLECTED);
182 if (flags == DB_FIRST)
183 for (; td != NULL; td = SH_TAILQ_NEXT(td, links, __txn_detail))
184 F_CLR(td, TXN_DTL_COLLECTED);
185 TXN_SYSTEM_UNLOCK(env);
188 * Now link all the transactions into the transaction manager's list.
191 MUTEX_LOCK(env, mgr->mutex);
192 for (i = 0; i < *retp; i++)
193 TAILQ_INSERT_TAIL(&mgr->txn_chain, txns[i].txn, links);
194 MUTEX_UNLOCK(env, mgr->mutex);
197 * If we are restoring, update our count of outstanding
201 REP_SYSTEM_LOCK(env);
202 env->rep_handle->region->op_cnt += (u_long)*retp;
203 REP_SYSTEM_UNLOCK(env);
208 * If recovery already opened the files for us, don't
211 if (restored != 0 && flags == DB_FIRST &&
212 !F_ISSET(env->lg_handle, DBLOG_OPENFILES)) {
213 ENV_GET_THREAD_INFO(env, ip);
214 ret = __txn_openfiles(env, ip, &min, 0);
218 err: TXN_SYSTEM_UNLOCK(env);
225 * Call env_openfiles.
227 * PUBLIC: int __txn_openfiles __P((ENV *, DB_THREAD_INFO *, DB_LSN *, int));
230 __txn_openfiles(env, ip, min, force)
240 __txn_ckp_args *ckp_args;
244 * Figure out the last checkpoint before the smallest
245 * start_lsn in the region.
248 if ((ret = __log_cursor(env, &logc)) != 0)
251 memset(&data, 0, sizeof(data));
252 if ((ret = __txn_getckp(env, &open_lsn)) == 0)
253 while (!IS_ZERO_LSN(open_lsn) && (ret =
254 __logc_get(logc, &open_lsn, &data, DB_SET)) == 0 &&
256 (min != NULL && LOG_COMPARE(min, &open_lsn) < 0))) {
257 /* Format the log record. */
258 if ((ret = __txn_ckp_read(
259 env, data.data, &ckp_args)) != 0) {
261 "Invalid checkpoint record at [%lu][%lu]",
262 (u_long)open_lsn.file,
263 (u_long)open_lsn.offset);
267 * If force is set, then we're forcing ourselves
268 * to go back far enough to open files.
269 * Use ckp_lsn and then break out of the loop.
271 open_lsn = force ? ckp_args->ckp_lsn :
273 __os_free(env, ckp_args);
275 if ((ret = __logc_get(logc, &open_lsn,
276 &data, DB_SET)) != 0)
283 * There are several ways by which we may have gotten here.
284 * - We got a DB_NOTFOUND -- we need to read the first
286 * - We found a checkpoint before min. We're done.
287 * - We found a checkpoint after min who's last_ckp is 0. We
288 * need to start at the beginning of the log.
289 * - We are forcing an openfiles and we have our ckp_lsn.
291 if ((ret == DB_NOTFOUND || IS_ZERO_LSN(open_lsn)) && (ret =
292 __logc_get(logc, &open_lsn, &data, DB_FIRST)) != 0) {
293 __db_errx(env, "No log records");
297 if ((ret = __db_txnlist_init(env, ip, 0, 0, NULL, &txninfo)) != 0)
299 ret = __env_openfiles(
300 env, logc, txninfo, &data, &open_lsn, NULL, (double)0, 0);
302 __db_txnlist_end(env, txninfo);
305 if (logc != NULL && (t_ret = __logc_close(logc)) != 0 && ret == 0)