Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

txn_rec.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  */
00007 /*
00008  * Copyright (c) 1996
00009  *      The President and Fellows of Harvard University.  All rights reserved.
00010  *
00011  * Redistribution and use in source and binary forms, with or without
00012  * modification, are permitted provided that the following conditions
00013  * are met:
00014  * 1. Redistributions of source code must retain the above copyright
00015  *    notice, this list of conditions and the following disclaimer.
00016  * 2. Redistributions in binary form must reproduce the above copyright
00017  *    notice, this list of conditions and the following disclaimer in the
00018  *    documentation and/or other materials provided with the distribution.
00019  * 3. Neither the name of the University nor the names of its contributors
00020  *    may be used to endorse or promote products derived from this software
00021  *    without specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00024  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00027  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00028  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00029  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00032  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00033  * SUCH DAMAGE.
00034  *
00035  * $Id: txn_rec.c,v 12.4 2005/10/19 15:10:45 bostic Exp $
00036  */
00037 
00038 #include "db_config.h"
00039 
00040 #ifndef NO_SYSTEM_INCLUDES
00041 #include <sys/types.h>
00042 
00043 #include <string.h>
00044 #endif
00045 
00046 #include "db_int.h"
00047 #include "dbinc/db_page.h"
00048 #include "dbinc/txn.h"
00049 #include "dbinc/db_am.h"
00050 
00051 /*
00052  * PUBLIC: int __txn_regop_recover
00053  * PUBLIC:    __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
00054  *
00055  * These records are only ever written for commits.  Normally, we redo any
00056  * committed transaction, however if we are doing recovery to a timestamp, then
00057  * we may treat transactions that committed after the timestamp as aborted.
00058  */
00059 int
00060 __txn_regop_recover(dbenv, dbtp, lsnp, op, info)
00061         DB_ENV *dbenv;
00062         DBT *dbtp;
00063         DB_LSN *lsnp;
00064         db_recops op;
00065         void *info;
00066 {
00067         DB_TXNHEAD *headp;
00068         __txn_regop_args *argp;
00069         int ret;
00070         u_int32_t status;
00071 
00072 #ifdef DEBUG_RECOVER
00073         (void)__txn_regop_print(dbenv, dbtp, lsnp, op, info);
00074 #endif
00075 
00076         if ((ret = __txn_regop_read(dbenv, dbtp->data, &argp)) != 0)
00077                 return (ret);
00078 
00079         headp = info;
00080         /*
00081          * We are only ever called during FORWARD_ROLL or BACKWARD_ROLL.
00082          * We check for the former explicitly and the last two clauses
00083          * apply to the BACKWARD_ROLL case.
00084          */
00085 
00086         if (op == DB_TXN_FORWARD_ROLL) {
00087                 /*
00088                  * If this was a 2-phase-commit transaction, then it
00089                  * might already have been removed from the list, and
00090                  * that's OK.  Ignore the return code from remove.
00091                  */
00092                 if ((ret = __db_txnlist_remove(dbenv,
00093                     info, argp->txnid->txnid)) != DB_NOTFOUND && ret != 0)
00094                         goto err;
00095         } else if ((dbenv->tx_timestamp != 0 &&
00096             argp->timestamp > (int32_t)dbenv->tx_timestamp) ||
00097             (!IS_ZERO_LSN(headp->trunc_lsn) &&
00098             log_compare(&headp->trunc_lsn, lsnp) < 0)) {
00099                 /*
00100                  * We failed either the timestamp check or the trunc_lsn check,
00101                  * so we treat this as an abort even if it was a commit record.
00102                  */
00103                 if ((ret = __db_txnlist_update(dbenv, info,
00104                     argp->txnid->txnid, TXN_ABORT, NULL, &status, 1)) != 0)
00105                         goto err;
00106                 else if (status != TXN_IGNORE && status != TXN_OK)
00107                         goto err;
00108         } else {
00109                 /* This is a normal commit; mark it appropriately. */
00110                 if ((ret = __db_txnlist_update(dbenv,
00111                     info, argp->txnid->txnid, argp->opcode, lsnp,
00112                     &status, 0)) == DB_NOTFOUND) {
00113                         if ((ret = __db_txnlist_add(dbenv,
00114                             info, argp->txnid->txnid,
00115                             argp->opcode == TXN_ABORT ?
00116                             TXN_IGNORE : argp->opcode, lsnp)) != 0)
00117                                 goto err;
00118                 } else if (ret != 0 ||
00119                     (status != TXN_IGNORE && status != TXN_OK))
00120                         goto err;
00121         }
00122 
00123         if (ret == 0)
00124                 *lsnp = argp->prev_lsn;
00125 
00126         if (0) {
00127 err:            __db_err(dbenv,
00128                     "txnid %lx commit record found, already on commit list",
00129                     (u_long)argp->txnid->txnid);
00130                 ret = EINVAL;
00131         }
00132         __os_free(dbenv, argp);
00133 
00134         return (ret);
00135 }
00136 
00137 /*
00138  * PUBLIC: int __txn_xa_regop_recover
00139  * PUBLIC:    __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
00140  *
00141  * These records are only ever written for prepares.
00142  */
00143 int
00144 __txn_xa_regop_recover(dbenv, dbtp, lsnp, op, info)
00145         DB_ENV *dbenv;
00146         DBT *dbtp;
00147         DB_LSN *lsnp;
00148         db_recops op;
00149         void *info;
00150 {
00151         __txn_xa_regop_args *argp;
00152         int ret;
00153         u_int32_t status;
00154 
00155 #ifdef DEBUG_RECOVER
00156         (void)__txn_xa_regop_print(dbenv, dbtp, lsnp, op, info);
00157 #endif
00158 
00159         if ((ret = __txn_xa_regop_read(dbenv, dbtp->data, &argp)) != 0)
00160                 return (ret);
00161 
00162         if (argp->opcode != TXN_PREPARE && argp->opcode != TXN_ABORT) {
00163                 ret = EINVAL;
00164                 goto err;
00165         }
00166 
00167         /*
00168          * The return value here is either a DB_NOTFOUND or it is
00169          * the transaction status from the list.  It is not a normal
00170          * error return, so we must make sure that in each of the
00171          * cases below, we overwrite the ret value so we return
00172          * appropriately.
00173          */
00174         ret = __db_txnlist_find(dbenv, info, argp->txnid->txnid, &status);
00175 
00176         /*
00177          * If we are rolling forward, then an aborted prepare
00178          * indicates that this may the last record we'll see for
00179          * this transaction ID, so we should remove it from the
00180          * list.
00181          */
00182 
00183         if (op == DB_TXN_FORWARD_ROLL) {
00184                 if ((ret = __db_txnlist_remove(dbenv,
00185                     info, argp->txnid->txnid)) != 0)
00186                         goto txn_err;
00187         } else if (op == DB_TXN_BACKWARD_ROLL && status == TXN_PREPARE) {
00188                 /*
00189                  * On the backward pass, we have four possibilities:
00190                  * 1. The transaction is already committed, no-op.
00191                  * 2. The transaction is already aborted, no-op.
00192                  * 3. The prepare failed and was aborted, mark as abort.
00193                  * 4. The transaction is neither committed nor aborted.
00194                  *       Treat this like a commit and roll forward so that
00195                  *       the transaction can be resurrected in the region.
00196                  * We handle cases 3 and 4 here; cases 1 and 2
00197                  * are the final clause below.
00198                  */
00199                 if (argp->opcode == TXN_ABORT) {
00200                         if ((ret = __db_txnlist_update(dbenv,
00201                              info, argp->txnid->txnid,
00202                              TXN_ABORT, NULL, &status, 0)) != 0 &&
00203                              status != TXN_PREPARE)
00204                                 goto txn_err;
00205                         ret = 0;
00206                 }
00207                 /*
00208                  * This is prepared, but not yet committed transaction.  We
00209                  * need to add it to the transaction list, so that it gets
00210                  * rolled forward. We also have to add it to the region's
00211                  * internal state so it can be properly aborted or committed
00212                  * after recovery (see txn_recover).
00213                  */
00214                 else if ((ret = __db_txnlist_remove(dbenv,
00215                     info, argp->txnid->txnid)) != 0) {
00216 txn_err:                __db_err(dbenv,
00217                             "Transaction not in list %x", argp->txnid->txnid);
00218                         ret = DB_NOTFOUND;
00219                 } else if ((ret = __db_txnlist_add(dbenv,
00220                    info, argp->txnid->txnid, TXN_COMMIT, lsnp)) == 0)
00221                         ret = __txn_restore_txn(dbenv, lsnp, argp);
00222         } else
00223                 ret = 0;
00224 
00225         if (ret == 0)
00226                 *lsnp = argp->prev_lsn;
00227 
00228 err:    __os_free(dbenv, argp);
00229 
00230         return (ret);
00231 }
00232 
00233 /*
00234  * PUBLIC: int __txn_ckp_recover
00235  * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
00236  */
00237 int
00238 __txn_ckp_recover(dbenv, dbtp, lsnp, op, info)
00239         DB_ENV *dbenv;
00240         DBT *dbtp;
00241         DB_LSN *lsnp;
00242         db_recops op;
00243         void *info;
00244 {
00245         DB_REP *db_rep;
00246         REP *rep;
00247         __txn_ckp_args *argp;
00248         int ret;
00249 
00250 #ifdef DEBUG_RECOVER
00251         __txn_ckp_print(dbenv, dbtp, lsnp, op, info);
00252 #endif
00253         if ((ret = __txn_ckp_read(dbenv, dbtp->data, &argp)) != 0)
00254                 return (ret);
00255 
00256         if (op == DB_TXN_BACKWARD_ROLL)
00257                 __db_txnlist_ckp(dbenv, info, lsnp);
00258 
00259         if (op == DB_TXN_FORWARD_ROLL) {
00260                 /* Record the max generation number that we've seen. */
00261                 if (REP_ON(dbenv)) {
00262                         db_rep = dbenv->rep_handle;
00263                         rep = db_rep->region;
00264                         if (argp->rep_gen > rep->recover_gen)
00265                                 rep->recover_gen = argp->rep_gen;
00266                 }
00267         }
00268 
00269         *lsnp = argp->last_ckp;
00270         __os_free(dbenv, argp);
00271         return (DB_TXN_CKP);
00272 }
00273 
00274 /*
00275  * __txn_child_recover
00276  *      Recover a commit record for a child transaction.
00277  *
00278  * PUBLIC: int __txn_child_recover
00279  * PUBLIC:    __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
00280  */
00281 int
00282 __txn_child_recover(dbenv, dbtp, lsnp, op, info)
00283         DB_ENV *dbenv;
00284         DBT *dbtp;
00285         DB_LSN *lsnp;
00286         db_recops op;
00287         void *info;
00288 {
00289         __txn_child_args *argp;
00290         int ret, t_ret;
00291         u_int32_t c_stat, p_stat, tmpstat;
00292 
00293 #ifdef DEBUG_RECOVER
00294         (void)__txn_child_print(dbenv, dbtp, lsnp, op, info);
00295 #endif
00296         if ((ret = __txn_child_read(dbenv, dbtp->data, &argp)) != 0)
00297                 return (ret);
00298 
00299         /*
00300          * This is a record in a PARENT's log trail indicating that a
00301          * child committed.  If we are aborting, return the childs last
00302          * record's LSN.  If we are in recovery, then if the
00303          * parent is committing, we set ourselves up to commit, else
00304          * we do nothing.
00305          */
00306         if (op == DB_TXN_ABORT) {
00307                 *lsnp = argp->c_lsn;
00308                 ret = __db_txnlist_lsnadd(dbenv, info, &argp->prev_lsn);
00309                 goto out;
00310         } else if (op == DB_TXN_BACKWARD_ROLL) {
00311                 /* Child might exist -- look for it. */
00312                 ret = __db_txnlist_find(dbenv, info, argp->child, &c_stat);
00313                 t_ret =
00314                     __db_txnlist_find(dbenv, info, argp->txnid->txnid, &p_stat);
00315                 if (ret != 0 && ret != DB_NOTFOUND)
00316                         goto out;
00317                 if (t_ret != 0 && t_ret != DB_NOTFOUND) {
00318                         ret = t_ret;
00319                         goto out;
00320                 }
00321                 /*
00322                  * If the parent is in state COMMIT or IGNORE, then we apply
00323                  * that to the child, else we need to abort the child.
00324                  */
00325 
00326                 if (ret == DB_NOTFOUND  ||
00327                     c_stat == TXN_OK || c_stat == TXN_COMMIT) {
00328                         if (t_ret == DB_NOTFOUND ||
00329                              (p_stat != TXN_COMMIT  && p_stat != TXN_IGNORE))
00330                                 c_stat = TXN_ABORT;
00331                         else
00332                                 c_stat = p_stat;
00333 
00334                         if (ret == DB_NOTFOUND)
00335                                 ret = __db_txnlist_add(dbenv,
00336                                      info, argp->child, c_stat, NULL);
00337                         else
00338                                 ret = __db_txnlist_update(dbenv, info,
00339                                      argp->child, c_stat, NULL, &tmpstat, 0);
00340                 } else if (c_stat == TXN_EXPECTED) {
00341                         /*
00342                          * The open after this create succeeded.  If the
00343                          * parent succeeded, we don't want to redo; if the
00344                          * parent aborted, we do want to undo.
00345                          */
00346                         switch (p_stat) {
00347                         case TXN_COMMIT:
00348                         case TXN_IGNORE:
00349                                 c_stat = TXN_IGNORE;
00350                                 break;
00351                         default:
00352                                 c_stat = TXN_ABORT;
00353                         }
00354                         ret = __db_txnlist_update(dbenv,
00355                             info, argp->child, c_stat, NULL, &tmpstat, 0);
00356                 } else if (c_stat == TXN_UNEXPECTED) {
00357                         /*
00358                          * The open after this create failed.  If the parent
00359                          * is rolling forward, we need to roll forward.  If
00360                          * the parent failed, then we do not want to abort
00361                          * (because the file may not be the one in which we
00362                          * are interested).
00363                          */
00364                         ret = __db_txnlist_update(dbenv, info, argp->child,
00365                             p_stat == TXN_COMMIT ? TXN_COMMIT : TXN_IGNORE,
00366                             NULL, &tmpstat, 0);
00367                 }
00368         } else if (op == DB_TXN_OPENFILES) {
00369                 /*
00370                  * If we have a partial subtransaction, then the whole
00371                  * transaction should be ignored.
00372                  */
00373                 if ((ret = __db_txnlist_find(dbenv,
00374                     info, argp->child, &c_stat)) == DB_NOTFOUND)
00375                         ret = __db_txnlist_update(dbenv, info,
00376                              argp->txnid->txnid, TXN_IGNORE,
00377                              NULL, &p_stat, 1);
00378         } else if (DB_REDO(op)) {
00379                 /* Forward Roll */
00380                 if ((ret =
00381                     __db_txnlist_remove(dbenv, info, argp->child)) != 0)
00382                         __db_err(dbenv,
00383                             "Transaction not in list %x", argp->child);
00384         }
00385 
00386         if (ret == 0)
00387                 *lsnp = argp->prev_lsn;
00388 
00389 out:    __os_free(dbenv, argp);
00390 
00391         return (ret);
00392 }
00393 
00394 /*
00395  * __txn_restore_txn --
00396  *      Using only during XA recovery.  If we find any transactions that are
00397  * prepared, but not yet committed, then we need to restore the transaction's
00398  * state into the shared region, because the TM is going to issue an abort
00399  * or commit and we need to respond correctly.
00400  *
00401  * lsnp is the LSN of the returned LSN
00402  * argp is the prepare record (in an appropriate structure)
00403  *
00404  * PUBLIC: int __txn_restore_txn __P((DB_ENV *,
00405  * PUBLIC:     DB_LSN *, __txn_xa_regop_args *));
00406  */
00407 int
00408 __txn_restore_txn(dbenv, lsnp, argp)
00409         DB_ENV *dbenv;
00410         DB_LSN *lsnp;
00411         __txn_xa_regop_args *argp;
00412 {
00413         DB_TXNMGR *mgr;
00414         TXN_DETAIL *td;
00415         DB_TXNREGION *region;
00416         int ret;
00417 
00418         if (argp->xid.size == 0)
00419                 return (0);
00420 
00421         mgr = dbenv->tx_handle;
00422         region = mgr->reginfo.primary;
00423         TXN_SYSTEM_LOCK(dbenv);
00424 
00425         /* Allocate a new transaction detail structure. */
00426         if ((ret =
00427             __db_shalloc(&mgr->reginfo, sizeof(TXN_DETAIL), 0, &td)) != 0) {
00428                 TXN_SYSTEM_UNLOCK(dbenv);
00429                 return (ret);
00430         }
00431 
00432         /* Place transaction on active transaction list. */
00433         SH_TAILQ_INSERT_HEAD(&region->active_txn, td, links, __txn_detail);
00434 
00435         td->txnid = argp->txnid->txnid;
00436         td->begin_lsn = argp->begin_lsn;
00437         td->last_lsn = *lsnp;
00438         td->parent = 0;
00439         td->status = TXN_PREPARED;
00440         td->xa_status = TXN_XA_PREPARED;
00441         memcpy(td->xid, argp->xid.data, argp->xid.size);
00442         td->bqual = argp->bqual;
00443         td->gtrid = argp->gtrid;
00444         td->format = argp->formatID;
00445         td->flags = 0;
00446         F_SET(td, TXN_DTL_RESTORED);
00447 
00448         region->stat.st_nrestores++;
00449         region->stat.st_nactive++;
00450         if (region->stat.st_nactive > region->stat.st_maxnactive)
00451                 region->stat.st_maxnactive = region->stat.st_nactive;
00452         TXN_SYSTEM_UNLOCK(dbenv);
00453         return (0);
00454 }
00455 
00456 /*
00457  * __txn_recycle_recover --
00458  *      Recovery function for recycle.
00459  *
00460  * PUBLIC: int __txn_recycle_recover
00461  * PUBLIC:   __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
00462  */
00463 int
00464 __txn_recycle_recover(dbenv, dbtp, lsnp, op, info)
00465         DB_ENV *dbenv;
00466         DBT *dbtp;
00467         DB_LSN *lsnp;
00468         db_recops op;
00469         void *info;
00470 {
00471         __txn_recycle_args *argp;
00472         int ret;
00473 
00474 #ifdef DEBUG_RECOVER
00475         (void)__txn_child_print(dbenv, dbtp, lsnp, op, info);
00476 #endif
00477         if ((ret = __txn_recycle_read(dbenv, dbtp->data, &argp)) != 0)
00478                 return (ret);
00479 
00480         COMPQUIET(lsnp, NULL);
00481 
00482         if ((ret = __db_txnlist_gen(dbenv, info,
00483             DB_UNDO(op) ? -1 : 1, argp->min, argp->max)) != 0)
00484                 return (ret);
00485 
00486         __os_free(dbenv, argp);
00487 
00488         return (0);
00489 }

Generated on Sun Dec 25 12:14:56 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2