txn_rec.c 13.4 KB
Newer Older
unknown's avatar
unknown committed
1 2 3
/*-
 * See the file LICENSE for redistribution information.
 *
unknown's avatar
unknown committed
4
 * Copyright (c) 1996-2005
unknown's avatar
unknown committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *	Sleepycat Software.  All rights reserved.
 */
/*
 * Copyright (c) 1996
 *	The President and Fellows of Harvard University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
unknown's avatar
unknown committed
34
 *
unknown's avatar
unknown committed
35
 * $Id: txn_rec.c,v 12.4 2005/10/19 15:10:45 bostic Exp $
unknown's avatar
unknown committed
36 37 38 39 40 41 42
 */

#include "db_config.h"

#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>

unknown's avatar
unknown committed
43
#include <string.h>
unknown's avatar
unknown committed
44 45 46
#endif

#include "db_int.h"
unknown's avatar
unknown committed
47 48 49
#include "dbinc/db_page.h"
#include "dbinc/txn.h"
#include "dbinc/db_am.h"
unknown's avatar
unknown committed
50 51 52 53 54 55 56

/*
 * PUBLIC: int __txn_regop_recover
 * PUBLIC:    __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
 *
 * These records are only ever written for commits.  Normally, we redo any
 * committed transaction, however if we are doing recovery to a timestamp, then
unknown's avatar
unknown committed
57
 * we may treat transactions that committed after the timestamp as aborted.
unknown's avatar
unknown committed
58 59 60 61 62 63 64 65 66
 */
int
__txn_regop_recover(dbenv, dbtp, lsnp, op, info)
	DB_ENV *dbenv;
	DBT *dbtp;
	DB_LSN *lsnp;
	db_recops op;
	void *info;
{
unknown's avatar
unknown committed
67
	DB_TXNHEAD *headp;
unknown's avatar
unknown committed
68 69
	__txn_regop_args *argp;
	int ret;
unknown's avatar
unknown committed
70
	u_int32_t status;
unknown's avatar
unknown committed
71 72 73 74 75 76 77 78

#ifdef DEBUG_RECOVER
	(void)__txn_regop_print(dbenv, dbtp, lsnp, op, info);
#endif

	if ((ret = __txn_regop_read(dbenv, dbtp->data, &argp)) != 0)
		return (ret);

unknown's avatar
unknown committed
79 80 81 82 83 84
	headp = info;
	/*
	 * We are only ever called during FORWARD_ROLL or BACKWARD_ROLL.
	 * We check for the former explicitly and the last two clauses
	 * apply to the BACKWARD_ROLL case.
	 */
unknown's avatar
unknown committed
85

unknown's avatar
unknown committed
86
	if (op == DB_TXN_FORWARD_ROLL) {
unknown's avatar
unknown committed
87
		/*
unknown's avatar
unknown committed
88 89 90
		 * If this was a 2-phase-commit transaction, then it
		 * might already have been removed from the list, and
		 * that's OK.  Ignore the return code from remove.
unknown's avatar
unknown committed
91
		 */
unknown's avatar
unknown committed
92 93 94 95
		if ((ret = __db_txnlist_remove(dbenv,
		    info, argp->txnid->txnid)) != DB_NOTFOUND && ret != 0)
			goto err;
	} else if ((dbenv->tx_timestamp != 0 &&
unknown's avatar
unknown committed
96 97 98
	    argp->timestamp > (int32_t)dbenv->tx_timestamp) ||
	    (!IS_ZERO_LSN(headp->trunc_lsn) &&
	    log_compare(&headp->trunc_lsn, lsnp) < 0)) {
unknown's avatar
unknown committed
99
		/*
unknown's avatar
unknown committed
100 101
		 * We failed either the timestamp check or the trunc_lsn check,
		 * so we treat this as an abort even if it was a commit record.
unknown's avatar
unknown committed
102
		 */
unknown's avatar
unknown committed
103 104 105 106
		if ((ret = __db_txnlist_update(dbenv, info,
		    argp->txnid->txnid, TXN_ABORT, NULL, &status, 1)) != 0)
			goto err;
		else if (status != TXN_IGNORE && status != TXN_OK)
unknown's avatar
unknown committed
107 108 109
			goto err;
	} else {
		/* This is a normal commit; mark it appropriately. */
unknown's avatar
unknown committed
110 111 112 113
		if ((ret = __db_txnlist_update(dbenv,
		    info, argp->txnid->txnid, argp->opcode, lsnp,
		    &status, 0)) == DB_NOTFOUND) {
			if ((ret = __db_txnlist_add(dbenv,
unknown's avatar
unknown committed
114 115
			    info, argp->txnid->txnid,
			    argp->opcode == TXN_ABORT ?
unknown's avatar
unknown committed
116 117 118 119
			    TXN_IGNORE : argp->opcode, lsnp)) != 0)
				goto err;
		} else if (ret != 0 ||
		    (status != TXN_IGNORE && status != TXN_OK))
unknown's avatar
unknown committed
120 121
			goto err;
	}
unknown's avatar
unknown committed
122 123 124

	if (ret == 0)
		*lsnp = argp->prev_lsn;
unknown's avatar
unknown committed
125 126 127 128

	if (0) {
err:		__db_err(dbenv,
		    "txnid %lx commit record found, already on commit list",
unknown's avatar
unknown committed
129
		    (u_long)argp->txnid->txnid);
unknown's avatar
unknown committed
130 131 132
		ret = EINVAL;
	}
	__os_free(dbenv, argp);
unknown's avatar
unknown committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

	return (ret);
}

