diff --git a/ctdb/Makefile.in b/ctdb/Makefile.in
index a92f7c8124a..05ab99a7ef2 100644
--- a/ctdb/Makefile.in
+++ b/ctdb/Makefile.in
@@ -48,10 +48,10 @@ CTDB_SERVER_OBJ = server/ctdbd.o server/ctdb_daemon.o server/ctdb_lockwait.o \
server/ctdb_tunables.o server/ctdb_monitor.o server/ctdb_server.o \
server/ctdb_control.o server/ctdb_call.o server/ctdb_ltdb_server.o \
server/ctdb_traverse.o server/eventscript.o server/ctdb_takeover.o \
- server/ctdb_serverids.o \
+ server/ctdb_serverids.o server/ctdb_persistent.o \
$(CTDB_CLIENT_OBJ) $(CTDB_TCP_OBJ) @INFINIBAND_WRAPPER_OBJ@
-TEST_BINS=bin/ctdb_bench bin/ctdb_fetch bin/ctdb_store bin/rb_test \
+TEST_BINS=bin/ctdb_bench bin/ctdb_fetch bin/ctdb_store bin/ctdb_persistent bin/rb_test \
@INFINIBAND_BINS@
BINS = bin/ctdb @CTDB_SCSI_IO@ bin/smnotify
@@ -120,6 +120,10 @@ bin/ctdb_store: $(CTDB_CLIENT_OBJ) tests/ctdb_store.o
@echo Linking $@
@$(CC) $(CFLAGS) -o $@ tests/ctdb_store.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
+bin/ctdb_persistent: $(CTDB_CLIENT_OBJ) tests/ctdb_persistent.o
+ @echo Linking $@
+ @$(CC) $(CFLAGS) -o $@ tests/ctdb_persistent.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
+
bin/ibwrapper_test: $(CTDB_CLIENT_OBJ) ib/ibwrapper_test.o
@echo Linking $@
@$(CC) $(CFLAGS) -o $@ ib/ibwrapper_test.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
diff --git a/ctdb/client/ctdb_client.c b/ctdb/client/ctdb_client.c
index 6463c2d8fdb..b816d2d720c 100644
--- a/ctdb/client/ctdb_client.c
+++ b/ctdb/client/ctdb_client.c
@@ -638,7 +638,46 @@ again:
*/
int ctdb_record_store(struct ctdb_record_handle *h, TDB_DATA data)
{
- return ctdb_ltdb_store(h->ctdb_db, h->key, &h->header, data);
+ int ret;
+ int32_t status;
+ struct ctdb_rec_data *rec;
+ TDB_DATA recdata;
+
+ if (h->ctdb_db->persistent) {
+ h->header.rsn++;
+ }
+
+ ret = ctdb_ltdb_store(h->ctdb_db, h->key, &h->header, data);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* don't need the persistent_store control for non-persistent databases */
+ if (!h->ctdb_db->persistent) {
+ return 0;
+ }
+
+ rec = ctdb_marshall_record(h, h->ctdb_db->db_id, h->key, &h->header, data);
+ if (rec == NULL) {
+ DEBUG(0,("Unable to marshall record in ctdb_record_store\n"));
+ return -1;
+ }
+
+ recdata.dptr = (uint8_t *)rec;
+ recdata.dsize = rec->length;
+
+ ret = ctdb_control(h->ctdb_db->ctdb, CTDB_CURRENT_NODE, 0,
+ CTDB_CONTROL_PERSISTENT_STORE, 0,
+ recdata, NULL, NULL, &status, NULL, NULL);
+
+ talloc_free(rec);
+
+ if (ret != 0 || status != 0) {
+ DEBUG(0,("Failed persistent store in ctdb_record_store\n"));
+ return -1;
+ }
+
+ return 0;
}
/*
@@ -1449,7 +1488,8 @@ int ctdb_ctrl_getdbname(struct ctdb_context *ctdb, struct timeval timeout, uint3
/*
create a database
*/
-int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, const char *name)
+int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode,
+ TALLOC_CTX *mem_ctx, const char *name, bool persistent)
{
int ret;
int32_t res;
@@ -1459,7 +1499,8 @@ int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout, uint32
data.dsize = strlen(name)+1;
ret = ctdb_control(ctdb, destnode, 0,
- CTDB_CONTROL_DB_ATTACH, 0, data,
+ persistent?CTDB_CONTROL_DB_ATTACH_PERSISTENT:CTDB_CONTROL_DB_ATTACH,
+ 0, data,
mem_ctx, &data, &res, &timeout, NULL);
if (ret != 0 || res != 0) {
@@ -1571,7 +1612,7 @@ int ctdb_statistics_reset(struct ctdb_context *ctdb, uint32_t destnode)
/*
attach to a specific database - client call
*/
-struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name)
+struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, bool persistent)
{
struct ctdb_db_context *ctdb_db;
TDB_DATA data;
@@ -1594,7 +1635,8 @@ struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name)
data.dsize = strlen(name)+1;
/* tell ctdb daemon to attach */
- ret = ctdb_control(ctdb, CTDB_CURRENT_NODE, 0, CTDB_CONTROL_DB_ATTACH,
+ ret = ctdb_control(ctdb, CTDB_CURRENT_NODE, 0,
+ persistent?CTDB_CONTROL_DB_ATTACH_PERSISTENT:CTDB_CONTROL_DB_ATTACH,
0, data, ctdb_db, &data, &res, NULL, NULL);
if (ret != 0 || res != 0 || data.dsize != sizeof(uint32_t)) {
DEBUG(0,("Failed to attach to database '%s'\n", name));
@@ -1619,6 +1661,8 @@ struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name)
return NULL;
}
+ ctdb_db->persistent = persistent;
+
DLIST_ADD(ctdb->db_list, ctdb_db);
return ctdb_db;
diff --git a/ctdb/common/ctdb_util.c b/ctdb/common/ctdb_util.c
index dc8128aa20f..90489a7a716 100644
--- a/ctdb/common/ctdb_util.c
+++ b/ctdb/common/ctdb_util.c
@@ -165,13 +165,20 @@ void ctdb_reqid_remove(struct ctdb_context *ctdb, uint32_t reqid)
/*
form a ctdb_rec_data record from a key/data pair
+
+ note that header may be NULL. If not NULL then it is included in the data portion
+ of the record
*/
-struct ctdb_rec_data *ctdb_marshall_record(TALLOC_CTX *mem_ctx, uint32_t reqid, TDB_DATA key, TDB_DATA data)
+struct ctdb_rec_data *ctdb_marshall_record(TALLOC_CTX *mem_ctx, uint32_t reqid,
+ TDB_DATA key,
+ struct ctdb_ltdb_header *header,
+ TDB_DATA data)
{
size_t length;
struct ctdb_rec_data *d;
- length = offsetof(struct ctdb_rec_data, data) + key.dsize + data.dsize;
+ length = offsetof(struct ctdb_rec_data, data) + key.dsize +
+ data.dsize + (header?sizeof(*header):0);
d = (struct ctdb_rec_data *)talloc_size(mem_ctx, length);
if (d == NULL) {
return NULL;
@@ -179,9 +186,15 @@ struct ctdb_rec_data *ctdb_marshall_record(TALLOC_CTX *mem_ctx, uint32_t reqid,
d->length = length;
d->reqid = reqid;
d->keylen = key.dsize;
- d->datalen = data.dsize;
memcpy(&d->data[0], key.dptr, key.dsize);
- memcpy(&d->data[key.dsize], data.dptr, data.dsize);
+ if (header) {
+ d->datalen = data.dsize + sizeof(*header);
+ memcpy(&d->data[key.dsize], header, sizeof(*header));
+ memcpy(&d->data[key.dsize+sizeof(*header)], data.dptr, data.dsize);
+ } else {
+ d->datalen = data.dsize;
+ memcpy(&d->data[key.dsize], data.dptr, data.dsize);
+ }
return d;
}
diff --git a/ctdb/include/ctdb.h b/ctdb/include/ctdb.h
index 5248883ae7f..010efb5cfc9 100644
--- a/ctdb/include/ctdb.h
+++ b/ctdb/include/ctdb.h
@@ -134,6 +134,7 @@ int ctdb_set_transport(struct ctdb_context *ctdb, const char *transport);
set the directory for the local databases
*/
int ctdb_set_tdb_dir(struct ctdb_context *ctdb, const char *dir);
+int ctdb_set_tdb_dir_persistent(struct ctdb_context *ctdb, const char *dir);
/*
set some flags
@@ -167,7 +168,7 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork);
/*
attach to a ctdb database
*/
-struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name);
+struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, bool persistent);
/*
find an attached ctdb_db handle given a name
@@ -267,7 +268,10 @@ int ctdb_ctrl_setvnnmap(struct ctdb_context *ctdb,
*/
struct ctdb_dbid_map {
uint32_t num;
- uint32_t dbids[1];
+ struct ctdb_dbid {
+ uint32_t dbid;
+ bool persistent;
+ } dbs[1];
};
int ctdb_ctrl_getdbmap(struct ctdb_context *ctdb,
struct timeval timeout, uint32_t destnode,
@@ -295,7 +299,7 @@ int ctdb_ctrl_copydb(struct ctdb_context *ctdb,
int ctdb_ctrl_getdbpath(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, uint32_t dbid, TALLOC_CTX *mem_ctx, const char **path);
int ctdb_ctrl_getdbname(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, uint32_t dbid, TALLOC_CTX *mem_ctx, const char **name);
-int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, const char *name);
+int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, const char *name, bool persistent);
int ctdb_ctrl_process_exists(struct ctdb_context *ctdb, uint32_t destnode, pid_t pid);
diff --git a/ctdb/include/ctdb_private.h b/ctdb/include/ctdb_private.h
index e2be8eda709..7aa3431ba51 100644
--- a/ctdb/include/ctdb_private.h
+++ b/ctdb/include/ctdb_private.h
@@ -328,6 +328,7 @@ struct ctdb_context {
struct ctdb_address address;
const char *name;
const char *db_directory;
+ const char *db_directory_persistent;
const char *transport;
const char *logfile;
char *node_list_file;
@@ -365,6 +366,7 @@ struct ctdb_db_context {
struct ctdb_db_context *next, *prev;
struct ctdb_context *ctdb;
uint32_t db_id;
+ bool persistent;
const char *db_name;
const char *db_path;
struct tdb_wrap *ltdb;
@@ -465,6 +467,9 @@ enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS = 0,
CTDB_CONTROL_UNREGISTER_SERVER_ID = 58,
CTDB_CONTROL_CHECK_SERVER_ID = 59,
CTDB_CONTROL_GET_SERVER_ID_LIST = 60,
+ CTDB_CONTROL_DB_ATTACH_PERSISTENT = 61,
+ CTDB_CONTROL_PERSISTENT_STORE = 62,
+ CTDB_CONTROL_UPDATE_RECORD = 63,
};
/*
@@ -516,6 +521,15 @@ struct ctdb_control_tcp_vnn {
struct sockaddr_in dest;
};
+/*
+ persistent store control - update this record on all other nodes
+ */
+struct ctdb_control_persistent_store {
+ uint32_t db_id;
+ uint32_t len;
+ uint8_t data[1];
+};
+
/*
structure used for CTDB_SRVID_NODE_FLAGS_CHANGED
*/
@@ -857,7 +871,7 @@ int ctdb_daemon_send_control(struct ctdb_context *ctdb, uint32_t destnode,
void *private_data);
int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
- TDB_DATA *outdata);
+ TDB_DATA *outdata, bool persistent);
int ctdb_daemon_set_call(struct ctdb_context *ctdb, uint32_t db_id,
ctdb_fn_t fn, int id);
@@ -991,7 +1005,8 @@ int32_t ctdb_ltdb_enable_seqnum(struct ctdb_context *ctdb, uint32_t db_id);
int32_t ctdb_ltdb_update_seqnum(struct ctdb_context *ctdb, uint32_t db_id, uint32_t srcnode);
int32_t ctdb_ltdb_set_seqnum_frequency(struct ctdb_context *ctdb, uint32_t frequency);
-struct ctdb_rec_data *ctdb_marshall_record(TALLOC_CTX *mem_ctx, uint32_t reqid, TDB_DATA key, TDB_DATA data);
+struct ctdb_rec_data *ctdb_marshall_record(TALLOC_CTX *mem_ctx, uint32_t reqid,
+ TDB_DATA key, struct ctdb_ltdb_header *, TDB_DATA data);
int32_t ctdb_control_pull_db(struct ctdb_context *ctdb, TDB_DATA indata, TDB_DATA *outdata);
int32_t ctdb_control_push_db(struct ctdb_context *ctdb, TDB_DATA indata);
@@ -1146,4 +1161,14 @@ int32_t ctdb_control_unregister_server_id(struct ctdb_context *ctdb,
int32_t ctdb_control_get_server_id_list(struct ctdb_context *ctdb,
TDB_DATA *outdata);
+int ctdb_attach_persistent(struct ctdb_context *ctdb);
+
+int32_t ctdb_control_persistent_store(struct ctdb_context *ctdb,
+ struct ctdb_req_control *c,
+ TDB_DATA recdata, bool *async_reply);
+int32_t ctdb_control_update_record(struct ctdb_context *ctdb,
+ struct ctdb_req_control *c, TDB_DATA recdata,
+ bool *async_reply);
+
+
#endif
diff --git a/ctdb/server/ctdb_control.c b/ctdb/server/ctdb_control.c
index d831e019b5c..cd6c9c6e52a 100644
--- a/ctdb/server/ctdb_control.c
+++ b/ctdb/server/ctdb_control.c
@@ -175,7 +175,10 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
}
case CTDB_CONTROL_DB_ATTACH:
- return ctdb_control_db_attach(ctdb, indata, outdata);
+ return ctdb_control_db_attach(ctdb, indata, outdata, false);
+
+ case CTDB_CONTROL_DB_ATTACH_PERSISTENT:
+ return ctdb_control_db_attach(ctdb, indata, outdata, true);
case CTDB_CONTROL_SET_CALL: {
struct ctdb_control_set_call *sc =
@@ -312,6 +315,12 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
CHECK_CONTROL_DATA_SIZE(0);
return ctdb_control_get_server_id_list(ctdb, outdata);
+ case CTDB_CONTROL_PERSISTENT_STORE:
+ return ctdb_control_persistent_store(ctdb, c, indata, async_reply);
+
+ case CTDB_CONTROL_UPDATE_RECORD:
+ return ctdb_control_update_record(ctdb, c, indata, async_reply);
+
default:
DEBUG(0,(__location__ " Unknown CTDB control opcode %u\n", opcode));
return -1;
diff --git a/ctdb/server/ctdb_daemon.c b/ctdb/server/ctdb_daemon.c
index 22b13455e82..27b18ccba11 100644
--- a/ctdb/server/ctdb_daemon.c
+++ b/ctdb/server/ctdb_daemon.c
@@ -92,56 +92,6 @@ static void ctdb_start_transport(struct ctdb_context *ctdb, int status, void *p)
ctdb_start_tcp_tickle_update(ctdb);
}
-/* go into main ctdb loop */
-static void ctdb_main_loop(struct ctdb_context *ctdb)
-{
- int ret = -1;
-
- if (strcmp(ctdb->transport, "tcp") == 0) {
- int ctdb_tcp_init(struct ctdb_context *);
- ret = ctdb_tcp_init(ctdb);
- }
-#ifdef USE_INFINIBAND
- if (strcmp(ctdb->transport, "ib") == 0) {
- int ctdb_ibw_init(struct ctdb_context *);
- ret = ctdb_ibw_init(ctdb);
- }
-#endif
- if (ret != 0) {
- DEBUG(0,("Failed to initialise transport '%s'\n", ctdb->transport));
- return;
- }
-
- /* initialise the transport */
- if (ctdb->methods->initialise(ctdb) != 0) {
- DEBUG(0,("transport failed to initialise!\n"));
- ctdb_fatal(ctdb, "transport failed to initialise");
- }
-
- /* tell all other nodes we've just started up */
- ctdb_daemon_send_control(ctdb, CTDB_BROADCAST_ALL,
- 0, CTDB_CONTROL_STARTUP, 0,
- CTDB_CTRL_FLAG_NOREPLY,
- tdb_null, NULL, NULL);
-
- /* release any IPs we hold from previous runs of the daemon */
- ctdb_release_all_ips(ctdb);
-
- ret = ctdb_event_script_callback(ctdb, timeval_zero(), ctdb,
- ctdb_start_transport, NULL, "startup");
- if (ret != 0) {
- DEBUG(0,("Failed startup event script\n"));
- return;
- }
-
- /* go into a wait loop to allow other nodes to complete */
- event_loop_wait(ctdb->ev);
-
- DEBUG(0,("event_loop_wait() returned. this should not happen\n"));
- exit(1);
-}
-
-
static void block_signal(int signum)
{
struct sigaction act;
@@ -626,7 +576,7 @@ static void print_exit_message(void)
*/
int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork)
{
- int res;
+ int res, ret = -1;
struct fd_event *fde;
const char *domain_socket_name;
@@ -665,23 +615,66 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork)
ctdb->ev = event_context_init(NULL);
- /* start frozen, then let the first election sort things out */
- if (!ctdb_blocking_freeze(ctdb)) {
- DEBUG(0,("Failed to get initial freeze\n"));
- exit(12);
- }
-
/* force initial recovery for election */
ctdb->recovery_mode = CTDB_RECOVERY_ACTIVE;
+ if (strcmp(ctdb->transport, "tcp") == 0) {
+ int ctdb_tcp_init(struct ctdb_context *);
+ ret = ctdb_tcp_init(ctdb);
+ }
+#ifdef USE_INFINIBAND
+ if (strcmp(ctdb->transport, "ib") == 0) {
+ int ctdb_ibw_init(struct ctdb_context *);
+ ret = ctdb_ibw_init(ctdb);
+ }
+#endif
+ if (ret != 0) {
+ DEBUG(0,("Failed to initialise transport '%s'\n", ctdb->transport));
+ return -1;
+ }
+
+ /* initialise the transport */
+ if (ctdb->methods->initialise(ctdb) != 0) {
+ DEBUG(0,("transport failed to initialise!\n"));
+ ctdb_fatal(ctdb, "transport failed to initialise");
+ }
+
+ /* attach to any existing persistent databases */
+ if (ctdb_attach_persistent(ctdb) != 0) {
+ ctdb_fatal(ctdb, "Failed to attach to persistent databases\n");
+ }
+
+ /* start frozen, then let the first election sort things out */
+ if (!ctdb_blocking_freeze(ctdb)) {
+ ctdb_fatal(ctdb, "Failed to get initial freeze\n");
+ }
+
/* now start accepting clients, only can do this once frozen */
fde = event_add_fd(ctdb->ev, ctdb, ctdb->daemon.sd,
EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
ctdb_accept_client, ctdb);
- ctdb_main_loop(ctdb);
+ /* tell all other nodes we've just started up */
+ ctdb_daemon_send_control(ctdb, CTDB_BROADCAST_ALL,
+ 0, CTDB_CONTROL_STARTUP, 0,
+ CTDB_CTRL_FLAG_NOREPLY,
+ tdb_null, NULL, NULL);
- return 0;
+ /* release any IPs we hold from previous runs of the daemon */
+ ctdb_release_all_ips(ctdb);
+
+ ret = ctdb_event_script_callback(ctdb, timeval_zero(), ctdb,
+ ctdb_start_transport, NULL, "startup");
+ if (ret != 0) {
+ DEBUG(0,("Failed startup event script\n"));
+ return -1;
+ }
+
+ /* go into a wait loop to allow other nodes to complete */
+ event_loop_wait(ctdb->ev);
+
+ DEBUG(0,("event_loop_wait() returned. this should not happen\n"));
+ exit(1);
}
/*
diff --git a/ctdb/server/ctdb_ltdb_server.c b/ctdb/server/ctdb_ltdb_server.c
index 5097481bd0a..6232fbcae1a 100644
--- a/ctdb/server/ctdb_ltdb_server.c
+++ b/ctdb/server/ctdb_ltdb_server.c
@@ -22,6 +22,7 @@
#include "lib/tdb/include/tdb.h"
#include "system/network.h"
#include "system/filesys.h"
+#include "system/dir.h"
#include "../include/ctdb_private.h"
#include "db_wrap.h"
#include "lib/util/dlinklist.h"
@@ -186,36 +187,16 @@ static void ctdb_check_db_empty(struct ctdb_db_context *ctdb_db)
}
}
+
/*
- a client has asked to attach a new database
+ attach to a database, handling both persistent and non-persistent databases
+ return 0 on success, -1 on failure
*/
-int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
- TDB_DATA *outdata)
+static int ctdb_local_attach(struct ctdb_context *ctdb, const char *db_name, bool persistent)
{
- const char *db_name = (const char *)indata.dptr;
struct ctdb_db_context *ctdb_db, *tmp_db;
- struct ctdb_node *node = ctdb->nodes[ctdb->pnn];
int ret;
-
- /* If the node is inactive it is not part of the cluster
- and we should not allow clients to attach to any
- databases
- */
- if (node->flags & NODE_FLAGS_INACTIVE) {
- DEBUG(0,("DB Attach to database %s refused since node is inactive (disconnected or banned)\n", db_name));
- return -1;
- }
-
-
- /* see if we already have this name */
- for (tmp_db=ctdb->db_list;tmp_db;tmp_db=tmp_db->next) {
- if (strcmp(db_name, tmp_db->db_name) == 0) {
- /* this is not an error */
- outdata->dptr = (uint8_t *)&tmp_db->db_id;
- outdata->dsize = sizeof(tmp_db->db_id);
- return 0;
- }
- }
+ struct TDB_DATA key;
ctdb_db = talloc_zero(ctdb, struct ctdb_db_context);
CTDB_NO_MEMORY(ctdb, ctdb_db);
@@ -224,10 +205,10 @@ int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
ctdb_db->db_name = talloc_strdup(ctdb_db, db_name);
CTDB_NO_MEMORY(ctdb, ctdb_db->db_name);
- ctdb_db->db_id = ctdb_hash(&indata);
-
- outdata->dptr = (uint8_t *)&ctdb_db->db_id;
- outdata->dsize = sizeof(ctdb_db->db_id);
+ key.dsize = strlen(db_name)+1;
+ key.dptr = discard_const(db_name);
+ ctdb_db->db_id = ctdb_hash(&key);
+ ctdb_db->persistent = persistent;
/* check for hash collisions */
for (tmp_db=ctdb->db_list;tmp_db;tmp_db=tmp_db->next) {
@@ -251,21 +232,31 @@ int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
return -1;
}
+ if (persistent && mkdir(ctdb->db_directory_persistent, 0700) == -1 && errno != EEXIST) {
+ DEBUG(0,(__location__ " Unable to create ctdb persistent directory '%s'\n",
+ ctdb->db_directory_persistent));
+ talloc_free(ctdb_db);
+ return -1;
+ }
+
/* open the database */
ctdb_db->db_path = talloc_asprintf(ctdb_db, "%s/%s.%u",
- ctdb->db_directory,
+ persistent?ctdb->db_directory_persistent:ctdb->db_directory,
db_name, ctdb->pnn);
ctdb_db->ltdb = tdb_wrap_open(ctdb, ctdb_db->db_path,
ctdb->tunable.database_hash_size,
- TDB_CLEAR_IF_FIRST, O_CREAT|O_RDWR, 0666);
+ persistent?TDB_DEFAULT:TDB_CLEAR_IF_FIRST,
+ O_CREAT|O_RDWR, 0666);
if (ctdb_db->ltdb == NULL) {
DEBUG(0,("Failed to open tdb '%s'\n", ctdb_db->db_path));
talloc_free(ctdb_db);
return -1;
}
- ctdb_check_db_empty(ctdb_db);
+ if (!persistent) {
+ ctdb_check_db_empty(ctdb_db);
+ }
DLIST_ADD(ctdb->db_list, ctdb_db);
@@ -290,18 +281,113 @@ int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
talloc_free(ctdb_db);
return -1;
}
+
+ DEBUG(1,("Attached to database '%s'\n", ctdb_db->db_path));
+ /* success */
+ return 0;
+}
+
+
+/*
+ a client has asked to attach a new database
+ */
+int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
+ TDB_DATA *outdata, bool persistent)
+{
+ const char *db_name = (const char *)indata.dptr;
+ struct ctdb_db_context *db;
+ struct ctdb_node *node = ctdb->nodes[ctdb->pnn];
+
+ /* If the node is inactive it is not part of the cluster
+ and we should not allow clients to attach to any
+ databases
+ */
+ if (node->flags & NODE_FLAGS_INACTIVE) {
+ DEBUG(0,("DB Attach to database %s refused since node is inactive (disconnected or banned)\n", db_name));
+ return -1;
+ }
+
+
+ /* see if we already have this name */
+ db = ctdb_db_handle(ctdb, db_name);
+ if (db) {
+ outdata->dptr = (uint8_t *)&db->db_id;
+ outdata->dsize = sizeof(db->db_id);
+ return 0;
+ }
+
+ if (ctdb_local_attach(ctdb, db_name, persistent) != 0) {
+ return -1;
+ }
+
+ db = ctdb_db_handle(ctdb, db_name);
+ if (!db) {
+ DEBUG(0,("Failed to find db handle for name '%s'\n", db_name));
+ return -1;
+ }
+
+ outdata->dptr = (uint8_t *)&db->db_id;
+ outdata->dsize = sizeof(db->db_id);
+
/* tell all the other nodes about this database */
ctdb_daemon_send_control(ctdb, CTDB_BROADCAST_ALL, 0,
CTDB_CONTROL_DB_ATTACH, 0, CTDB_CTRL_FLAG_NOREPLY,
indata, NULL, NULL);
- DEBUG(1,("Attached to database '%s'\n", ctdb_db->db_path));
-
/* success */
return 0;
}
+
+/*
+ attach to all existing persistent databases
+ */
+int ctdb_attach_persistent(struct ctdb_context *ctdb)
+{
+ DIR *d;
+ struct dirent *de;
+
+ /* open the persistent db directory and scan it for files */
+ d = opendir(ctdb->db_directory_persistent);
+ if (d == NULL) {
+ return 0;
+ }
+
+ while ((de=readdir(d))) {
+ char *p, *s;
+ size_t len = strlen(de->d_name);
+ uint32_t node;
+
+ s = talloc_strdup(ctdb, de->d_name);
+ CTDB_NO_MEMORY(ctdb, s);
+
+ /* only accept names ending in .tdb */
+ p = strstr(s, ".tdb.");
+ if (len < 7 || p == NULL) {
+ talloc_free(s);
+ continue;
+ }
+ if (sscanf(p+5, "%u", &node) != 1 || node != ctdb->pnn) {
+ talloc_free(s);
+ continue;
+ }
+ p[4] = 0;
+
+ if (ctdb_local_attach(ctdb, s, true) != 0) {
+ DEBUG(0,("Failed to attach to persistent database '%s'\n", de->d_name));
+ closedir(d);
+ talloc_free(s);
+ return -1;
+ }
+ DEBUG(0,("Attached to persistent database %s\n", s));
+
+ talloc_free(s);
+ }
+ closedir(d);
+ return 0;
+}
+
/*
called when a broadcast seqnum update comes in
*/
diff --git a/ctdb/server/ctdb_persistent.c b/ctdb/server/ctdb_persistent.c
new file mode 100644
index 00000000000..7d9bd48a33a
--- /dev/null
+++ b/ctdb/server/ctdb_persistent.c
@@ -0,0 +1,262 @@
+/*
+ persistent store logic
+
+ Copyright (C) Andrew Tridgell 2007
+ Copyright (C) Ronnie Sahlberg 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "system/wait.h"
+#include "db_wrap.h"
+#include "lib/tdb/include/tdb.h"
+#include "../include/ctdb_private.h"
+
+struct ctdb_persistent_state {
+ struct ctdb_context *ctdb;
+ struct ctdb_req_control *c;
+ const char *errormsg;
+ uint32_t num_pending;
+ int32_t status;
+};
+
+/*
+ called when a node has acknowledged a ctdb_control_update_record call
+ */
+static void ctdb_persistent_callback(struct ctdb_context *ctdb,
+ int32_t status, TDB_DATA data,
+ const char *errormsg,
+ void *private_data)
+{
+ struct ctdb_persistent_state *state = talloc_get_type(private_data,
+ struct ctdb_persistent_state);
+ if (status != 0) {
+ DEBUG(0,("ctdb_persistent_callback failed with status %d (%s)\n",
+ status, errormsg));
+ state->status = status;
+ state->errormsg = errormsg;
+ }
+ state->num_pending--;
+ if (state->num_pending == 0) {
+ ctdb_request_control_reply(state->ctdb, state->c, NULL, state->status, state->errormsg);
+ talloc_free(state);
+ }
+}
+
+
+/*
+ store a persistent record - called from a ctdb client when it has updated
+ a record in a persistent database. The client will have the record
+ locked for the duration of this call. The client is the dmaster when
+ this call is made
+ */
+int32_t ctdb_control_persistent_store(struct ctdb_context *ctdb,
+ struct ctdb_req_control *c,
+ TDB_DATA recdata, bool *async_reply)
+{
+ struct ctdb_persistent_state *state;
+ int i;
+
+ state = talloc_zero(c, struct ctdb_persistent_state);
+ CTDB_NO_MEMORY(ctdb, state);
+
+ state->ctdb = ctdb;
+ state->c = c;
+
+ for (i=0;inum_nodes;i++) {
+ struct ctdb_node *node = ctdb->nodes[i];
+ int ret;
+
+ /* only send to active nodes */
+ if (node->flags & NODE_FLAGS_INACTIVE) {
+ continue;
+ }
+
+ /* don't send to ourselves */
+ if (node->pnn == ctdb->pnn) {
+ continue;
+ }
+
+ ret = ctdb_daemon_send_control(ctdb, node->pnn, 0, CTDB_CONTROL_UPDATE_RECORD,
+ c->client_id, 0, recdata,
+ ctdb_persistent_callback, state);
+ if (ret == -1) {
+ DEBUG(0,("Unable to send CTDB_CONTROL_UPDATE_RECORD to pnn %u\n", node->pnn));
+ talloc_free(state);
+ return -1;
+ }
+
+ state->num_pending++;
+ }
+
+ if (state->num_pending == 0) {
+ talloc_free(state);
+ return 0;
+ }
+
+ /* we need to wait for the replies */
+ *async_reply = true;
+ return 0;
+}
+
+
+struct ctdb_persistent_lock_state {
+ struct ctdb_db_context *ctdb_db;
+ TDB_DATA key;
+ TDB_DATA data;
+ struct ctdb_ltdb_header *header;
+ struct tdb_context *tdb;
+ struct ctdb_req_control *c;
+};
+
+
+/*
+ called with a lock held in the current process
+ */
+static int ctdb_persistent_store(struct ctdb_persistent_lock_state *state)
+{
+ struct ctdb_ltdb_header oldheader;
+ int ret;
+
+ /* fetch the old header and ensure the rsn is less than the new rsn */
+ ret = ctdb_ltdb_fetch(state->ctdb_db, state->key, &oldheader, NULL, NULL);
+ if (ret != 0) {
+ DEBUG(0,("Failed to fetch old record for db_id 0x%08x in ctdb_persistent_store\n",
+ state->ctdb_db->db_id));
+ return -1;
+ }
+
+ if (oldheader.rsn >= state->header->rsn) {
+ DEBUG(0,("existing header for db_id 0x%08x has larger RSN %llu than new RSN %llu in ctdb_persistent_store\n",
+ state->ctdb_db->db_id, oldheader.rsn, state->header->rsn));
+ return -1;
+ }
+
+ ret = ctdb_ltdb_store(state->ctdb_db, state->key, state->header, state->data);
+ if (ret != 0) {
+ DEBUG(0,("Failed to store record for db_id 0x%08x in ctdb_persistent_store\n",
+ state->ctdb_db->db_id));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ called when we get the lock on the given record
+ at this point the lockwait child holds a lock on our behalf
+ */
+static void ctdb_persistent_lock_callback(void *private_data)
+{
+ struct ctdb_persistent_lock_state *state = talloc_get_type(private_data,
+ struct ctdb_persistent_lock_state);
+ int ret;
+
+ ret = tdb_chainlock_mark(state->tdb, state->key);
+ if (ret != 0) {
+ DEBUG(0,("Failed to mark lock in ctdb_persistent_lock_callback\n"));
+ ctdb_request_control_reply(state->ctdb_db->ctdb, state->c, NULL, ret, NULL);
+ return;
+ }
+
+ ret = ctdb_persistent_store(state);
+ ctdb_request_control_reply(state->ctdb_db->ctdb, state->c, NULL, ret, NULL);
+ tdb_chainlock_unmark(state->tdb, state->key);
+}
+
+/*
+ called if our lockwait child times out
+ */
+static void ctdb_persistent_lock_timeout(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct ctdb_persistent_lock_state *state = talloc_get_type(private_data,
+ struct ctdb_persistent_lock_state);
+ ctdb_request_control_reply(state->ctdb_db->ctdb, state->c, NULL, -1, "timeout in ctdb_persistent_lock");
+ talloc_free(state);
+}
+
+
+/*
+ update a record on this node if the new record has a higher rsn than the
+ current record
+ */
+int32_t ctdb_control_update_record(struct ctdb_context *ctdb,
+ struct ctdb_req_control *c, TDB_DATA recdata,
+ bool *async_reply)
+{
+ struct ctdb_rec_data *rec = (struct ctdb_rec_data *)&recdata.dptr[0];
+ int ret;
+ struct ctdb_db_context *ctdb_db;
+ uint32_t db_id = rec->reqid;
+ struct lockwait_handle *handle;
+ struct ctdb_persistent_lock_state *state;
+
+ if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) {
+ DEBUG(0,("rejecting ctdb_control_update_record when recovery active\n"));
+ return -1;
+ }
+
+ ctdb_db = find_ctdb_db(ctdb, db_id);
+ if (ctdb_db == NULL) {
+ DEBUG(0,("Unknown database 0x%08x in ctdb_control_update_record\n", db_id));
+ return -1;
+ }
+
+ state = talloc(c, struct ctdb_persistent_lock_state);
+ CTDB_NO_MEMORY(ctdb, state);
+
+ state->ctdb_db = ctdb_db;
+ state->c = c;
+ state->tdb = ctdb_db->ltdb->tdb;
+ state->key.dptr = &rec->data[0];
+ state->key.dsize = rec->keylen;
+ state->data.dptr = &rec->data[rec->keylen];
+ state->data.dsize = rec->datalen;
+
+ if (state->data.dsize < sizeof(struct ctdb_ltdb_header)) {
+ DEBUG(0,("Invalid data size %u in ctdb_control_update_record\n", state->data.dsize));
+ return -1;
+ }
+
+ state->header = (struct ctdb_ltdb_header *)&state->data.dptr[0];
+ state->data.dptr += sizeof(struct ctdb_ltdb_header);
+ state->data.dsize -= sizeof(struct ctdb_ltdb_header);
+
+ /* try and do it without a lockwait */
+ ret = tdb_chainlock_nonblock(state->tdb, state->key);
+ if (ret == 0) {
+ ret = ctdb_persistent_store(state);
+ tdb_chainunlock(state->tdb, state->key);
+ return ret;
+ }
+
+ /* wait until we have a lock on this record */
+ handle = ctdb_lockwait(ctdb_db, state->key, ctdb_persistent_lock_callback, state);
+ if (handle == NULL) {
+ DEBUG(0,("Failed to setup lockwait handler in ctdb_control_update_record\n"));
+ return -1;
+ }
+
+ *async_reply = true;
+
+ event_add_timed(ctdb->ev, state, timeval_current_ofs(ctdb->tunable.control_timeout, 0),
+ ctdb_persistent_lock_timeout, state);
+
+ return 0;
+}
diff --git a/ctdb/server/ctdb_recover.c b/ctdb/server/ctdb_recover.c
index c79c85d4e7d..f89980880d2 100644
--- a/ctdb/server/ctdb_recover.c
+++ b/ctdb/server/ctdb_recover.c
@@ -125,7 +125,7 @@ ctdb_control_getdbmap(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA indat
}
- outdata->dsize = offsetof(struct ctdb_dbid_map, dbids) + 4*len;
+ outdata->dsize = offsetof(struct ctdb_dbid_map, dbs) + sizeof(dbid_map->dbs[0])*len;
outdata->dptr = (unsigned char *)talloc_zero_size(outdata, outdata->dsize);
if (!outdata->dptr) {
DEBUG(0, (__location__ " Failed to allocate dbmap array\n"));
@@ -134,8 +134,9 @@ ctdb_control_getdbmap(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA indat
dbid_map = (struct ctdb_dbid_map *)outdata->dptr;
dbid_map->num = len;
- for(i=0,ctdb_db=ctdb->db_list;ctdb_db;i++,ctdb_db=ctdb_db->next){
- dbid_map->dbids[i] = ctdb_db->db_id;
+ for (i=0,ctdb_db=ctdb->db_list;ctdb_db;i++,ctdb_db=ctdb_db->next){
+ dbid_map->dbs[i].dbid = ctdb_db->db_id;
+ dbid_map->dbs[i].persistent = ctdb_db->persistent;
}
return 0;
@@ -254,7 +255,7 @@ int32_t ctdb_control_pull_db(struct ctdb_context *ctdb, TDB_DATA indata, TDB_DAT
for (i=0;icount;i++) {
struct ctdb_rec_data *rec;
- rec = ctdb_marshall_record(outdata, 0, params.recs[i].key, params.recs[i].data);
+ rec = ctdb_marshall_record(outdata, 0, params.recs[i].key, NULL, params.recs[i].data);
reply = talloc_realloc_size(outdata, reply, rec->length + len);
memcpy(len+(uint8_t *)reply, rec, rec->length);
len += rec->length;
diff --git a/ctdb/server/ctdb_recoverd.c b/ctdb/server/ctdb_recoverd.c
index 7d16b18015c..703690f0536 100644
--- a/ctdb/server/ctdb_recoverd.c
+++ b/ctdb/server/ctdb_recoverd.c
@@ -301,7 +301,7 @@ static int create_missing_remote_databases(struct ctdb_context *ctdb, struct ctd
for (i=0;inum;i++) {
- if (dbmap->dbids[db] == remote_dbmap->dbids[i]) {
+ if (dbmap->dbs[db].dbid == remote_dbmap->dbs[i].dbid) {
break;
}
}
@@ -310,12 +310,14 @@ static int create_missing_remote_databases(struct ctdb_context *ctdb, struct ctd
continue;
}
/* ok so we need to create this database */
- ctdb_ctrl_getdbname(ctdb, CONTROL_TIMEOUT(), pnn, dbmap->dbids[db], mem_ctx, &name);
+ ctdb_ctrl_getdbname(ctdb, CONTROL_TIMEOUT(), pnn, dbmap->dbs[db].dbid,
+ mem_ctx, &name);
if (ret != 0) {
DEBUG(0, (__location__ " Unable to get dbname from node %u\n", pnn));
return -1;
}
- ctdb_ctrl_createdb(ctdb, CONTROL_TIMEOUT(), nodemap->nodes[j].pnn, mem_ctx, name);
+ ctdb_ctrl_createdb(ctdb, CONTROL_TIMEOUT(), nodemap->nodes[j].pnn,
+ mem_ctx, name, dbmap->dbs[db].persistent);
if (ret != 0) {
DEBUG(0, (__location__ " Unable to create remote db:%s\n", name));
return -1;
@@ -359,7 +361,7 @@ static int create_missing_local_databases(struct ctdb_context *ctdb, struct ctdb
const char *name;
for (i=0;i<(*dbmap)->num;i++) {
- if (remote_dbmap->dbids[db] == (*dbmap)->dbids[i]) {
+ if (remote_dbmap->dbs[db].dbid == (*dbmap)->dbs[i].dbid) {
break;
}
}
@@ -371,13 +373,14 @@ static int create_missing_local_databases(struct ctdb_context *ctdb, struct ctdb
rebuild dbmap
*/
ctdb_ctrl_getdbname(ctdb, CONTROL_TIMEOUT(), nodemap->nodes[j].pnn,
- remote_dbmap->dbids[db], mem_ctx, &name);
+ remote_dbmap->dbs[db].dbid, mem_ctx, &name);
if (ret != 0) {
DEBUG(0, (__location__ " Unable to get dbname from node %u\n",
nodemap->nodes[j].pnn));
return -1;
}
- ctdb_ctrl_createdb(ctdb, CONTROL_TIMEOUT(), pnn, mem_ctx, name);
+ ctdb_ctrl_createdb(ctdb, CONTROL_TIMEOUT(), pnn, mem_ctx, name,
+ remote_dbmap->dbs[db].persistent);
if (ret != 0) {
DEBUG(0, (__location__ " Unable to create local db:%s\n", name));
return -1;
@@ -416,7 +419,7 @@ static int pull_all_remote_databases(struct ctdb_context *ctdb, struct ctdb_node
continue;
}
ret = ctdb_ctrl_copydb(ctdb, CONTROL_TIMEOUT(), nodemap->nodes[j].pnn,
- pnn, dbmap->dbids[i], CTDB_LMASTER_ANY, mem_ctx);
+ pnn, dbmap->dbs[i].dbid, CTDB_LMASTER_ANY, mem_ctx);
if (ret != 0) {
DEBUG(0, (__location__ " Unable to copy db from node %u to node %u\n",
nodemap->nodes[j].pnn, pnn));
@@ -444,9 +447,11 @@ static int update_dmaster_on_all_databases(struct ctdb_context *ctdb, struct ctd
if (nodemap->nodes[j].flags & NODE_FLAGS_INACTIVE) {
continue;
}
- ret = ctdb_ctrl_setdmaster(ctdb, CONTROL_TIMEOUT(), nodemap->nodes[j].pnn, ctdb, dbmap->dbids[i], pnn);
+ ret = ctdb_ctrl_setdmaster(ctdb, CONTROL_TIMEOUT(), nodemap->nodes[j].pnn,
+ ctdb, dbmap->dbs[i].dbid, pnn);
if (ret != 0) {
- DEBUG(0, (__location__ " Unable to set dmaster for node %u db:0x%08x\n", nodemap->nodes[j].pnn, dbmap->dbids[i]));
+ DEBUG(0, (__location__ " Unable to set dmaster for node %u db:0x%08x\n",
+ nodemap->nodes[j].pnn, dbmap->dbs[i].dbid));
return -1;
}
}
@@ -537,7 +542,7 @@ static int vacuum_all_databases(struct ctdb_context *ctdb, struct ctdb_node_map
/* update dmaster to point to this node for all databases/nodes */
for (i=0;inum;i++) {
- if (vacuum_db(ctdb, dbmap->dbids[i], nodemap) != 0) {
+ if (vacuum_db(ctdb, dbmap->dbs[i].dbid, nodemap) != 0) {
return -1;
}
}
@@ -565,7 +570,7 @@ static int push_all_local_databases(struct ctdb_context *ctdb, struct ctdb_node_
continue;
}
ret = ctdb_ctrl_copydb(ctdb, CONTROL_TIMEOUT(), pnn, nodemap->nodes[j].pnn,
- dbmap->dbids[i], CTDB_LMASTER_ANY, mem_ctx);
+ dbmap->dbs[i].dbid, CTDB_LMASTER_ANY, mem_ctx);
if (ret != 0) {
DEBUG(0, (__location__ " Unable to copy db from node %u to node %u\n",
pnn, nodemap->nodes[j].pnn));
@@ -919,6 +924,9 @@ static int do_recovery(struct ctdb_recoverd *rec,
DEBUG(1, (__location__ " Recovery - done takeover\n"));
}
+ for (i=0;inum;i++) {
+ DEBUG(0,("Recovered database with db_id 0x%08x\n", dbmap->dbs[i].dbid));
+ }
/* disable recovery mode */
ret = set_recovery_mode(ctdb, nodemap, CTDB_RECOVERY_NORMAL);
@@ -1805,8 +1813,6 @@ static void ctdb_recoverd_parent(struct event_context *ev, struct fd_event *fde,
_exit(1);
}
-
-
/*
startup the recovery daemon as a child of the main ctdb daemon
*/
diff --git a/ctdb/server/ctdb_server.c b/ctdb/server/ctdb_server.c
index a9ae79bc7ec..afa1b317f24 100644
--- a/ctdb/server/ctdb_server.c
+++ b/ctdb/server/ctdb_server.c
@@ -81,6 +81,18 @@ int ctdb_set_tdb_dir(struct ctdb_context *ctdb, const char *dir)
return 0;
}
+/*
+ set the directory for the persistent databases
+*/
+int ctdb_set_tdb_dir_persistent(struct ctdb_context *ctdb, const char *dir)
+{
+ ctdb->db_directory_persistent = talloc_strdup(ctdb, dir);
+ if (ctdb->db_directory_persistent == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
/*
add a node to the list of active nodes
*/
diff --git a/ctdb/server/ctdb_traverse.c b/ctdb/server/ctdb_traverse.c
index a18ce654d3f..c48387af34c 100644
--- a/ctdb/server/ctdb_traverse.c
+++ b/ctdb/server/ctdb_traverse.c
@@ -95,7 +95,7 @@ static int ctdb_traverse_local_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DAT
return 0;
}
- d = ctdb_marshall_record(h, 0, key, data);
+ d = ctdb_marshall_record(h, 0, key, NULL, data);
if (d == NULL) {
/* error handling is tricky in this child code .... */
return -1;
@@ -280,7 +280,7 @@ static void traverse_all_callback(void *p, TDB_DATA key, TDB_DATA data)
struct ctdb_rec_data *d;
TDB_DATA cdata;
- d = ctdb_marshall_record(state, state->reqid, key, data);
+ d = ctdb_marshall_record(state, state->reqid, key, NULL, data);
if (d == NULL) {
/* darn .... */
DEBUG(0,("Out of memory in traverse_all_callback\n"));
@@ -408,7 +408,7 @@ static void traverse_start_callback(void *p, TDB_DATA key, TDB_DATA data)
state = talloc_get_type(p, struct traverse_start_state);
- d = ctdb_marshall_record(state, state->reqid, key, data);
+ d = ctdb_marshall_record(state, state->reqid, key, NULL, data);
if (d == NULL) {
return;
}
diff --git a/ctdb/server/ctdbd.c b/ctdb/server/ctdbd.c
index 92f4b86c537..6ba76ffc953 100644
--- a/ctdb/server/ctdbd.c
+++ b/ctdb/server/ctdbd.c
@@ -46,6 +46,7 @@ static struct {
const char *logfile;
const char *recovery_lock_file;
const char *db_dir;
+ const char *db_dir_persistent;
const char *public_interface;
int no_setsched;
} options = {
@@ -54,6 +55,7 @@ static struct {
.event_script_dir = ETCDIR "/ctdb/events.d",
.logfile = VARDIR "/log/log.ctdb",
.db_dir = VARDIR "/ctdb",
+ .db_dir_persistent = VARDIR "/ctdb/persistent",
};
@@ -108,6 +110,7 @@ int main(int argc, const char *argv[])
{ "listen", 0, POPT_ARG_STRING, &options.myaddress, 0, "address to listen on", "address" },
{ "transport", 0, POPT_ARG_STRING, &options.transport, 0, "protocol transport", NULL },
{ "dbdir", 0, POPT_ARG_STRING, &options.db_dir, 0, "directory for the tdb files", NULL },
+ { "dbdir-persistent", 0, POPT_ARG_STRING, &options.db_dir_persistent, 0, "directory for persistent tdb files", NULL },
{ "reclock", 0, POPT_ARG_STRING, &options.recovery_lock_file, 0, "location of recovery lock file", "filename" },
{ "nosetsched", 0, POPT_ARG_NONE, &options.no_setsched, 0, "disable setscheduler SCHED_FIFO call", NULL },
POPT_TABLEEND
@@ -199,6 +202,13 @@ int main(int argc, const char *argv[])
exit(1);
}
}
+ if (options.db_dir_persistent) {
+ ret = ctdb_set_tdb_dir_persistent(ctdb, options.db_dir_persistent);
+ if (ret == -1) {
+ DEBUG(0,("ctdb_set_tdb_dir_persistent failed - %s\n", ctdb_errstr(ctdb)));
+ exit(1);
+ }
+ }
if (options.public_interface) {
ctdb->default_public_interface = talloc_strdup(ctdb, options.public_interface);
diff --git a/ctdb/tests/ctdb_bench.c b/ctdb/tests/ctdb_bench.c
index 1eefe9ca4f4..c14ef2b3cd5 100644
--- a/ctdb/tests/ctdb_bench.c
+++ b/ctdb/tests/ctdb_bench.c
@@ -201,7 +201,7 @@ int main(int argc, const char *argv[])
&cluster_ready);
/* attach to a specific database */
- ctdb_db = ctdb_attach(ctdb, "test.tdb");
+ ctdb_db = ctdb_attach(ctdb, "test.tdb", false);
if (!ctdb_db) {
printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
exit(1);
diff --git a/ctdb/tests/ctdb_fetch.c b/ctdb/tests/ctdb_fetch.c
index 1ac87017bee..56eb244a704 100644
--- a/ctdb/tests/ctdb_fetch.c
+++ b/ctdb/tests/ctdb_fetch.c
@@ -219,7 +219,7 @@ int main(int argc, const char *argv[])
&cluster_ready);
/* attach to a specific database */
- ctdb_db = ctdb_attach(ctdb, "test.tdb");
+ ctdb_db = ctdb_attach(ctdb, "test.tdb", false);
if (!ctdb_db) {
printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
exit(1);
diff --git a/ctdb/tests/ctdb_persistent.c b/ctdb/tests/ctdb_persistent.c
new file mode 100644
index 00000000000..f46fd4017a8
--- /dev/null
+++ b/ctdb/tests/ctdb_persistent.c
@@ -0,0 +1,139 @@
+/*
+ simple tool to test persistent databases
+
+ Copyright (C) Andrew Tridgell 2006-2007
+ Copyright (c) Ronnie sahlberg 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "popt.h"
+#include "cmdline.h"
+
+#include
+#include
+
+static void test_store_records(struct ctdb_context *ctdb, struct event_context *ev)
+{
+ TDB_DATA key, data;
+ struct ctdb_db_context *ctdb_db;
+ TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+ int ret, i;
+ struct ctdb_record_handle *h;
+ unsigned node=0, count=0;
+
+ ctdb_db = ctdb_db_handle(ctdb, "persistent.tdb");
+
+ key.dptr = discard_const("testkey");
+ key.dsize = strlen((const char *)key.dptr)+1;
+
+ for (i=0;i<10;i++) {
+ h = ctdb_fetch_lock(ctdb_db, tmp_ctx, key, &data);
+ if (h == NULL) {
+ printf("Failed to fetch record '%s' on node %d\n",
+ (const char *)key.dptr, ctdb_get_pnn(ctdb));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ printf("Current value: %*.*s\n", data.dsize, data.dsize, data.dptr);
+
+ if (data.dsize != 0) {
+ if (sscanf((char *)data.dptr, "Node %u Count %u", &node, &count) != 2) {
+ printf("Badly formatted node data!\n");
+ exit(1);
+ }
+ }
+
+ node = ctdb_get_pnn(ctdb);
+ count++;
+
+ data.dptr = (uint8_t *)talloc_asprintf(h, "Node %u Count %u", node, count);
+ data.dsize = strlen((char *)data.dptr)+1;
+
+ ret = ctdb_record_store(h, data);
+ if (ret != 0) {
+ DEBUG(0,("Failed to store record\n"));
+ exit(1);
+ }
+ talloc_free(h);
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+/*
+ main program
+*/
+int main(int argc, const char *argv[])
+{
+ struct ctdb_context *ctdb;
+ struct ctdb_db_context *ctdb_db;
+
+ struct poptOption popt_options[] = {
+ POPT_AUTOHELP
+ POPT_CTDB_CMDLINE
+ POPT_TABLEEND
+ };
+ int opt;
+ const char **extra_argv;
+ int extra_argc = 0;
+ poptContext pc;
+ struct event_context *ev;
+
+ pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ default:
+ fprintf(stderr, "Invalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ exit(1);
+ }
+ }
+
+ /* setup the remaining options for the main program to use */
+ extra_argv = poptGetArgs(pc);
+ if (extra_argv) {
+ extra_argv++;
+ while (extra_argv[extra_argc]) extra_argc++;
+ }
+
+ ev = event_context_init(NULL);
+
+ ctdb = ctdb_cmdline_client(ev);
+
+ /* attach to a specific database */
+ ctdb_db = ctdb_attach(ctdb, "persistent.tdb", true);
+ if (!ctdb_db) {
+ printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
+ exit(1);
+ }
+
+ printf("Waiting for cluster\n");
+ while (1) {
+ uint32_t recmode=1;
+ ctdb_ctrl_getrecmode(ctdb, ctdb, timeval_zero(), CTDB_CURRENT_NODE, &recmode);
+ if (recmode == 0) break;
+ event_loop_once(ev);
+ }
+
+ printf("Starting test\n");
+ test_store_records(ctdb, ev);
+
+ return 0;
+}
diff --git a/ctdb/tests/ctdb_store.c b/ctdb/tests/ctdb_store.c
index 9d4b30c2a7f..45373dd28ab 100644
--- a/ctdb/tests/ctdb_store.c
+++ b/ctdb/tests/ctdb_store.c
@@ -136,7 +136,7 @@ int main(int argc, const char *argv[])
ctdb = ctdb_cmdline_client(ev);
/* attach to a specific database */
- ctdb_db = ctdb_attach(ctdb, "test.tdb");
+ ctdb_db = ctdb_attach(ctdb, "test.tdb", false);
if (!ctdb_db) {
printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
exit(1);
diff --git a/ctdb/tests/persistent.sh b/ctdb/tests/persistent.sh
new file mode 100755
index 00000000000..e53e11f99e8
--- /dev/null
+++ b/ctdb/tests/persistent.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+NUMNODES=2
+if [ $# -gt 0 ]; then
+ NUMNODES=$1
+fi
+
+rm -f nodes.txt
+for i in `seq 1 $NUMNODES`; do
+ echo 127.0.0.$i >> nodes.txt
+done
+
+tests/start_daemons.sh $NUMNODES nodes.txt || exit 1
+
+
+killall -9 -q ctdb_persistent
+for i in `seq 1 $NUMNODES`; do
+ $VALGRIND bin/ctdb_persistent --socket sock.$i $* &
+done
+wait
+
+echo "Shutting down"
+bin/ctdb shutdown -n all --socket=sock.1
+exit 0
diff --git a/ctdb/tests/start_daemons.sh b/ctdb/tests/start_daemons.sh
index f577ef42bee..966cc5dfd7d 100755
--- a/ctdb/tests/start_daemons.sh
+++ b/ctdb/tests/start_daemons.sh
@@ -7,7 +7,7 @@ shift
killall -q ctdbd
-CTDB_OPTIONS="--reclock=rec.lock --nlist $NODES --event-script-dir=tests/events.d --logfile=- --dbdir=test.db $*"
+CTDB_OPTIONS="--reclock=rec.lock --nlist $NODES --event-script-dir=tests/events.d --logfile=- --dbdir=test.db --dbdir-persistent=test.db/persistent $*"
if [ `id -u` -eq 0 ]; then
CTDB_OPTIONS="$CTDB_OPTIONS --public-addresses=tests/public_addresses --public-interface=lo"
fi
diff --git a/ctdb/tools/ctdb.c b/ctdb/tools/ctdb.c
index b4f2ae575c3..ed544e5c41b 100644
--- a/ctdb/tools/ctdb.c
+++ b/ctdb/tools/ctdb.c
@@ -762,7 +762,7 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
}
db_name = argv[0];
- ctdb_db = ctdb_attach(ctdb, db_name);
+ ctdb_db = ctdb_attach(ctdb, db_name, false);
if (ctdb_db == NULL) {
DEBUG(0,("Unable to attach to database '%s'\n", db_name));
@@ -800,10 +800,13 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
for(i=0;inum;i++){
const char *path;
const char *name;
+ bool persistent;
- ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbids[i], ctdb, &path);
- ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbids[i], ctdb, &name);
- printf("dbid:0x%08x name:%s path:%s\n", dbmap->dbids[i], name, path);
+ ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &path);
+ ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name);
+ persistent = dbmap->dbs[i].persistent;
+ printf("dbid:0x%08x name:%s path:%s %s\n", dbmap->dbs[i].dbid, name,
+ path, persistent?"PERSISTENT":"");
}
return 0;
@@ -982,7 +985,7 @@ static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv
}
db_name = argv[0];
- ctdb_db = ctdb_attach(ctdb, db_name);
+ ctdb_db = ctdb_attach(ctdb, db_name, false);
if (ctdb_db == NULL) {
DEBUG(0,("Unable to attach to database '%s'\n", db_name));
return -1;