00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 00008 #include "config.h" 00009 00010 #ifndef lint 00011 static const char revid[] = "$Id: log__findckp_8c-source.html,v 1.1 2008年06月08日 10:20:23 sebdiaz Exp $"; 00012 #endif /* not lint */ 00013 00014 #ifndef NO_SYSTEM_INCLUDES 00015 #include <sys/types.h> 00016 00017 #include <errno.h> 00018 #include <string.h> 00019 #endif 00020 00021 #include "db_int.h" 00022 #include "log.h" 00023 #include "txn.h" 00024 00025 /* 00026 * CDB___log_findckp -- 00027 * 00028 * Looks for the most recent checkpoint that occurs before the most recent 00029 * checkpoint LSN, subject to the constraint that there must be at least two 00030 * checkpoints. The reason you need two checkpoints is that you might have 00031 * crashed during the most recent one and may not have a copy of all the 00032 * open files. This is the point from which recovery can start and the 00033 * point up to which archival/truncation can take place. Checkpoints in 00034 * the log look like: 00035 * 00036 * ------------------------------------------------------------------- 00037 * | ckp A, ckplsn 100 | .... record .... | ckp B, ckplsn 600 | ... 00038 * ------------------------------------------------------------------- 00039 * LSN 500 LSN 1000 00040 * 00041 * If we read what log returns from using the DB_CKP parameter to logput, 00042 * we'll get the record at LSN 1000. The checkpoint LSN there is 600. 00043 * Now we have to scan backwards looking for a checkpoint before LSN 600. 00044 * We find one at 500. This means that we can truncate the log before 00045 * 500 or run recovery beginning at 500. 00046 * 00047 * Returns 0 if we find a suitable checkpoint or we retrieved the first 00048 * record in the log from which to start. Returns DB_NOTFOUND if there 00049 * are no log records, errno on error. 00050 * 00051 * PUBLIC: int CDB___log_findckp __P((DB_ENV *, DB_LSN *)); 00052 */ 00053 int 00054 CDB___log_findckp(dbenv, lsnp) 00055 DB_ENV *dbenv; 00056 DB_LSN *lsnp; 00057 { 00058 DBT data; 00059 DB_LSN ckp_lsn, final_ckp, last_ckp, next_lsn; 00060 __txn_ckp_args *ckp_args; 00061 int ret; 00062 00063 /* 00064 * Need to find the appropriate point from which to begin 00065 * recovery. 00066 */ 00067 memset(&data, 0, sizeof(data)); 00068 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00069 F_SET(&data, DB_DBT_MALLOC); 00070 ZERO_LSN(ckp_lsn); 00071 if ((ret = CDB_log_get(dbenv, &last_ckp, &data, DB_CHECKPOINT)) != 0) { 00072 if (ret == ENOENT) 00073 goto get_first; 00074 else 00075 return (ret); 00076 } 00077 final_ckp = last_ckp; 00078 00079 next_lsn = last_ckp; 00080 do { 00081 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00082 CDB___os_free(data.data, data.size); 00083 00084 if ((ret = CDB_log_get(dbenv, &next_lsn, &data, DB_SET)) != 0) 00085 return (ret); 00086 if ((ret = CDB___txn_ckp_read(dbenv, data.data, &ckp_args)) != 0) { 00087 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00088 CDB___os_free(data.data, data.size); 00089 return (ret); 00090 } 00091 if (IS_ZERO_LSN(ckp_lsn)) 00092 ckp_lsn = ckp_args->ckp_lsn; 00093 if (FLD_ISSET(dbenv->verbose, DB_VERB_CHKPOINT)) { 00094 CDB___db_err(dbenv, "Checkpoint at: [%lu][%lu]", 00095 (u_long)last_ckp.file, (u_long)last_ckp.offset); 00096 CDB___db_err(dbenv, "Checkpoint LSN: [%lu][%lu]", 00097 (u_long)ckp_args->ckp_lsn.file, 00098 (u_long)ckp_args->ckp_lsn.offset); 00099 CDB___db_err(dbenv, "Previous checkpoint: [%lu][%lu]", 00100 (u_long)ckp_args->last_ckp.file, 00101 (u_long)ckp_args->last_ckp.offset); 00102 } 00103 last_ckp = next_lsn; 00104 next_lsn = ckp_args->last_ckp; 00105 CDB___os_free(ckp_args, sizeof(*ckp_args)); 00106 00107 /* 00108 * Keep looping until either you 1) run out of checkpoints, 00109 * 2) you've found a checkpoint before the most recent 00110 * checkpoint's LSN and you have at least 2 checkpoints. 00111 */ 00112 } while (!IS_ZERO_LSN(next_lsn) && 00113 (CDB_log_compare(&last_ckp, &ckp_lsn) > 0 || 00114 CDB_log_compare(&final_ckp, &last_ckp) == 0)); 00115 00116 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00117 CDB___os_free(data.data, data.size); 00118 00119 /* 00120 * At this point, either, next_lsn is ZERO or ckp_lsn is the 00121 * checkpoint lsn and last_ckp is the LSN of the last checkpoint 00122 * before ckp_lsn. If the compare in the loop is still true, then 00123 * next_lsn must be 0 and we need to roll forward from the 00124 * beginning of the log. 00125 */ 00126 if (CDB_log_compare(&last_ckp, &ckp_lsn) >= 0 || 00127 CDB_log_compare(&final_ckp, &last_ckp) == 0) { 00128 get_first: if ((ret = CDB_log_get(dbenv, &last_ckp, &data, DB_FIRST)) != 0) 00129 return (ret); 00130 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00131 CDB___os_free(data.data, data.size); 00132 } 00133 *lsnp = last_ckp; 00134 00135 return (IS_ZERO_LSN(last_ckp) ? DB_NOTFOUND : 0); 00136 }