1N/A/*-
1N/A * See the file LICENSE for redistribution information.
1N/A *
1N/A * Copyright (c) 1996, 1997, 1998
1N/A * Sleepycat Software. All rights reserved.
1N/A */
1N/A
1N/A#include "config.h"
1N/A
1N/A#ifndef lint
1N/Astatic const char sccsid[] = "@(#)log_findckp.c 10.17 (Sleepycat) 9/17/98";
1N/A#endif /* not lint */
1N/A
1N/A#ifndef NO_SYSTEM_INCLUDES
1N/A#include <sys/types.h>
1N/A
1N/A#include <errno.h>
1N/A#include <string.h>
1N/A#endif
1N/A
1N/A#include "db_int.h"
1N/A#include "shqueue.h"
1N/A#include "log.h"
1N/A#include "txn.h"
1N/A#include "common_ext.h"
1N/A
1N/A/*
1N/A * __log_findckp --
1N/A *
1N/A * Looks for the most recent checkpoint that occurs before the most recent
1N/A * checkpoint LSN, subject to the constraint that there must be at least two
1N/A * checkpoints. The reason you need two checkpoints is that you might have
1N/A * crashed during the most recent one and may not have a copy of all the
1N/A * open files. This is the point from which recovery can start and the
1N/A * point up to which archival/truncation can take place. Checkpoints in
1N/A * the log look like:
1N/A *
1N/A * -------------------------------------------------------------------
1N/A * | ckp A, ckplsn 100 | .... record .... | ckp B, ckplsn 600 | ...
1N/A * -------------------------------------------------------------------
1N/A * LSN 500 LSN 1000
1N/A *
1N/A * If we read what log returns from using the DB_CKP parameter to logput,
1N/A * we'll get the record at LSN 1000. The checkpoint LSN there is 600.
1N/A * Now we have to scan backwards looking for a checkpoint before LSN 600.
1N/A * We find one at 500. This means that we can truncate the log before
1N/A * 500 or run recovery beginning at 500.
1N/A *
1N/A * Returns 0 if we find a suitable checkpoint or we retrieved the
1N/A * first record in the log from which to start.
1N/A * Returns DB_NOTFOUND if there are no log records.
1N/A * Returns errno on error.
1N/A *
1N/A * PUBLIC: int __log_findckp __P((DB_LOG *, DB_LSN *));
1N/A */
1N/Aint
1N/A__log_findckp(lp, lsnp)
1N/A DB_LOG *lp;
1N/A DB_LSN *lsnp;
1N/A{
1N/A DBT data;
1N/A DB_LSN ckp_lsn, final_ckp, last_ckp, next_lsn;
1N/A __txn_ckp_args *ckp_args;
1N/A int ret, verbose;
1N/A
1N/A verbose = lp->dbenv != NULL && lp->dbenv->db_verbose != 0;
1N/A
1N/A /*
1N/A * Need to find the appropriate point from which to begin
1N/A * recovery.
1N/A */
1N/A memset(&data, 0, sizeof(data));
1N/A if (F_ISSET(lp, DB_AM_THREAD))
1N/A F_SET(&data, DB_DBT_MALLOC);
1N/A ZERO_LSN(ckp_lsn);
1N/A if ((ret = log_get(lp, &last_ckp, &data, DB_CHECKPOINT)) != 0)
1N/A if (ret == ENOENT)
1N/A goto get_first;
1N/A else
1N/A return (ret);
1N/A
1N/A final_ckp = last_ckp;
1N/A next_lsn = last_ckp;
1N/A do {
1N/A if (F_ISSET(lp, DB_AM_THREAD))
1N/A __os_free(data.data, data.size);
1N/A
1N/A if ((ret = log_get(lp, &next_lsn, &data, DB_SET)) != 0)
1N/A return (ret);
1N/A if ((ret = __txn_ckp_read(data.data, &ckp_args)) != 0) {
1N/A if (F_ISSET(lp, DB_AM_THREAD))
1N/A __os_free(data.data, data.size);
1N/A return (ret);
1N/A }
1N/A if (IS_ZERO_LSN(ckp_lsn))
1N/A ckp_lsn = ckp_args->ckp_lsn;
1N/A if (verbose) {
1N/A __db_err(lp->dbenv, "Checkpoint at: [%lu][%lu]",
1N/A (u_long)last_ckp.file, (u_long)last_ckp.offset);
1N/A __db_err(lp->dbenv, "Checkpoint LSN: [%lu][%lu]",
1N/A (u_long)ckp_args->ckp_lsn.file,
1N/A (u_long)ckp_args->ckp_lsn.offset);
1N/A __db_err(lp->dbenv, "Previous checkpoint: [%lu][%lu]",
1N/A (u_long)ckp_args->last_ckp.file,
1N/A (u_long)ckp_args->last_ckp.offset);
1N/A }
1N/A last_ckp = next_lsn;
1N/A next_lsn = ckp_args->last_ckp;
1N/A __os_free(ckp_args, sizeof(*ckp_args));
1N/A
1N/A /*
1N/A * Keep looping until either you 1) run out of checkpoints,
1N/A * 2) you've found a checkpoint before the most recent
1N/A * checkpoint's LSN and you have at least 2 checkpoints.
1N/A */
1N/A } while (!IS_ZERO_LSN(next_lsn) &&
1N/A (log_compare(&last_ckp, &ckp_lsn) > 0 ||
1N/A log_compare(&final_ckp, &last_ckp) == 0));
1N/A
1N/A if (F_ISSET(lp, DB_AM_THREAD))
1N/A __os_free(data.data, data.size);
1N/A
1N/A /*
1N/A * At this point, either, next_lsn is ZERO or ckp_lsn is the
1N/A * checkpoint lsn and last_ckp is the LSN of the last checkpoint
1N/A * before ckp_lsn. If the compare in the loop is still true, then
1N/A * next_lsn must be 0 and we need to roll forward from the
1N/A * beginning of the log.
1N/A */
1N/A if (log_compare(&last_ckp, &ckp_lsn) > 0 ||
1N/A log_compare(&final_ckp, &last_ckp) == 0) {
1N/Aget_first: if ((ret = log_get(lp, &last_ckp, &data, DB_FIRST)) != 0)
1N/A return (ret);
1N/A if (F_ISSET(lp, DB_AM_THREAD))
1N/A __os_free(data.data, data.size);
1N/A }
1N/A *lsnp = last_ckp;
1N/A
1N/A return (IS_ZERO_LSN(last_ckp) ? DB_NOTFOUND : 0);
1N/A}