tdb transaction_prepare
Howard Chu
hyc at highlandsun.com
Mon Mar 30 22:58:53 GMT 2009
Tridge, here's a first pass as we discussed on irc. I think that zeroing out
the recovery data from the magic_offset needs to be moved into
transaction_cancel but I haven't tried that yet.
--
-- Howard Chu
CTO, Symas Corp. http://www.symas.com
Director, Highland Sun http://highlandsun.com/hyc/
Chief Architect, OpenLDAP http://www.openldap.org/project/
-------------- next part --------------
diff --git a/lib/tdb/common/transaction.c b/lib/tdb/common/transaction.c
index 7acda64..10a3321 100644
--- a/lib/tdb/common/transaction.c
+++ b/lib/tdb/common/transaction.c
@@ -116,6 +116,10 @@ struct tdb_transaction {
but don't create a new transaction */
int nesting;
+ /* set when a prepare has already occurred */
+ int prepared;
+ tdb_off_t magic_offset;
+
/* old file size before transaction */
tdb_len_t old_map_size;
};
@@ -826,36 +830,33 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
}
/*
- commit the current transaction
+ prepare the current transaction
*/
-int tdb_transaction_commit(struct tdb_context *tdb)
+int tdb_transaction_prepare(struct tdb_context *tdb)
{
const struct tdb_methods *methods;
- tdb_off_t magic_offset = 0;
uint32_t zero = 0;
int i;
if (tdb->transaction == NULL) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare: no transaction\n"));
return -1;
}
if (tdb->transaction->transaction_error) {
tdb->ecode = TDB_ERR_IO;
tdb_transaction_cancel(tdb);
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare: transaction error pending\n"));
return -1;
}
if (tdb->transaction->nesting != 0) {
- tdb->transaction->nesting--;
return 0;
}
/* check for a null transaction */
if (tdb->transaction->blocks == NULL) {
- tdb_transaction_cancel(tdb);
return 0;
}
@@ -865,14 +866,14 @@ int tdb_transaction_commit(struct tdb_context *tdb)
nested their locks properly, so fail the transaction */
if (tdb->num_locks || tdb->global_lock.count) {
tdb->ecode = TDB_ERR_LOCK;
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare: locks pending on commit\n"));
tdb_transaction_cancel(tdb);
return -1;
}
/* upgrade the main transaction lock region to a write lock */
if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare: failed to upgrade hash locks\n"));
tdb->ecode = TDB_ERR_LOCK;
tdb_transaction_cancel(tdb);
return -1;
@@ -881,7 +882,7 @@ int tdb_transaction_commit(struct tdb_context *tdb)
/* get the global lock - this prevents new users attaching to the database
during the commit */
if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare: failed to get global lock\n"));
tdb->ecode = TDB_ERR_LOCK;
tdb_transaction_cancel(tdb);
return -1;
@@ -889,21 +890,23 @@ int tdb_transaction_commit(struct tdb_context *tdb)
if (!(tdb->flags & TDB_NOSYNC)) {
/* write the recovery data to the end of the file */
- if (transaction_setup_recovery(tdb, &magic_offset) == -1) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n"));
+ if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) {
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare: failed to setup recovery data\n"));
tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
tdb_transaction_cancel(tdb);
return -1;
}
}
+ tdb->transaction->prepared = 1;
+
/* expand the file to the new size if needed */
if (tdb->map_size != tdb->transaction->old_map_size) {
if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
tdb->map_size -
tdb->transaction->old_map_size) == -1) {
tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n"));
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare: expansion failed\n"));
tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
tdb_transaction_cancel(tdb);
return -1;
@@ -912,6 +915,52 @@ int tdb_transaction_commit(struct tdb_context *tdb)
methods->tdb_oob(tdb, tdb->map_size + 1, 1);
}
+ /* Keep the global lock until the actual commit */
+
+ return 0;
+}
+
+/*
+ commit the current transaction
+*/
+int tdb_transaction_commit(struct tdb_context *tdb)
+{
+ const struct tdb_methods *methods;
+ uint32_t zero = 0;
+ int i;
+
+ if (tdb->transaction == NULL) {
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
+ return -1;
+ }
+
+ if (tdb->transaction->transaction_error) {
+ tdb->ecode = TDB_ERR_IO;
+ tdb_transaction_cancel(tdb);
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n"));
+ return -1;
+ }
+
+
+ if (tdb->transaction->nesting != 0) {
+ tdb->transaction->nesting--;
+ return 0;
+ }
+
+ /* check for a null transaction */
+ if (tdb->transaction->blocks == NULL) {
+ tdb_transaction_cancel(tdb);
+ return 0;
+ }
+
+ if (!tdb->transaction->prepared) {
+ i = tdb_transaction_prepare(tdb);
+ if (i)
+ return i;
+ }
+
+ methods = tdb->transaction->io_methods;
+
/* perform all the writes */
for (i=0;i<tdb->transaction->num_blocks;i++) {
tdb_off_t offset;
@@ -955,13 +1004,13 @@ int tdb_transaction_commit(struct tdb_context *tdb)
}
/* remove the recovery marker */
- if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) {
+ if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &zero, 4) == -1) {
TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n"));
return -1;
}
/* ensure the recovery marker has been removed on disk */
- if (transaction_sync(tdb, magic_offset, 4) == -1) {
+ if (transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) {
return -1;
}
}
More information about the samba-technical
mailing list