From 51b9a2e9f78cab95fe69f9efb0b37552082cdf8d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 Jul 2009 08:47:27 +0930 Subject: [PATCH] Import from SAMBA tdb: commit 936d76802f98d04d9743b2ca8eeeaadd4362db51 Author: Andrew Tridgell Date: Tue Dec 16 14:38:17 2008 +1100 imported the tdb_repack() code from CTDB The tdb_repack() function repacks a TDB so that it has a single freelist entry. The file doesn't shrink, but it does remove all freelist fragmentation. This code originated in the CTDB vacuuming code, but will now be used in ldb to cope with fragmentation from re-indexing --- ccan/tdb/tdb.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ ccan/tdb/tdb.h | 5 ++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/ccan/tdb/tdb.c b/ccan/tdb/tdb.c index 42d2438b..3e663345 100644 --- a/ccan/tdb/tdb.c +++ b/ccan/tdb/tdb.c @@ -846,6 +846,98 @@ failed: return -1; } + +struct traverse_state { + bool error; + struct tdb_context *dest_db; +}; + +/* + traverse function for repacking + */ +static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private) +{ + struct traverse_state *state = (struct traverse_state *)private; + if (tdb_store(state->dest_db, key, data, TDB_INSERT) != 0) { + state->error = true; + return -1; + } + return 0; +} + +/* + repack a tdb + */ +int tdb_repack(struct tdb_context *tdb) +{ + struct tdb_context *tmp_db; + struct traverse_state state; + + tdb_trace(tdb, "tdb_repack"); + + if (tdb_transaction_start(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to start transaction\n")); + return -1; + } + + tmp_db = tdb_open("tmpdb", tdb_hash_size(tdb), TDB_INTERNAL, O_RDWR|O_CREAT, 0); + if (tmp_db == NULL) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to create tmp_db\n")); + tdb_transaction_cancel(tdb); + return -1; + } + + state.error = false; + state.dest_db = tmp_db; + + if (tdb_traverse_read(tdb, repack_traverse, &state) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to traverse copying out\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + if (state.error) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Error during traversal\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + if (tdb_wipe_all(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to wipe database\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + state.error = false; + state.dest_db = tdb; + + if (tdb_traverse_read(tmp_db, repack_traverse, &state) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to traverse copying back\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + if (state.error) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Error during second traversal\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + tdb_close(tmp_db); + + if (tdb_transaction_commit(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to commit\n")); + return -1; + } + + return 0; +} + #ifdef TDB_TRACE static void tdb_trace_write(struct tdb_context *tdb, const char *str) { diff --git a/ccan/tdb/tdb.h b/ccan/tdb/tdb.h index 43282e57..80bf8961 100644 --- a/ccan/tdb/tdb.h +++ b/ccan/tdb/tdb.h @@ -161,11 +161,14 @@ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key); void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr); +/* wipe and repack */ +int tdb_wipe_all(struct tdb_context *tdb); +int tdb_repack(struct tdb_context *tdb); + /* Debug functions. Not used in production. */ void tdb_dump_all(struct tdb_context *tdb); int tdb_printfreelist(struct tdb_context *tdb); int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries); -int tdb_wipe_all(struct tdb_context *tdb); int tdb_freelist_size(struct tdb_context *tdb); extern TDB_DATA tdb_null; -- 2.39.2