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

txn_recover.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2001-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: txn_recover.c,v 12.11 2005/10/14 21:12:18 ubell Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <string.h>
00016 #endif
00017 
00018 #include "db_int.h"
00019 #include "dbinc/txn.h"
00020 #include "dbinc/db_page.h"
00021 #include "dbinc/db_dispatch.h"
00022 #include "dbinc/log.h"
00023 #include "dbinc_auto/db_auto.h"
00024 #include "dbinc_auto/crdel_auto.h"
00025 #include "dbinc_auto/db_ext.h"
00026 
00027 /*
00028  * __txn_map_gid
00029  *      Return the txn that corresponds to this global ID.
00030  *
00031  * PUBLIC: int __txn_map_gid __P((DB_ENV *,
00032  * PUBLIC:     u_int8_t *, TXN_DETAIL **, roff_t *));
00033  */
00034 int
00035 __txn_map_gid(dbenv, gid, tdp, offp)
00036         DB_ENV *dbenv;
00037         u_int8_t *gid;
00038         TXN_DETAIL **tdp;
00039         roff_t *offp;
00040 {
00041         DB_TXNMGR *mgr;
00042         DB_TXNREGION *region;
00043 
00044         mgr = dbenv->tx_handle;
00045         region = mgr->reginfo.primary;
00046 
00047         /*
00048          * Search the internal active transaction table to find the
00049          * matching xid.  If this is a performance hit, then we
00050          * can create a hash table, but I doubt it's worth it.
00051          */
00052         TXN_SYSTEM_LOCK(dbenv);
00053         for (*tdp = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
00054             *tdp != NULL;
00055             *tdp = SH_TAILQ_NEXT(*tdp, links, __txn_detail))
00056                 if (memcmp(gid, (*tdp)->xid, sizeof((*tdp)->xid)) == 0)
00057                         break;
00058         TXN_SYSTEM_UNLOCK(dbenv);
00059 
00060         if (*tdp == NULL)
00061                 return (EINVAL);
00062 
00063         *offp = R_OFFSET(&mgr->reginfo, *tdp);
00064         return (0);
00065 }
00066 
00067 /*
00068  * __txn_recover_pp --
00069  *      DB_ENV->txn_recover pre/post processing.
00070  *
00071  * PUBLIC: int __txn_recover_pp
00072  * PUBLIC:     __P((DB_ENV *, DB_PREPLIST *, long, long *, u_int32_t));
00073  */
00074 int
00075 __txn_recover_pp(dbenv, preplist, count, retp, flags)
00076         DB_ENV *dbenv;
00077         DB_PREPLIST *preplist;
00078         long count, *retp;
00079         u_int32_t flags;
00080 {
00081         DB_THREAD_INFO *ip;
00082         int ret;
00083 
00084         PANIC_CHECK(dbenv);
00085         ENV_REQUIRES_CONFIG(
00086             dbenv, dbenv->tx_handle, "txn_recover", DB_INIT_TXN);
00087 
00088         if (F_ISSET((DB_TXNREGION *)
00089             ((DB_TXNMGR *)dbenv->tx_handle)->reginfo.primary,
00090             TXN_IN_RECOVERY)) {
00091                 __db_err(dbenv, "operation not permitted while in recovery");
00092                 return (EINVAL);
00093         }
00094 
00095         ENV_ENTER(dbenv, ip);
00096         REPLICATION_WRAP(dbenv,
00097             (__txn_recover(dbenv, preplist, count, retp, flags)), ret);
00098         ENV_LEAVE(dbenv, ip);
00099         return (ret);
00100 }
00101 
00102 /*
00103  * __txn_recover --
00104  *      DB_ENV->txn_recover.
00105  *
00106  * PUBLIC: int __txn_recover
00107  * PUBLIC:         __P((DB_ENV *, DB_PREPLIST *, long, long *, u_int32_t));
00108  */
00109 int
00110 __txn_recover(dbenv, preplist, count, retp, flags)
00111         DB_ENV *dbenv;
00112         DB_PREPLIST *preplist;
00113         long count, *retp;
00114         u_int32_t flags;
00115 {
00116         /*
00117          * Public API to retrieve the list of prepared, but not yet committed
00118          * transactions.  See __txn_get_prepared for details.  This function
00119          * and __db_xa_recover both wrap that one.
00120          */
00121         return (__txn_get_prepared(dbenv, NULL, preplist, count, retp, flags));
00122 }
00123 
00124 /*
00125  * __txn_get_prepared --
00126  *      Returns a list of prepared (and for XA, heuristically completed)
00127  *      transactions (less than or equal to the count parameter).  One of
00128  *      xids or txns must be set to point to an array of the appropriate type.
00129  *      The count parameter indicates the number of entries in the xids and/or
00130  *      txns array. The retp parameter will be set to indicate the number of
00131  *      entries returned in the xids/txns array.  Flags indicates the operation,
00132  *      one of DB_FIRST or DB_NEXT.
00133  *
00134  * PUBLIC: int __txn_get_prepared __P((DB_ENV *,
00135  * PUBLIC:     XID *, DB_PREPLIST *, long, long *, u_int32_t));
00136  */
00137 int
00138 __txn_get_prepared(dbenv, xids, txns, count, retp, flags)
00139         DB_ENV *dbenv;
00140         XID *xids;
00141         DB_PREPLIST *txns;
00142         long count;             /* This is long for XA compatibility. */
00143         long  *retp;
00144         u_int32_t flags;
00145 {
00146         DB_LSN min;
00147         DB_PREPLIST *prepp;
00148         DB_TXNMGR *mgr;
00149         DB_TXNREGION *region;
00150         TXN_DETAIL *td;
00151         XID *xidp;
00152         long i;
00153         int nrestores, open_files, ret;
00154 
00155         *retp = 0;
00156 
00157         MAX_LSN(min);
00158         prepp = txns;
00159         xidp = xids;
00160         nrestores = ret = 0;
00161         open_files = 1;
00162 
00163         /*
00164          * If we are starting a scan, then we traverse the active transaction
00165          * list once making sure that all transactions are marked as not having
00166          * been collected.  Then on each pass, we mark the ones we collected
00167          * so that if we cannot collect them all at once, we can finish up
00168          * next time with a continue.
00169          */
00170 
00171         mgr = dbenv->tx_handle;
00172         region = mgr->reginfo.primary;
00173 
00174         /*
00175          * During this pass we need to figure out if we are going to need
00176          * to open files.  We need to open files if we've never collected
00177          * before (in which case, none of the COLLECTED bits will be set)
00178          * and the ones that we are collecting are restored (if they aren't
00179          * restored, then we never crashed; just the main server did).
00180          */
00181         TXN_SYSTEM_LOCK(dbenv);
00182         if (flags == DB_FIRST) {
00183                 for (td = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
00184                     td != NULL;
00185                     td = SH_TAILQ_NEXT(td, links, __txn_detail)) {
00186                         if (F_ISSET(td, TXN_DTL_RESTORED))
00187                                 nrestores++;
00188                         if (F_ISSET(td, TXN_DTL_COLLECTED))
00189                                 open_files = 0;
00190                         F_CLR(td, TXN_DTL_COLLECTED);
00191                 }
00192                 mgr->n_discards = 0;
00193         } else
00194                 open_files = 0;
00195 
00196         /* Now begin collecting active transactions. */
00197         for (td = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
00198             td != NULL && *retp < count;
00199             td = SH_TAILQ_NEXT(td, links, __txn_detail)) {
00200                 if (td->status != TXN_PREPARED ||
00201                     F_ISSET(td, TXN_DTL_COLLECTED))
00202                         continue;
00203 
00204                 if (xids != NULL) {
00205                         xidp->formatID = td->format;
00206                         /*
00207                          * XID structure uses longs; we use u_int32_t's as we
00208                          * log them to disk.  Cast them to make the conversion
00209                          * explicit.
00210                          */
00211                         xidp->gtrid_length = (long)td->gtrid;
00212                         xidp->bqual_length = (long)td->bqual;
00213                         memcpy(xidp->data, td->xid, sizeof(td->xid));
00214                         xidp++;
00215                 }
00216 
00217                 if (txns != NULL) {
00218                         if ((ret = __os_calloc(dbenv,
00219                             1, sizeof(DB_TXN), &prepp->txn)) != 0) {
00220                                 TXN_SYSTEM_UNLOCK(dbenv);
00221                                 goto err;
00222                         }
00223                         __txn_continue(dbenv, prepp->txn, td);
00224                         F_SET(prepp->txn, TXN_MALLOC);
00225                         memcpy(prepp->gid, td->xid, sizeof(td->xid));
00226                         prepp++;
00227                 }
00228 
00229                 if (!IS_ZERO_LSN(td->begin_lsn) &&
00230                     log_compare(&td->begin_lsn, &min) < 0)
00231                         min = td->begin_lsn;
00232 
00233                 (*retp)++;
00234                 F_SET(td, TXN_DTL_COLLECTED);
00235                 if (IS_ENV_REPLICATED(dbenv) &&
00236                     (ret = __op_rep_enter(dbenv)) != 0)
00237                         goto err;
00238         }
00239         TXN_SYSTEM_UNLOCK(dbenv);
00240 
00241         /*
00242          * Now link all the transactions into the transaction manager's list.
00243          */
00244         if (txns != NULL) {
00245                 MUTEX_LOCK(dbenv, mgr->mutex);
00246                 for (i = 0; i < *retp; i++)
00247                         TAILQ_INSERT_TAIL(&mgr->txn_chain, txns[i].txn, links);
00248                 MUTEX_UNLOCK(dbenv, mgr->mutex);
00249         }
00250 
00251         if (open_files && nrestores && *retp != 0 && !IS_MAX_LSN(min)) {
00252                 F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
00253                 ret = __txn_openfiles(dbenv, &min, 0);
00254                 F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
00255         }
00256         return (0);
00257 
00258 err:    TXN_SYSTEM_UNLOCK(dbenv);
00259         return (ret);
00260 }
00261 
00262 /*
00263  * __txn_openfiles --
00264  *      Call env_openfiles.
00265  *
00266  * PUBLIC: int __txn_openfiles __P((DB_ENV *, DB_LSN *, int));
00267  */
00268 int
00269 __txn_openfiles(dbenv, min, force)
00270         DB_ENV *dbenv;
00271         DB_LSN *min;
00272         int force;
00273 {
00274         DBT data;
00275         DB_LOGC *logc;
00276         DB_LSN open_lsn;
00277         DB_TXNHEAD *txninfo;
00278         __txn_ckp_args *ckp_args;
00279         int ret, t_ret;
00280 
00281         /*
00282          * Figure out the last checkpoint before the smallest
00283          * start_lsn in the region.
00284          */
00285         logc = NULL;
00286         if ((ret = __log_cursor(dbenv, &logc)) != 0)
00287                 goto err;
00288 
00289         memset(&data, 0, sizeof(data));
00290         if ((ret = __txn_getckp(dbenv, &open_lsn)) == 0)
00291                 while (!IS_ZERO_LSN(open_lsn) && (ret =
00292                     __log_c_get(logc, &open_lsn, &data, DB_SET)) == 0 &&
00293                     (force ||
00294                     (min != NULL && log_compare(min, &open_lsn) < 0))) {
00295                         /* Format the log record. */
00296                         if ((ret = __txn_ckp_read(dbenv,
00297                             data.data, &ckp_args)) != 0) {
00298                                 __db_err(dbenv,
00299                             "Invalid checkpoint record at [%lu][%lu]",
00300                                     (u_long)open_lsn.file,
00301                                     (u_long)open_lsn.offset);
00302                                 goto err;
00303                         }
00304                         /*
00305                          * If force is set, then we're forcing ourselves
00306                          * to go back far enough to open files.
00307                          * Use ckp_lsn and then break out of the loop.
00308                          */
00309                         open_lsn = force ? ckp_args->ckp_lsn :
00310                             ckp_args->last_ckp;
00311                         __os_free(dbenv, ckp_args);
00312                         if (force) {
00313                                 if ((ret = __log_c_get(logc, &open_lsn,
00314                                     &data, DB_SET)) != 0)
00315                                         goto err;
00316                                 break;
00317                         }
00318                 }
00319 
00320         /*
00321          * There are several ways by which we may have gotten here.
00322          * - We got a DB_NOTFOUND -- we need to read the first
00323          *      log record.
00324          * - We found a checkpoint before min.  We're done.
00325          * - We found a checkpoint after min who's last_ckp is 0.  We
00326          *      need to start at the beginning of the log.
00327          * - We are forcing an openfiles and we have our ckp_lsn.
00328          */
00329         if ((ret == DB_NOTFOUND || IS_ZERO_LSN(open_lsn)) && (ret =
00330             __log_c_get(logc, &open_lsn, &data, DB_FIRST)) != 0) {
00331                 __db_err(dbenv, "No log records");
00332                 goto err;
00333         }
00334 
00335         if ((ret = __db_txnlist_init(dbenv, 0, 0, NULL, &txninfo)) != 0)
00336                 goto err;
00337         ret = __env_openfiles(dbenv, logc,
00338             txninfo, &data, &open_lsn, NULL, 0, 0);
00339         if (txninfo != NULL)
00340                 __db_txnlist_end(dbenv, txninfo);
00341 
00342 err:
00343         if (logc != NULL && (t_ret = __log_c_close(logc)) != 0 && ret == 0)
00344                 ret = t_ret;
00345         return (ret);
00346 }

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