From: Rusty Russell Date: Thu, 17 Mar 2011 11:42:20 +0000 (+1030) Subject: tdb2: TDB_SEQNUM and tdb_get_seqnum support. X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=8d8de08d9079f503256e1682dfa93bb22dcd4ad0 tdb2: TDB_SEQNUM and tdb_get_seqnum support. As per TDB1, except that the sequence number is 63 bit. --- diff --git a/ccan/tdb2/io.c b/ccan/tdb2/io.c index 7e5dcb73..0e27e1d0 100644 --- a/ccan/tdb2/io.c +++ b/ccan/tdb2/io.c @@ -563,6 +563,35 @@ static void *tdb_direct(struct tdb_context *tdb, tdb_off_t off, size_t len, return (char *)tdb->file->map_ptr + off; } +void tdb_inc_seqnum(struct tdb_context *tdb) +{ + tdb_off_t seq; + + if (likely(!(tdb->flags & TDB_CONVERT))) { + int64_t *direct; + + direct = tdb->methods->direct(tdb, + offsetof(struct tdb_header, + seqnum), + sizeof(*direct), true); + if (likely(direct)) { + /* Don't let it go negative, even briefly */ + if (unlikely((*direct) + 1) < 0) + *direct = 0; + (*direct)++; + return; + } + } + + seq = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum)); + if (!TDB_OFF_IS_ERR(seq)) { + seq++; + if (unlikely((int64_t)seq < 0)) + seq = 0; + tdb_write_off(tdb, offsetof(struct tdb_header, seqnum), seq); + } +} + void add_stat_(struct tdb_context *tdb, uint64_t *s, size_t val) { if ((uintptr_t)s < (uintptr_t)tdb->stats + tdb->stats->size) diff --git a/ccan/tdb2/open.c b/ccan/tdb2/open.c index 473dc98b..f2d23aed 100644 --- a/ccan/tdb2/open.c +++ b/ccan/tdb2/open.c @@ -107,6 +107,7 @@ static enum TDB_ERROR tdb_new_database(struct tdb_context *tdb, tdb->hash_priv); newdb.hdr.recovery = 0; newdb.hdr.features_used = newdb.hdr.features_offered = TDB_FEATURE_MASK; + newdb.hdr.seqnum = 0; memset(newdb.hdr.reserved, 0, sizeof(newdb.hdr.reserved)); /* Initial hashes are empty. */ memset(newdb.hdr.hashtable, 0, sizeof(newdb.hdr.hashtable)); @@ -243,7 +244,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, } if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT - | TDB_NOSYNC)) { + | TDB_NOSYNC | TDB_SEQNUM)) { ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, "tdb_open: unknown flags %u", tdb_flags); goto fail; diff --git a/ccan/tdb2/private.h b/ccan/tdb2/private.h index 4e7fc8ff..030b46ca 100644 --- a/ccan/tdb2/private.h +++ b/ccan/tdb2/private.h @@ -246,7 +246,9 @@ struct tdb_header { uint64_t features_used; /* Features all writers understand */ uint64_t features_offered; /* Features offered */ - tdb_off_t reserved[24]; + uint64_t seqnum; /* Sequence number for TDB_SEQNUM */ + + tdb_off_t reserved[23]; /* Top level hash table. */ tdb_off_t hashtable[1ULL << TDB_TOPLEVEL_HASH_BITS]; @@ -523,6 +525,9 @@ enum TDB_ERROR tdb_write_convert(struct tdb_context *tdb, tdb_off_t off, enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off, void *rec, size_t len); +/* Bump the seqnum (caller checks for tdb->flags & TDB_SEQNUM) */ +void tdb_inc_seqnum(struct tdb_context *tdb); + /* Adds a stat, if it's in range. */ void add_stat_(struct tdb_context *tdb, uint64_t *stat, size_t val); #define add_stat(tdb, statname, val) \ diff --git a/ccan/tdb2/tdb.c b/ccan/tdb2/tdb.c index 79ccd0f1..569c3d04 100644 --- a/ccan/tdb2/tdb.c +++ b/ccan/tdb2/tdb.c @@ -63,7 +63,9 @@ static enum TDB_ERROR replace_data(struct tdb_context *tdb, return ecode; } - /* FIXME: tdb_increment_seqnum(tdb); */ + if (tdb->flags & TDB_SEQNUM) + tdb_inc_seqnum(tdb); + return TDB_SUCCESS; } @@ -79,6 +81,9 @@ static enum TDB_ERROR update_data(struct tdb_context *tdb, /* Put a zero in; future versions may append other data. */ ecode = tdb->methods->twrite(tdb, off + dbuf.dsize, "", 1); } + if (tdb->flags & TDB_SEQNUM) + tdb_inc_seqnum(tdb); + return ecode; } @@ -285,6 +290,9 @@ enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key) + rec_data_length(&rec) + rec_extra_padding(&rec)); + if (tdb->flags & TDB_SEQNUM) + tdb_inc_seqnum(tdb); + unlock: tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK); return ecode; @@ -313,6 +321,9 @@ void tdb_add_flag(struct tdb_context *tdb, unsigned flag) case TDB_NOSYNC: tdb->flags |= TDB_NOSYNC; break; + case TDB_SEQNUM: + tdb->flags |= TDB_SEQNUM; + break; default: tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, "tdb_add_flag: Unknown flag %u", flag); @@ -337,6 +348,9 @@ void tdb_remove_flag(struct tdb_context *tdb, unsigned flag) case TDB_NOSYNC: tdb->flags &= ~TDB_NOSYNC; break; + case TDB_SEQNUM: + tdb->flags &= ~TDB_SEQNUM; + break; default: tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, "tdb_remove_flag: Unknown flag %u", flag); @@ -433,6 +447,12 @@ const char *tdb_name(const struct tdb_context *tdb) return tdb->name; } +int64_t tdb_get_seqnum(struct tdb_context *tdb) +{ + return tdb_read_off(tdb, offsetof(struct tdb_header, seqnum)); +} + + int tdb_fd(const struct tdb_context *tdb) { return tdb->file->fd; diff --git a/ccan/tdb2/tdb2.h b/ccan/tdb2/tdb2.h index f934a7bf..0940be1c 100644 --- a/ccan/tdb2/tdb2.h +++ b/ccan/tdb2/tdb2.h @@ -78,6 +78,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, #define TDB_NOMMAP 8 /* don't use mmap */ #define TDB_CONVERT 16 /* convert endian */ #define TDB_NOSYNC 64 /* don't use synchronous transactions */ +#define TDB_SEQNUM 128 /* maintain a sequence number */ /** * tdb_close - close and free a tdb. @@ -323,6 +324,25 @@ enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb, TDB_DATA data, void *p), void *p); + +/** + * tdb_get_seqnum - get a database sequence number + * @tdb: the tdb context returned from tdb_open() + * + * This returns a sequence number: any change to the database from a + * tdb context opened with the TDB_SEQNUM flag will cause that number + * to increment. Note that the incrementing is unreliable (it is done + * without locking), so this is only useful as an optimization. + * + * For example, you may have a regular database backup routine which + * does not operate if the sequence number is unchanged. In the + * unlikely event of a failed increment, it will be backed up next + * time any way. + * + * Returns an enum TDB_ERROR (ie. negative) on error. + */ +int64_t tdb_get_seqnum(struct tdb_context *tdb); + /** * tdb_firstkey - get the "first" key in a TDB * @tdb: the tdb context returned from tdb_open() diff --git a/ccan/tdb2/test/failtest_helper.h b/ccan/tdb2/test/failtest_helper.h index c26774f3..6a02cdb0 100644 --- a/ccan/tdb2/test/failtest_helper.h +++ b/ccan/tdb2/test/failtest_helper.h @@ -4,7 +4,7 @@ #include /* FIXME: Check these! */ -#define INITIAL_TDB_MALLOC "open.c", 198, FAILTEST_MALLOC +#define INITIAL_TDB_MALLOC "open.c", 199, FAILTEST_MALLOC #define URANDOM_OPEN "open.c", 44, FAILTEST_OPEN #define URANDOM_READ "open.c", 24, FAILTEST_READ diff --git a/ccan/tdb2/test/run-81-seqnum.c b/ccan/tdb2/test/run-81-seqnum.c new file mode 100644 index 00000000..db42b976 --- /dev/null +++ b/ccan/tdb2/test/run-81-seqnum.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" + +int main(int argc, char *argv[]) +{ + unsigned int i; + struct tdb_context *tdb; + struct tdb_data d = { NULL, 0 }; /* Bogus GCC warning */ + struct tdb_data key = { (unsigned char *)"key", 3 }; + struct tdb_data data = { (unsigned char *)"data", 4 }; + int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP, + TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, + TDB_NOMMAP|TDB_CONVERT }; + + plan_tests(sizeof(flags) / sizeof(flags[0]) * 15 + 4 * 13); + for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { + tdb = tdb_open("run-new_database.tdb", flags[i]|TDB_SEQNUM, + O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr); + if (!ok1(tdb)) + continue; + + ok1(tdb_get_seqnum(tdb) == 0); + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_get_seqnum(tdb) == 1); + /* Fetch doesn't change seqnum */ + if (ok1(tdb_fetch(tdb, key, &d) == TDB_SUCCESS)) + free(d.dptr); + ok1(tdb_get_seqnum(tdb) == 1); + ok1(tdb_append(tdb, key, data) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 2); + + ok1(tdb_delete(tdb, key) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 3); + /* Empty append works */ + ok1(tdb_append(tdb, key, data) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 4); + + ok1(tdb_wipe_all(tdb) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 5); + + if (!(flags[i] & TDB_INTERNAL)) { + ok1(tdb_transaction_start(tdb) == TDB_SUCCESS); + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_get_seqnum(tdb) == 6); + ok1(tdb_append(tdb, key, data) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 7); + ok1(tdb_delete(tdb, key) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 8); + ok1(tdb_transaction_commit(tdb) == TDB_SUCCESS); + ok1(tdb_get_seqnum(tdb) == 8); + + ok1(tdb_transaction_start(tdb) == TDB_SUCCESS); + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_get_seqnum(tdb) == 9); + tdb_transaction_cancel(tdb); + ok1(tdb_get_seqnum(tdb) == 8); + } + tdb_close(tdb); + ok1(tap_log_messages == 0); + } + return exit_status(); +}