/*
 * PUBLIC: int __txn_xa_regop_recover
 * PUBLIC:    __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
 *
 * These records are only ever written for prepares.
 */
int
__txn_xa_regop_recover(dbenv, dbtp, lsnp, op, info)
	DB_ENV *dbenv;
	DBT *dbtp;
	DB_LSN *lsnp;
	db_recops op;
	void *info;
{
	__txn_xa_regop_args *argp;
	int ret;
unknown's avatar
unknown committed
153
	u_int32_t status;
unknown's avatar
unknown committed
154 155 156 157 158 159 160 161

#ifdef DEBUG_RECOVER
	(void)__txn_xa_regop_print(dbenv, dbtp, lsnp, op, info);
#endif

	if ((ret = __txn_xa_regop_read(dbenv, dbtp->data, &argp)) != 0)
		return (ret);

unknown's avatar
unknown committed
162
	if (argp->opcode != TXN_PREPARE && argp->opcode != TXN_ABORT) {
unknown's avatar
unknown committed
163 164 165 166
		ret = EINVAL;
		goto err;
	}

unknown's avatar
unknown committed
167 168 169 170 171 172 173 174
	/*
	 * The return value here is either a DB_NOTFOUND or it is
	 * the transaction status from the list.  It is not a normal
	 * error return, so we must make sure that in each of the
	 * cases below, we overwrite the ret value so we return
	 * appropriately.
	 */
	ret = __db_txnlist_find(dbenv, info, argp->txnid->txnid, &status);
unknown's avatar
unknown committed
175 176 177

	/*
	 * If we are rolling forward, then an aborted prepare
unknown's avatar
unknown committed
178 179
	 * indicates that this may the last record we'll see for
	 * this transaction ID, so we should remove it from the
unknown's avatar
unknown committed
180 181 182
	 * list.
	 */

unknown's avatar
unknown committed
183 184
	if (op == DB_TXN_FORWARD_ROLL) {
		if ((ret = __db_txnlist_remove(dbenv,
unknown's avatar
unknown committed
185
		    info, argp->txnid->txnid)) != 0)
unknown's avatar
unknown committed
186
			goto txn_err;
unknown's avatar
unknown committed
187
	} else if (op == DB_TXN_BACKWARD_ROLL && status == TXN_PREPARE) {
unknown's avatar
unknown committed
188
		/*
unknown's avatar
unknown committed
189
		 * On the backward pass, we have four possibilities:
unknown's avatar
unknown committed
190
		 * 1. The transaction is already committed, no-op.
unknown's avatar
unknown committed
191
		 * 2. The transaction is already aborted, no-op.
unknown's avatar
unknown committed
192 193
		 * 3. The prepare failed and was aborted, mark as abort.
		 * 4. The transaction is neither committed nor aborted.
unknown's avatar
unknown committed
194 195
		 *	 Treat this like a commit and roll forward so that
		 *	 the transaction can be resurrected in the region.
unknown's avatar
unknown committed
196 197 198 199 200 201 202 203 204 205 206 207
		 * We handle cases 3 and 4 here; cases 1 and 2
		 * are the final clause below.
		 */
		if (argp->opcode == TXN_ABORT) {
			if ((ret = __db_txnlist_update(dbenv,
			     info, argp->txnid->txnid,
			     TXN_ABORT, NULL, &status, 0)) != 0 &&
			     status != TXN_PREPARE)
				goto txn_err;
			ret = 0;
		}
		/*
unknown's avatar
unknown committed
208 209 210 211 212
		 * This is prepared, but not yet committed transaction.  We
		 * need to add it to the transaction list, so that it gets
		 * rolled forward. We also have to add it to the region's
		 * internal state so it can be properly aborted or committed
		 * after recovery (see txn_recover).
unknown's avatar
unknown committed
213
		 */
unknown's avatar
unknown committed
214 215
		else if ((ret = __db_txnlist_remove(dbenv,
		    info, argp->txnid->txnid)) != 0) {
unknown's avatar
unknown committed
216 217 218 219 220 221
txn_err:		__db_err(dbenv,
			    "Transaction not in list %x", argp->txnid->txnid);
			ret = DB_NOTFOUND;
		} else if ((ret = __db_txnlist_add(dbenv,
		   info, argp->txnid->txnid, TXN_COMMIT, lsnp)) == 0)
			ret = __txn_restore_txn(dbenv, lsnp, argp);
unknown's avatar
unknown committed
222 223 224 225 226 227
	} else
		ret = 0;

	if (ret == 0)
		*lsnp = argp->prev_lsn;

unknown's avatar
unknown committed
228
err:	__os_free(dbenv, argp);
unknown's avatar
unknown committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

	return (ret);
}

/*
 * PUBLIC: int __txn_ckp_recover
 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
 */
int
__txn_ckp_recover(dbenv, dbtp, lsnp, op, info)
	DB_ENV *dbenv;
	DBT *dbtp;
	DB_LSN *lsnp;
	db_recops op;
	void *info;
{
unknown's avatar
unknown committed
245 246
	DB_REP *db_rep;
	REP *rep;
unknown's avatar
unknown committed
247 248 249 250 251 252 253 254 255
	__txn_ckp_args *argp;
	int ret;

#ifdef DEBUG_RECOVER
	__txn_ckp_print(dbenv, dbtp, lsnp, op, info);
#endif
	if ((ret = __txn_ckp_read(dbenv, dbtp->data, &argp)) != 0)
		return (ret);

unknown's avatar
unknown committed
256 257
	if (op == DB_TXN_BACKWARD_ROLL)
		__db_txnlist_ckp(dbenv, info, lsnp);
unknown's avatar
unknown committed
258

unknown's avatar
unknown committed
259 260 261 262 263 264 265 266 267 268
	if (op == DB_TXN_FORWARD_ROLL) {
		/* Record the max generation number that we've seen. */
		if (REP_ON(dbenv)) {
			db_rep = dbenv->rep_handle;
			rep = db_rep->region;
			if (argp->rep_gen > rep->recover_gen)
				rep->recover_gen = argp->rep_gen;
		}
	}

unknown's avatar
unknown committed
269
	*lsnp = argp->last_ckp;
unknown's avatar
unknown committed
270
	__os_free(dbenv, argp);
unknown's avatar
unknown committed
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	return (DB_TXN_CKP);
}

/*
 * __txn_child_recover
 *	Recover a commit record for a child transaction.
 *
 * PUBLIC: int __txn_child_recover
 * PUBLIC:    __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
 */
