📄 db_dispatch.c
字号:
DB_TXN *txn; DB_TXNLIST *elp; db_limbo_state state;{ DB *dbp; DB_MPOOLFILE *mpf; DBMETA *meta; DB_TXN *ctxn, *t; FNAME *fname; db_pgno_t last_pgno, pgno; int dbp_created, in_retry, ret, t_ret; ctxn = NULL; in_retry = 0; meta = NULL; mpf = NULL; ret = 0; for (; elp != NULL; elp = LIST_NEXT(elp, links)) { if (elp->type != TXNLIST_PGNO) continue;retry: dbp_created = 0; /* * Pick the transaction in which to potentially * log compensations. */ if (state == LIMBO_PREPARE) ctxn = txn; else if (!in_retry && state != LIMBO_RECOVER && state != LIMBO_TIMESTAMP && !T_RESTORED(txn) && (ret = __txn_compensate_begin(dbenv, &ctxn)) != 0) return (ret); /* * Either use the compensating transaction or * the one passed in, which will be null if recovering. */ t = ctxn == NULL ? txn : ctxn; /* First try to get a dbp by fileid. */ ret = __dbreg_id_to_db(dbenv, t, &dbp, elp->u.p.fileid, 0); /* * If the file was closed and reopened its id could change. * Look it up the hard way. */ if (ret == DB_DELETED || ret == ENOENT || ((ret == 0 && memcmp(elp->u.p.uid, dbp->fileid, DB_FILE_ID_LEN) != 0))) { if ((ret = __dbreg_fid_to_fname( dbenv->lg_handle, elp->u.p.uid, 0, &fname)) == 0) ret = __dbreg_id_to_db( dbenv, t, &dbp, fname->id, 0); } /* * File is being destroyed. No need to worry about * dealing with recovery of allocations. */ if (ret == DB_DELETED || (ret == 0 && F_ISSET(dbp, DB_AM_DISCARD))) goto next; if (ret != 0) { if ((ret = db_create(&dbp, dbenv, 0)) != 0) goto err; /* * This tells the system not to lock, which is always * OK, whether this is an abort or recovery. */ F_SET(dbp, DB_AM_COMPENSATE); dbp_created = 1; /* It is ok if the file is nolonger there. */ ret = __db_open(dbp, t, elp->u.p.fname, NULL, DB_UNKNOWN, DB_ODDFILESIZE, __db_omode("rw----"), PGNO_BASE_MD); if (ret == ENOENT) goto next; } /* * Verify that we are opening the same file that we were * referring to when we wrote this log record. */ if (memcmp(elp->u.p.uid, dbp->fileid, DB_FILE_ID_LEN) != 0) goto next; mpf = dbp->mpf; last_pgno = PGNO_INVALID; if (meta == NULL && (ctxn == NULL || state == LIMBO_COMPENSATE)) { pgno = PGNO_BASE_MD; if ((ret = __memp_fget(mpf, &pgno, 0, &meta)) != 0) goto err; last_pgno = meta->free; } if (state == LIMBO_PREPARE) { if ((ret = __db_limbo_prepare(dbp, ctxn, elp)) != 0) goto err; } else ret = __db_limbo_fix(dbp, ctxn, elp, &last_pgno, meta, state); /* * If we were doing compensating transactions, then we are * going to hope this error was due to running out of space. * We'll change modes (into the sync the file mode) and keep * trying. If we weren't doing compensating transactions, * then this is a real error and we're sunk. */ if (ret != 0) { if (ret == DB_RUNRECOVERY || ctxn == NULL) goto err; in_retry = 1; if ((ret = __txn_abort(ctxn)) != 0) goto err; ctxn = NULL; goto retry; } if (state == LIMBO_PREPARE) ctxn = NULL; else if (ctxn != NULL) { /* * We only force compensation at the end of recovery. * We want the txn_commit to be logged so turn * off the recovery flag briefly. */ if (state == LIMBO_COMPENSATE) F_CLR( (DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER); ret = __txn_commit(ctxn, DB_TXN_NOSYNC); ctxn = NULL; if (state == LIMBO_COMPENSATE) F_SET( (DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER); if (ret != 0) goto retry; } /* * This is where we handle the case where we're explicitly * putting together a free list. We need to decide whether * we have to write the meta-data page, and if we do, then * we need to sync it as well. */ else if (last_pgno == meta->free) { /* No change to page; just put the page back. */ if ((ret = __memp_fput(mpf, meta, 0)) != 0) goto err; meta = NULL; } else { /* * These changes are unlogged so we cannot have the * metapage pointing at pages that are not on disk. * Therefore, we flush the new free list, then update * the metapage. We have to put the meta-data page * first so that it isn't pinned when we try to sync. */ if (!IS_RECOVERING(dbenv) && !T_RESTORED(txn)) __db_err(dbenv, "Flushing free list to disk"); if ((ret = __memp_fput(mpf, meta, 0)) != 0) goto err; meta = NULL; /* * If the sync fails then we cannot flush the * newly allocated pages. That is, the file * cannot be extended. Don't let the metapage * point at them. * We may lose these pages from the file if it * can be extended later. If there is never * space for the pages, then things will be ok. */ if ((ret = __db_sync(dbp)) == 0) { pgno = PGNO_BASE_MD; if ((ret = __memp_fget(mpf, &pgno, 0, &meta)) != 0) goto err; meta->free = last_pgno; if ((ret = __memp_fput(mpf, meta, DB_MPOOL_DIRTY)) != 0) goto err; meta = NULL; } else { __db_err(dbenv, "%s: %s", dbp->fname, db_strerror(ret)); __db_err(dbenv, "%s: %s %s", dbp->fname, "allocation flush failed, some free pages", "may not appear in the free list"); ret = 0; } }next: /* * If we get here, either we have processed the list * or the db file has been deleted or could not be opened. */ if (ctxn != NULL && (t_ret = __txn_abort(ctxn)) != 0 && ret == 0) ret = t_ret; if (dbp_created && (t_ret = __db_close(dbp, txn, DB_NOSYNC)) != 0 && ret == 0) ret = t_ret; dbp = NULL; if (state != LIMBO_PREPARE && state != LIMBO_TIMESTAMP) { __os_free(dbenv, elp->u.p.fname); __os_free(dbenv, elp->u.p.pgno_array); } if (ret == ENOENT) ret = 0; else if (ret != 0) goto err; }err: if (meta != NULL) (void)__memp_fput(mpf, meta, 0); return (ret);}/* * __db_limbo_fix -- * Process a single limbo entry which describes all the page allocations * for a single file. */static int__db_limbo_fix(dbp, ctxn, elp, lastp, meta, state) DB *dbp; DB_TXN *ctxn; DB_TXNLIST *elp; db_pgno_t *lastp; DBMETA *meta; db_limbo_state state;{ DBC *dbc; DBT ldbt; DB_MPOOLFILE *mpf; PAGE *freep, *pagep; db_pgno_t next, pgno; u_int32_t i; int ret, t_ret; /* * Loop through the entries for this txnlist element and * either link them into the free list or write a compensating * record for each. */ dbc = NULL; mpf = dbp->mpf; pagep = NULL; ret = 0; for (i = 0; i < elp->u.p.nentries; i++) { pgno = elp->u.p.pgno_array[i]; if (pgno == PGNO_INVALID) continue; if ((ret = __memp_fget(mpf, &pgno, DB_MPOOL_CREATE, &pagep)) != 0) { if (ret != ENOSPC) goto err; continue; } if (state == LIMBO_COMPENSATE || IS_ZERO_LSN(LSN(pagep))) { if (ctxn == NULL) { /* * If this is a fatal recovery which * spans a previous crash this page may * be on the free list already. */ for (next = *lastp; next != 0; ) { if (next == pgno) break; if ((ret = __memp_fget(mpf, &next, 0, &freep)) != 0) goto err; next = NEXT_PGNO(freep); if ((ret = __memp_fput(mpf, freep, 0)) != 0) goto err; } if (next != pgno) { P_INIT(pagep, dbp->pgsize, pgno, PGNO_INVALID, *lastp, 0, P_INVALID); /* Make the lsn non-zero but generic. */ INIT_LSN(LSN(pagep)); *lastp = pgno; } } else if (state == LIMBO_COMPENSATE) { /* * Generate a log record for what we did on the * LIMBO_TIMESTAMP pass. All pages here are * free so P_OVERHEAD is sufficient. */ ZERO_LSN(pagep->lsn); memset(&ldbt, 0, sizeof(ldbt)); ldbt.data = pagep; ldbt.size = P_OVERHEAD(dbp); if ((ret = __db_pg_new_log(dbp, ctxn, &LSN(meta), 0, pagep->pgno, &LSN(meta), PGNO_BASE_MD, &ldbt, pagep->next_pgno)) != 0) goto err; } else { if (dbc == NULL && (ret = __db_cursor(dbp, ctxn, &dbc, 0)) != 0) goto err; /* * If the dbp is compensating (because we * opened it), the dbc will automatically be * marked compensating, but in case we didn't * do the open, we have to mark it explicitly. */ F_SET(dbc, DBC_COMPENSATE); ret = __db_free(dbc, pagep); pagep = NULL; /* * On any error, we hope that the error was * caused due to running out of space, and we * switch modes, doing the processing where we * sync out files instead of doing compensating * transactions. If this was a real error and * not out of space, we assume that some other * call will fail real soon. */ if (ret != 0) { /* Assume that this is out of space. */ (void)__db_c_close(dbc); dbc = NULL; goto err; } } } else elp->u.p.pgno_array[i] = PGNO_INVALID; if (pagep != NULL) { ret = __memp_fput(mpf, pagep, DB_MPOOL_DIRTY); pagep = NULL; } if (ret != 0) goto err; }err: if (pagep != NULL && (t_ret = __memp_fput(mpf, pagep, DB_MPOOL_DIRTY)) != 0 && ret == 0) ret = t_ret; if (dbc != NULL && (t_ret = __db_c_close(dbc)) != 0 && ret == 0) ret = t_ret; return (ret);}static int__db_limbo_prepare(dbp, txn, elp) DB *dbp; DB_TXN *txn; DB_TXNLIST *elp;{ DB_LSN lsn; DB_MPOOLFILE *mpf; PAGE *pagep; db_pgno_t pgno; u_int32_t i; int ret, t_ret; /* * Loop through the entries for this txnlist element and * output a prepare record for them. */ pagep = NULL; ret = 0; mpf = dbp->mpf; for (i = 0; i < elp->u.p.nentries; i++) { pgno = elp->u.p.pgno_array[i]; if ((ret = __memp_fget(mpf, &pgno, DB_MPOOL_CREATE, &pagep)) != 0) { if (ret != ENOSPC) return (ret); continue; } if (IS_ZERO_LSN(LSN(pagep))) ret = __db_pg_prepare_log(dbp, txn, &lsn, 0, pgno); if ((t_ret = __memp_fput(mpf, pagep, 0)) != 0 && ret == 0) ret = t_ret; if (ret != 0) return (ret); } return (0);}#define DB_TXNLIST_MAX_PGNO 8 /* A nice even number. *//* * __db_txnlist_pgnoadd -- * Find the txnlist entry for a file and add this pgno, or add the list * entry for the file and then add the pgno. */static int__db_txnlist_pgnoadd(dbenv, hp, fileid, uid, fname, pgno) DB_ENV *dbenv; DB_TXNHEAD *hp; int32_t fileid; u_int8_t uid[DB_FILE_ID_LEN]; char *fname; db_pgno_t pgno;{ DB_TXNLIST *elp; size_t len; u_int32_t hash, status; int ret; elp = NULL; if ((ret = __db_txnlist_find_internal(dbenv, hp, TXNLIST_PGNO, 0, uid, &elp, 0, &status)) != 0 && ret != DB_NOTFOUND) goto err; if (ret == DB_NOTFOUND || status != TXN_OK) { if ((ret = __os_malloc(dbenv, sizeof(DB_TXNLIST), &elp)) != 0) goto err; memcpy(&hash, uid, sizeof(hash)); LIST_INSERT_HEAD( &hp->head[DB_TXNLIST_MASK(hp, hash)], elp, links); memcpy(elp->u.p.uid, uid, DB_FILE_ID_LEN); len = strlen(fname) + 1; if ((ret = __os_malloc(dbenv, len, &elp->u.p.fname)) != 0) goto err; memcpy(elp->u.p.fname, fname, len); elp->u.p.maxentry = 0; elp->u.p.locked = 0; elp->type = TXNLIST_PGNO; if ((ret = __os_malloc(dbenv, 8 * sizeof(db_pgno_t), &elp->u.p.pgno_array)) != 0) goto err; elp->u.p.maxentry = DB_TXNLIST_MAX_PGNO; elp->u.p.nentries = 0; } else if (elp->u.p.nentries == elp->u.p.maxentry) { elp->u.p.maxentry <<= 1; if ((ret = __os_realloc(dbenv, elp->u.p.maxentry * sizeof(db_pgno_t), &elp->u.p.pgno_array)) != 0) goto err; } elp->u.p.pgno_array[elp->u.p.nentries++] = pgno; /* Update to the latest fileid. Limbo will find it faster. */ elp->u.p.fileid = fileid; return (0);err: return (ret);}#endif#ifdef DEBUG/* * __db_txnlist_print -- * Print out the transaction list. * * PUBLIC: void __db_txnlist_print __P((void *)); */void__db_txnlist_print(listp) void *listp;{ DB_TXNHEAD *hp; DB_TXNLIST *p; u_int32_t i; char *txntype; hp = (DB_TXNHEAD *)listp; printf("Maxid: %lu Generation: %lu\n", (u_long)hp->maxid, (u_long)hp->generation); for (i = 0; i < hp->nslots; i++) for (p = LIST_FIRST(&hp->head[i]); p != NULL; p = LIST_NEXT(p, links)) { if (p->type != TXNLIST_TXNID) { printf("Unrecognized type: %d\n", p->type); continue; } switch (p->u.t.status) { case TXN_OK: txntype = "OK"; break; case TXN_COMMIT: txntype = "commit"; break; case TXN_PREPARE: txntype = "prepare"; break; case TXN_ABORT: txntype = "abort"; break; case TXN_IGNORE: txntype = "ignore"; break; case TXN_EXPECTED: txntype = "expected"; break; case TXN_UNEXPECTED: txntype = "unexpected"; break; default: txntype = "UNKNOWN"; break; } printf("TXNID: %lx(%lu): %s\n", (u_long)p->u.t.txnid, (u_long)p->u.t.generation, txntype); }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -