r14799: added a tdb_get_seqnum() call, and the TDB_SEQNUM flag. This allows
authorAndrew Tridgell <tridge@samba.org>
Thu, 30 Mar 2006 04:52:39 +0000 (04:52 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:59:20 +0000 (13:59 -0500)
for an extremely lightweight test to see if a tdb has possibly
changed.

source/lib/tdb/common/tdb.c
source/lib/tdb/common/tdb_private.h
source/lib/tdb/common/transaction.c
source/lib/tdb/include/tdb.h

index 4b0d4a31c5d93bed8b7157d286e2022c4b7f9488..b0411601eb48ead853d01d98ac40c212470c8522 100644 (file)
 
 TDB_DATA tdb_null;
 
+/*
+  increment the tdb sequence number if the tdb has been opened using
+  the TDB_SEQNUM flag
+*/
+static void tdb_increment_seqnum(struct tdb_context *tdb)
+{
+       tdb_off_t seqnum=0;
+       
+       if (!(tdb->flags & TDB_SEQNUM)) {
+               return;
+       }
+
+       if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1) != 0) {
+               return;
+       }
+
+       /* we ignore errors from this, as we have no sane way of
+          dealing with them.
+       */
+       tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
+       seqnum++;
+       tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum);
+
+       tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1);
+}
+
+
 /* Returns 0 on fail.  On success, return offset of record, and fills
    in rec */
 static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, u32 hash,
@@ -203,6 +230,11 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash)
        if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))
                return -1;
        ret = tdb_do_delete(tdb, rec_ptr, &rec);
+
+       if (ret == 0) {
+               tdb_increment_seqnum(tdb);
+       }
+
        if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
                TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
        return ret;
@@ -295,6 +327,9 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
                /* Need to tdb_unallocate() here */
                goto fail;
        }
+
+       tdb_increment_seqnum(tdb);
+
  out:
        SAFE_FREE(p); 
        tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
@@ -369,3 +404,22 @@ tdb_log_func tdb_log_fn(struct tdb_context *tdb)
 {
        return tdb->log_fn;
 }
+
+
+/*
+  get the tdb sequence number. Only makes sense if the writers opened
+  with TDB_SEQNUM set. Note that this sequence number will wrap quite
+  quickly, so it should only be used for a 'has something changed'
+  test, not for code that relies on the count of the number of changes
+  made. If you want a counter then use a tdb record.
+
+  The aim of this sequence number is to allow for a very lightweight
+  test of a possible tdb change.
+*/
+int tdb_get_seqnum(struct tdb_context *tdb)
+{
+       tdb_off_t seqnum=0;
+
+       tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
+       return seqnum;
+}
index d7193b2b6780dd60f69910c307442389875c0611..d01a88695d8bdf2981863956b61c572ec7cae0bd 100644 (file)
@@ -94,6 +94,7 @@ typedef u32 tdb_off_t;
 #define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t))
 #define TDB_DATA_START(hash_size) TDB_HASH_TOP(hash_size-1)
 #define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start)
+#define TDB_SEQNUM_OFS    offsetof(struct tdb_header, sequence_number)
 #define TDB_PAD_BYTE 0x42
 #define TDB_PAD_U32  0x42424242
 
@@ -155,7 +156,8 @@ struct tdb_header {
        u32 hash_size; /* number of hash entries */
        tdb_off_t rwlocks; /* obsolete - kept to detect old formats */
        tdb_off_t recovery_start; /* offset of transaction recovery region */
-       tdb_off_t reserved[30];
+       tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */
+       tdb_off_t reserved[29];
 };
 
 struct tdb_lock_type {
index ace4aa51a6213a812e83b12caef6b9028131f166..6960a02ad833b3713a1a0b24bab8dc08ee89c882 100644 (file)
@@ -892,6 +892,12 @@ int tdb_transaction_commit(struct tdb_context *tdb)
 
        tdb_brlock_len(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
 
+       /*
+         TODO: maybe write to some dummy hdr field, or write to magic
+         offset without mmap, before the last sync, instead of the
+         utime() call
+       */
+
        /* on some systems (like Linux 2.6.x) changes via mmap/msync
           don't change the mtime of the file, this means the file may
           not be backed up (as tdb rounding to block sizes means that
index c116f29fc18849490c0b9cfea96e6756ce39f9dd..ddde17850bb65d39e3348458aec74e320bc032d6 100644 (file)
@@ -46,6 +46,7 @@ extern "C" {
 #define TDB_CONVERT 16 /* convert endian (internal use) */
 #define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
 #define TDB_NOSYNC   64 /* don't use synchronous transactions */
+#define TDB_SEQNUM   128 /* maintain a sequence number */
 
 #define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
 
@@ -109,6 +110,7 @@ int tdb_transaction_start(struct tdb_context *tdb);
 int tdb_transaction_commit(struct tdb_context *tdb);
 int tdb_transaction_cancel(struct tdb_context *tdb);
 int tdb_transaction_recover(struct tdb_context *tdb);
+int tdb_get_seqnum(struct tdb_context *tdb);
 
 /* Low level locking functions: use with care */
 int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);