int
__txn_child_recover(dbenv, dbtp, lsnp, op, info)
	DB_ENV *dbenv;
	DBT *dbtp;
	DB_LSN *lsnp;
	db_recops op;
	void *info;
{
	__txn_child_args *argp;
unknown's avatar
unknown committed
290 291
	int ret, t_ret;
	u_int32_t c_stat, p_stat, tmpstat;
unknown's avatar
unknown committed
292 293 294 295 296 297 298 299 300

#ifdef DEBUG_RECOVER
	(void)__txn_child_print(dbenv, dbtp, lsnp, op, info);
#endif
	if ((ret = __txn_child_read(dbenv, dbtp->data, &argp)) != 0)
		return (ret);

	/*
	 * This is a record in a PARENT's log trail indicating that a
unknown's avatar
unknown committed
301 302
	 * child committed.  If we are aborting, return the childs last
	 * record's LSN.  If we are in recovery, then if the
unknown's avatar
unknown committed
303
	 * parent is committing, we set ourselves up to commit, else
unknown's avatar
unknown committed
304 305 306
	 * we do nothing.
	 */
	if (op == DB_TXN_ABORT) {
unknown's avatar
unknown committed
307 308 309
		*lsnp = argp->c_lsn;
		ret = __db_txnlist_lsnadd(dbenv, info, &argp->prev_lsn);
		goto out;
unknown's avatar
unknown committed
310
	} else if (op == DB_TXN_BACKWARD_ROLL) {
unknown's avatar
unknown committed
311
		/* Child might exist -- look for it. */
unknown's avatar
unknown committed
312 313 314 315 316 317 318 319 320 321 322 323 324
		ret = __db_txnlist_find(dbenv, info, argp->child, &c_stat);
		t_ret =
		    __db_txnlist_find(dbenv, info, argp->txnid->txnid, &p_stat);
		if (ret != 0 && ret != DB_NOTFOUND)
			goto out;
		if (t_ret != 0 && t_ret != DB_NOTFOUND) {
			ret = t_ret;
			goto out;
		}
		/*
		 * If the parent is in state COMMIT or IGNORE, then we apply
		 * that to the child, else we need to abort the child.
		 */
unknown's avatar
unknown committed
325

unknown's avatar
unknown committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
		if (ret == DB_NOTFOUND  ||
		    c_stat == TXN_OK || c_stat == TXN_COMMIT) {
			if (t_ret == DB_NOTFOUND ||
			     (p_stat != TXN_COMMIT  && p_stat != TXN_IGNORE))
				c_stat = TXN_ABORT;
			else
				c_stat = p_stat;

			if (ret == DB_NOTFOUND)
				ret = __db_txnlist_add(dbenv,
				     info, argp->child, c_stat, NULL);
			else
				ret = __db_txnlist_update(dbenv, info,
				     argp->child, c_stat, NULL, &tmpstat, 0);
		} else if (c_stat == TXN_EXPECTED) {
unknown's avatar
unknown committed
341 342 343 344 345
			/*
			 * The open after this create succeeded.  If the
			 * parent succeeded, we don't want to redo; if the
			 * parent aborted, we do want to undo.
			 */
unknown's avatar
unknown committed
346 347 348 349 350 351 352 353
			switch (p_stat) {
			case TXN_COMMIT:
			case TXN_IGNORE:
				c_stat = TXN_IGNORE;
				break;
			default:
				c_stat = TXN_ABORT;
			}
unknown's avatar
unknown committed
354
			ret = __db_txnlist_update(dbenv,
unknown's avatar
unknown committed
355
			    info, argp->child, c_stat, NULL, &tmpstat, 0);
unknown's avatar
unknown committed
356 357 358 359 360 361 362 363 364 365
		} else if (c_stat == TXN_UNEXPECTED) {
			/*
			 * The open after this create failed.  If the parent
			 * is rolling forward, we need to roll forward.  If
			 * the parent failed, then we do not want to abort
			 * (because the file may not be the one in which we
			 * are interested).
			 */
			ret = __db_txnlist_update(dbenv, info, argp->child,
			    p_stat == TXN_COMMIT ? TXN_COMMIT : TXN_IGNORE,
unknown's avatar
unknown committed
366
			    NULL, &tmpstat, 0);
unknown's avatar
unknown committed
367
		}
unknown's avatar
unknown committed
368 369 370 371 372 373 374 375 376 377 378
	} else if (op == DB_TXN_OPENFILES) {
		/*
		 * If we have a partial subtransaction, then the whole
		 * transaction should be ignored.
		 */
		if ((ret = __db_txnlist_find(dbenv,
		    info, argp->child, &c_stat)) == DB_NOTFOUND)
			ret = __db_txnlist_update(dbenv, info,
			     argp->txnid->txnid, TXN_IGNORE,
			     NULL, &p_stat, 1);
	} else if (DB_REDO(op)) {
unknown's avatar
unknown committed
379 380
		/* Forward Roll */
		if ((ret =
unknown's avatar
unknown committed
381
		    __db_txnlist_remove(dbenv, info, argp->child)) != 0)
unknown's avatar
unknown committed
382
			__db_err(dbenv,
unknown's avatar
unknown committed
383
			    "Transaction not in list %x", argp->child);
unknown's avatar
unknown committed
384
	}
unknown's avatar
unknown committed
385 386 387 388

	if (ret == 0)
		*lsnp = argp->prev_lsn;

unknown's avatar
unknown committed
389
out:	__os_free(dbenv, argp);
unknown's avatar
unknown committed
390 391 392 393 394 395 396 397

	return (ret);
}

/*
 * __txn_restore_txn --
 *	Using only during XA recovery.  If we find any transactions that are
 * prepared, but not yet committed, then we need to restore the transaction's
unknown's avatar
unknown committed
398 399
 * state into the shared region, because the TM is going to issue an abort
 * or commit and we need to respond correctly.
unknown's avatar
unknown committed
400 401
 *
 * lsnp is the LSN of the returned LSN
unknown's avatar
unknown committed
402
 * argp is the prepare record (in an appropriate structure)
unknown's avatar
unknown committed
403 404 405
 *
 * PUBLIC: int __txn_restore_txn __P((DB_ENV *,
 * PUBLIC:     DB_LSN *, __txn_xa_regop_args *));
unknown's avatar
unknown committed
406
 */
unknown's avatar
unknown committed
407
int
unknown's avatar
unknown committed
408 409 410 411 412 413 414 415 416 417 418
__txn_restore_txn(dbenv, lsnp, argp)
	DB_ENV *dbenv;
	DB_LSN *lsnp;
	__txn_xa_regop_args *argp;
{
	DB_TXNMGR *mgr;
	TXN_DETAIL *td;
	DB_TXNREGION *region;
	int ret;

	if (argp->xid.size == 0)
unknown's avatar
unknown committed
419
		return (0);
unknown's avatar
unknown committed
420 421 422

	mgr = dbenv->tx_handle;
	region = mgr->reginfo.primary;
unknown's avatar
unknown committed
423
	TXN_SYSTEM_LOCK(dbenv);
unknown's avatar
unknown committed
424 425 426

	/* Allocate a new transaction detail structure. */
	if ((ret =
unknown's avatar
unknown committed
427
	    __db_shalloc(&mgr->reginfo, sizeof(TXN_DETAIL), 0, &td)) != 0) {
unknown's avatar
unknown committed
428
		TXN_SYSTEM_UNLOCK(dbenv);
unknown's avatar
unknown committed
429
		return (ret);
unknown's avatar
unknown committed
430
	}
unknown's avatar
unknown committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444

	/* Place transaction on active transaction list. */
	SH_TAILQ_INSERT_HEAD(&region->active_txn, td, links, __txn_detail);

	td->txnid = argp->txnid->txnid;
	td->begin_lsn = argp->begin_lsn;
	td->last_lsn = *lsnp;
	td->parent = 0;
	td->status = TXN_PREPARED;
	td->xa_status = TXN_XA_PREPARED;
	memcpy(td->xid, argp->xid.data, argp->xid.size);
	td->bqual = argp->bqual;
	td->gtrid = argp->gtrid;
	td->format = argp->formatID;
unknown's avatar
unknown committed
445
	td->flags = 0;
unknown's avatar
unknown committed
446
	F_SET(td, TXN_DTL_RESTORED);
unknown's avatar
unknown committed
447

unknown's avatar
unknown committed
448 449 450 451
	region->stat.st_nrestores++;
	region->stat.st_nactive++;
	if (region->stat.st_nactive > region->stat.st_maxnactive)
		region->stat.st_maxnactive = region->stat.st_nactive;
unknown's avatar
unknown committed
452
	TXN_SYSTEM_UNLOCK(dbenv);
unknown's avatar
unknown committed
453 454
	return (0);
}
unknown's avatar
unknown committed
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489

/*
 * __txn_recycle_recover --
 *	Recovery function for recycle.
 *
 * PUBLIC: int __txn_recycle_recover
 * PUBLIC:   __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
 */
int
__txn_recycle_recover(dbenv, dbtp, lsnp, op, info)
	DB_ENV *dbenv;
	DBT *dbtp;
	DB_LSN *lsnp;
	db_recops op;
	void *info;
{
	__txn_recycle_args *argp;
	int ret;

#ifdef DEBUG_RECOVER
	(void)__txn_child_print(dbenv, dbtp, lsnp, op, info);
#endif
	if ((ret = __txn_recycle_read(dbenv, dbtp->data, &argp)) != 0)
		return (ret);

	COMPQUIET(lsnp, NULL);

	if ((ret = __db_txnlist_gen(dbenv, info,
	    DB_UNDO(op) ? -1 : 1, argp->min, argp->max)) != 0)
		return (ret);

	__os_free(dbenv, argp);

	return (0);
}