📄 log.c
字号:
*/int__log_vtruncate(dbenv, lsn, ckplsn, trunclsn) DB_ENV *dbenv; DB_LSN *lsn, *ckplsn, *trunclsn;{ DBT log_dbt; DB_LOG *dblp; DB_LOGC *logc; DB_LSN end_lsn; DB_MUTEX *flush_mutexp; LOG *lp; u_int32_t bytes, c_len; int ret, t_ret; /* Need to find out the length of this soon-to-be-last record. */ if ((ret = __log_cursor(dbenv, &logc)) != 0) return (ret); memset(&log_dbt, 0, sizeof(log_dbt)); ret = __log_c_get(logc, lsn, &log_dbt, DB_SET); c_len = logc->c_len; if ((t_ret = __log_c_close(logc)) != 0 && ret == 0) ret = t_ret; if (ret != 0) return (ret); /* Now do the truncate. */ dblp = (DB_LOG *)dbenv->lg_handle; lp = (LOG *)dblp->reginfo.primary; R_LOCK(dbenv, &dblp->reginfo); /* * Flush the log so we can simply initialize the in-memory buffer * after the truncate. */ if ((ret = __log_flush_int(dblp, NULL, 0)) != 0) goto err; end_lsn = lp->lsn; lp->lsn = *lsn; lp->len = c_len; lp->lsn.offset += lp->len; if (lp->db_log_inmemory && (ret = __log_inmem_lsnoff(dblp, &lp->lsn, &lp->b_off)) != 0) goto err; /* * I am going to assume that the number of bytes written since * the last checkpoint doesn't exceed a 32-bit number. */ DB_ASSERT(lp->lsn.file >= ckplsn->file); bytes = 0; if (ckplsn->file != lp->lsn.file) { bytes = lp->log_size - ckplsn->offset; if (lp->lsn.file > ckplsn->file + 1) bytes += lp->log_size * ((lp->lsn.file - ckplsn->file) - 1); bytes += lp->lsn.offset; } else bytes = lp->lsn.offset - ckplsn->offset; lp->stat.st_wc_mbytes += bytes / MEGABYTE; lp->stat.st_wc_bytes += bytes % MEGABYTE; /* * If the saved lsn is greater than our new end of log, reset it * to our current end of log. */ flush_mutexp = R_ADDR(&dblp->reginfo, lp->flush_mutex_off); MUTEX_LOCK(dbenv, flush_mutexp); if (log_compare(&lp->s_lsn, lsn) > 0) lp->s_lsn = lp->lsn; MUTEX_UNLOCK(dbenv, flush_mutexp); /* Initialize the in-region buffer to a pristine state. */ ZERO_LSN(lp->f_lsn); lp->w_off = lp->lsn.offset; if (trunclsn != NULL) *trunclsn = lp->lsn; /* Truncate the log to the new point. */ if ((ret = __log_zero(dbenv, &lp->lsn, &end_lsn)) != 0) goto err;err: R_UNLOCK(dbenv, &dblp->reginfo); return (ret);}/* * __log_is_outdated -- * Used by the replication system to identify if a client's logs * are too old. The log represented by dbenv is compared to the file * number passed in fnum. If the log file fnum does not exist and is * lower-numbered than the current logs, the we return *outdatedp non * zero, else we return it 0. * * PUBLIC: int __log_is_outdated __P((DB_ENV *, u_int32_t, int *)); */int__log_is_outdated(dbenv, fnum, outdatedp) DB_ENV *dbenv; u_int32_t fnum; int *outdatedp;{ DB_LOG *dblp; LOG *lp; char *name; int ret; u_int32_t cfile; struct __db_filestart *filestart; dblp = dbenv->lg_handle; if (F_ISSET(dbenv, DB_ENV_LOG_INMEMORY)) { R_LOCK(dbenv, &dblp->reginfo); lp = (LOG *)dblp->reginfo.primary; filestart = SH_TAILQ_FIRST(&lp->logfiles, __db_filestart); *outdatedp = (fnum < filestart->file); R_UNLOCK(dbenv, &dblp->reginfo); return (0); } *outdatedp = 0; if ((ret = __log_name(dblp, fnum, &name, NULL, 0)) != 0) return (ret); /* If the file exists, we're just fine. */ if (__os_exists(name, NULL) == 0) goto out; /* * It didn't exist, decide if the file number is too big or * too little. If it's too little, then we need to indicate * that the LSN is outdated. */ R_LOCK(dbenv, &dblp->reginfo); lp = (LOG *)dblp->reginfo.primary; cfile = lp->lsn.file; R_UNLOCK(dbenv, &dblp->reginfo); if (cfile > fnum) *outdatedp = 1;out: __os_free(dbenv, name); return (ret);}/* * __log_zero -- * Zero out the tail of a log after a truncate. */static int__log_zero(dbenv, from_lsn, to_lsn) DB_ENV *dbenv; DB_LSN *from_lsn, *to_lsn;{ DB_FH *fhp; DB_LOG *dblp; LOG *lp; struct __db_filestart *filestart, *nextstart; size_t nbytes, len, nw; u_int32_t fn, mbytes, bytes; u_int8_t buf[4096]; int ret; char *fname; dblp = dbenv->lg_handle; DB_ASSERT(log_compare(from_lsn, to_lsn) <= 0); if (log_compare(from_lsn, to_lsn) > 0) { __db_err(dbenv, "Warning: truncating to point beyond end of log"); return (0); } lp = (LOG *)dblp->reginfo.primary; if (lp->db_log_inmemory) { /* * Remove the first file if it is invalidated by this write. * Log records can't be bigger than a file, so we only need to * check the first file. */ for (filestart = SH_TAILQ_FIRST(&lp->logfiles, __db_filestart); filestart != NULL && from_lsn->file < filestart->file; filestart = nextstart) { nextstart = SH_TAILQ_NEXT(filestart, links, __db_filestart); SH_TAILQ_REMOVE(&lp->logfiles, filestart, links, __db_filestart); SH_TAILQ_INSERT_HEAD(&lp->free_logfiles, filestart, links, __db_filestart); } return (0); } /* Close any open file handles so unlinks don't fail. */ if (dblp->lfhp != NULL) { (void)__os_closehandle(dbenv, dblp->lfhp); dblp->lfhp = NULL; } /* Throw away any extra log files that we have around. */ for (fn = from_lsn->file + 1;; fn++) { if (__log_name(dblp, fn, &fname, &fhp, DB_OSO_RDONLY) != 0) { __os_free(dbenv, fname); break; } (void)__os_closehandle(dbenv, fhp); ret = __os_unlink(dbenv, fname); __os_free(dbenv, fname); if (ret != 0) return (ret); } /* We removed some log files; have to 0 to end of file. */ if ((ret = __log_name(dblp, from_lsn->file, &fname, &dblp->lfhp, 0)) != 0) return (ret); __os_free(dbenv, fname); if ((ret = __os_ioinfo(dbenv, NULL, dblp->lfhp, &mbytes, &bytes, NULL)) != 0) goto err; DB_ASSERT((mbytes * MEGABYTE + bytes) >= from_lsn->offset); len = (mbytes * MEGABYTE + bytes) - from_lsn->offset; memset(buf, 0, sizeof(buf)); /* Initialize the write position. */ if ((ret = __os_seek(dbenv, dblp->lfhp, 0, 0, from_lsn->offset, 0, DB_OS_SEEK_SET)) != 0) goto err; while (len > 0) { nbytes = len > sizeof(buf) ? sizeof(buf) : len; if ((ret = __os_write(dbenv, dblp->lfhp, buf, nbytes, &nw)) != 0) goto err; len -= nbytes; }err: (void)__os_closehandle(dbenv, dblp->lfhp); dblp->lfhp = NULL; return (ret);}/* * __log_inmem_lsnoff -- * Find the offset in the buffer of a given LSN. * * PUBLIC: int __log_inmem_lsnoff __P((DB_LOG *, DB_LSN *, size_t *)); */int__log_inmem_lsnoff(dblp, lsn, offsetp) DB_LOG *dblp; DB_LSN *lsn; size_t *offsetp;{ LOG *lp; struct __db_filestart *filestart; lp = (LOG *)dblp->reginfo.primary; SH_TAILQ_FOREACH(filestart, &lp->logfiles, links, __db_filestart) if (filestart->file == lsn->file) { *offsetp = (filestart->b_off + lsn->offset) % lp->buffer_size; return (0); } return (DB_NOTFOUND);}/* * __log_inmem_newfile -- * Records the offset of the beginning of a new file in the in-memory * buffer. * * PUBLIC: int __log_inmem_newfile __P((DB_LOG *, u_int32_t)); */int__log_inmem_newfile(dblp, file) DB_LOG *dblp; u_int32_t file;{ HDR hdr; LOG *lp; struct __db_filestart *filestart; int ret;#ifdef DIAGNOSTIC struct __db_filestart *first, *last;#endif lp = (LOG *)dblp->reginfo.primary; /* * We write an empty header at the end of every in-memory log file. * This is used during cursor traversal to indicate when to switch the * LSN to the next file. */ if (file > 1) { memset(&hdr, 0, sizeof(HDR)); __log_inmem_copyin(dblp, lp->b_off, &hdr, sizeof(HDR)); lp->b_off = (lp->b_off + sizeof(HDR)) % lp->buffer_size; } filestart = SH_TAILQ_FIRST(&lp->free_logfiles, __db_filestart); if (filestart == NULL) { if ((ret = __db_shalloc(&dblp->reginfo, sizeof(struct __db_filestart), 0, &filestart)) != 0) return (ret); memset(filestart, 0, sizeof(*filestart)); } else SH_TAILQ_REMOVE(&lp->free_logfiles, filestart, links, __db_filestart); filestart->file = file; filestart->b_off = lp->b_off;#ifdef DIAGNOSTIC first = SH_TAILQ_FIRST(&lp->logfiles, __db_filestart); last = SH_TAILQ_LAST(&(lp)->logfiles, links, __db_filestart); /* Check that we don't wrap. */ DB_ASSERT(!first || first == last || RINGBUF_LEN(lp, first->b_off, lp->b_off) == RINGBUF_LEN(lp, first->b_off, last->b_off) + RINGBUF_LEN(lp, last->b_off, lp->b_off));#endif SH_TAILQ_INSERT_TAIL(&lp->logfiles, filestart, links); return (0);}/* * __log_inmem_chkspace -- * Ensure that the requested amount of space is available in the buffer, * and invalidate the region. * Note: assumes that the region lock is held on entry. * * PUBLIC: int __log_inmem_chkspace __P((DB_LOG *, size_t)); */int__log_inmem_chkspace(dblp, len) DB_LOG *dblp; size_t len;{ LOG *lp; DB_LSN active_lsn, old_active_lsn; struct __db_filestart *filestart; lp = dblp->reginfo.primary; DB_ASSERT(lp->db_log_inmemory); /* * Allow room for an extra header so that we don't need to check for * space when switching files. */ len += sizeof(HDR); /* * If transactions are enabled and we're about to fill available space, * update the active LSN and recheck. If transactions aren't enabled, * don't even bother checking: in that case we can always overwrite old * log records, because we're never going to abort. */ while (TXN_ON(dblp->dbenv) && RINGBUF_LEN(lp, lp->b_off, lp->a_off) <= len) { old_active_lsn = lp->active_lsn; active_lsn = lp->lsn; /* * Drop the log region lock so we don't hold it while * taking the transaction region lock. */ R_UNLOCK(dblp->dbenv, &dblp->reginfo); __txn_getactive(dblp->dbenv, &active_lsn); R_LOCK(dblp->dbenv, &dblp->reginfo); active_lsn.offset = 0; /* If we didn't make any progress, give up. */ if (log_compare(&active_lsn, &old_active_lsn) == 0) { __db_err(dblp->dbenv, "In-memory log buffer is full (an active transaction spans the buffer)"); return (DB_LOG_BUFFER_FULL); } /* Make sure we're moving the region LSN forwards. */ if (log_compare(&active_lsn, &lp->active_lsn) > 0) { lp->active_lsn = active_lsn; (void)__log_inmem_lsnoff(dblp, &active_lsn, &lp->a_off); } } /* * Remove the first file if it is invalidated by this write. * Log records can't be bigger than a file, so we only need to * check the first file. */ filestart = SH_TAILQ_FIRST(&lp->logfiles, __db_filestart); if (filestart != NULL && RINGBUF_LEN(lp, lp->b_off, filestart->b_off) <= len) { SH_TAILQ_REMOVE(&lp->logfiles, filestart, links, __db_filestart); SH_TAILQ_INSERT_HEAD(&lp->free_logfiles, filestart, links, __db_filestart); lp->f_lsn.file = filestart->file + 1; } return (0);}/* * __log_inmem_copyout -- * Copies the given number of bytes from the buffer -- no checking. * Note: assumes that the region lock is held on entry. * * PUBLIC: void __log_inmem_copyout __P((DB_LOG *, size_t, void *, size_t)); */void__log_inmem_copyout(dblp, offset, buf, size) DB_LOG *dblp; size_t offset; void *buf; size_t size;{ LOG *lp; size_t nbytes; lp = (LOG *)dblp->reginfo.primary; nbytes = (offset + size < lp->buffer_size) ? size : lp->buffer_size - offset; memcpy(buf, dblp->bufp + offset, nbytes); if (nbytes < size) memcpy((u_int8_t *)buf + nbytes, dblp->bufp, size - nbytes);}/* * __log_inmem_copyin -- * Copies the given number of bytes into the buffer -- no checking. * Note: assumes that the region lock is held on entry. * * PUBLIC: void __log_inmem_copyin __P((DB_LOG *, size_t, void *, size_t)); */void__log_inmem_copyin(dblp, offset, buf, size) DB_LOG *dblp; size_t offset; void *buf; size_t size;{ LOG *lp; size_t nbytes; lp = (LOG *)dblp->reginfo.primary; nbytes = (offset + size < lp->buffer_size) ? size : lp->buffer_size - offset; memcpy(dblp->bufp + offset, buf, nbytes); if (nbytes < size) memcpy(dblp->bufp, (u_int8_t *)buf + nbytes, size - nbytes);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -