ReadOnly: add per-database statistics to view how much delegations/revokes we have
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Wed, 8 Feb 2012 04:29:27 +0000 (15:29 +1100)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Wed, 8 Feb 2012 04:29:27 +0000 (15:29 +1100)
(This used to be ctdb commit 751ed46197661eb841042ab6a02855a51dd0b17c)

ctdb/include/ctdb.h
ctdb/include/ctdb_private.h
ctdb/include/ctdb_protocol.h
ctdb/libctdb/control.c
ctdb/libctdb/sync.c
ctdb/server/ctdb_call.c
ctdb/server/ctdb_control.c
ctdb/server/ctdb_daemon.c
ctdb/server/ctdb_ltdb_server.c
ctdb/tests/src/libctdb_test.c
ctdb/tools/ctdb.c

index 8dbdd47e1e246de201dbcf79cfdac3d3a3296778..93224cbdaa63277e95f029e42d91816f28e0baec 100644 (file)
@@ -509,6 +509,39 @@ bool ctdb_getpnn_recv(struct ctdb_connection *ctdb,
                      struct ctdb_request *req, uint32_t *pnn);
 
 
+/**
+ * ctdb_getdbstat_send - read statistics for a db
+ * @ctdb: the ctdb_connection from ctdb_connect.
+ * @destnode: the destination node (see below)
+ * @db_id:    the database to collect the statistics from
+ * @callback: the callback when ctdb replies to our message (typesafe)
+ * @cbdata: the argument to callback()
+ *
+ * There are several special values for destnode, detailed in
+ * ctdb_protocol.h, particularly CTDB_CURRENT_NODE which means the
+ * local ctdbd.
+ */
+struct ctdb_request *
+ctdb_getdbstat_send(struct ctdb_connection *ctdb,
+                    uint32_t destnode,
+                    uint32_t db_id,
+                    ctdb_callback_t callback,
+                    void *cbdata);
+/**
+ * ctdb_getdbstat_recv - read an ctdb_getdbstat reply from ctdbd
+ * @ctdb: the ctdb_connection from ctdb_connect.
+ * @req: the completed request.
+ * @stat: a pointer to the *stat to fill in
+ *
+ * This returns false if something went wrong, or otherwise fills in **stats
+ * stats must be freed later by calling ctdb_free_dbstat();
+ */
+bool ctdb_getdbstat_recv(struct ctdb_connection *ctdb,
+                        struct ctdb_request *req,
+                        struct ctdb_db_statistics **stat);
+
+void ctdb_free_dbstat(struct ctdb_db_statistics *stat);
+
 /**
  * ctdb_check_message_handlers_send - check a list of message_handlers
  * if they are registered
@@ -875,6 +908,26 @@ bool ctdb_getpnn(struct ctdb_connection *ctdb,
                 uint32_t destnode,
                 uint32_t *pnn);
 
+/**
+ * ctdb_getdbstat - read the db stat of a node (synchronous)
+ * @ctdb: the ctdb_connection from ctdb_connect.
+ * @destnode: the destination node (see below)
+ * @db_id:    the database to collect the statistics from
+ * @stat: a pointer to the *stat to fill in
+ *
+ * There are several special values for destnode, detailed in
+ * ctdb_protocol.h, particularly CTDB_CURRENT_NODE which means the
+ * local ctdbd.
+ *
+ * This returns false if something went wrong, or otherwise fills in **stat
+ * stat must be freed later by calling ctdb_free_dbstat();
+ */
+bool ctdb_getdbstat(struct ctdb_connection *ctdb,
+                   uint32_t destnode,
+                   uint32_t db_id,
+                   struct ctdb_db_statistics **stat);
+
+
 /**
  * ctdb_check_message_handlers - check a list of message_handlers (synchronous)
  * @ctdb: the ctdb_connection from ctdb_connect.
@@ -1092,6 +1145,10 @@ void ctdb_free_vnnmap(struct ctdb_vnn_map *vnnmap);
        ctdb_getpnn_send((ctdb), (destnode),                            \
                         ctdb_sendcb((cb), (cbdata)), (cbdata))
 
+#define ctdb_getdbstat_send(ctdb, destnode, db_id, cb, cbdata)         \
+       ctdb_getdbstat_send((ctdb), (destnode), (db_id),                \
+                           ctdb_sendcb((cb), (cbdata)), (cbdata))
+
 #define ctdb_check_message_handlers_send(ctdb, destnode, num, mhs,     \
                         cb, cbdata)                                    \
        ctdb_check_message_handlers_send((ctdb), (destnode), (num),     \
index f58356fbe741bb179e72c08066782686471bef47..f420e6c34174e00ca1b7e7987d7aa41569d67abf 100644 (file)
@@ -300,6 +300,11 @@ struct ctdb_daemon_data {
                ctdb->statistics_current.counter++;                                     \
        }
 
+#define CTDB_INCREMENT_DB_STAT(ctdb_db, counter) \
+       {                                                                               \
+               ctdb_db->statistics.counter++;                                          \
+       }
+
 #define CTDB_DECREMENT_STAT(ctdb, counter) \
        {                                                                               \
                if (ctdb->statistics.counter > 0)                                       \
@@ -513,6 +518,8 @@ struct ctdb_db_context {
           so we can avoid sending duplicate fetch requests
        */
        struct trbt_tree *deferred_fetch;
+
+       struct ctdb_db_statistics statistics;
 };
 
 
@@ -1440,4 +1447,8 @@ int ctdb_fetch_func(struct ctdb_call_info *call);
 
 int ctdb_fetch_with_header_func(struct ctdb_call_info *call);
 
+int32_t ctdb_control_get_db_statistics(struct ctdb_context *ctdb,
+                               uint32_t db_id,
+                               TDB_DATA *outdata);
+
 #endif
index 9cdf91c8d4d1781c447c6be3506393a9df0eabba..4cf27893ffa2a8489b8be6f6b4aa6310c204f396 100644 (file)
@@ -377,6 +377,7 @@ enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS          = 0,
                    CTDB_CONTROL_SET_DB_READONLY         = 129,
                    CTDB_CONTROL_CHECK_SRVIDS            = 130,
                    CTDB_CONTROL_TRAVERSE_START_EXT      = 131,
+                   CTDB_CONTROL_GET_DB_STATISTICS       = 132,
 };
 
 /*
@@ -658,6 +659,14 @@ struct ctdb_statistics_wire {
        struct ctdb_statistics stats[1];
 };
 
+/*
+ * db statistics
+ */
+struct ctdb_db_statistics {
+       uint32_t db_ro_delegations;
+       uint32_t db_ro_revokes;
+};
+
 /*
  * wire format for interface list
  */
index ba22444ee54d49a00144648a4acdfd16c0851f4b..8741ad9c0b2b1e1f50521bff1b6796fa03d4e9a3 100644 (file)
@@ -25,6 +25,7 @@
 #undef ctdb_getrecmaster_send
 #undef ctdb_getrecmode_send
 #undef ctdb_getpnn_send
+#undef ctdb_getdbstat_send
 #undef ctdb_check_message_handlers_send
 #undef ctdb_getnodemap_send
 #undef ctdb_getpublicips_send
@@ -112,6 +113,56 @@ struct ctdb_request *ctdb_getpnn_send(struct ctdb_connection *ctdb,
                                        NULL, 0, callback, private_data);
 }
 
+bool ctdb_getdbstat_recv(struct ctdb_connection *ctdb,
+                        struct ctdb_request *req,
+                        struct ctdb_db_statistics **stat)
+{
+       struct ctdb_reply_control *reply;
+       struct ctdb_db_statistics *s;
+
+       reply = unpack_reply_control(req, CTDB_CONTROL_GET_DB_STATISTICS);
+       if (!reply) {
+               return false;
+       }
+       if (reply->status == -1) {
+               DEBUG(ctdb, LOG_ERR, "ctdb_getpnn_recv: status -1");
+               return false;
+       }
+       if (reply->datalen != sizeof(struct ctdb_db_statistics)) {
+               DEBUG(ctdb, LOG_ERR, "ctdb_getdbstat_recv: returned data is %d bytes but should be %d", reply->datalen, (int)sizeof(struct ctdb_db_statistics));
+               return false;
+       }
+
+       s = malloc(sizeof(struct ctdb_db_statistics));
+       if (!s) {
+               return false;
+       }
+       memcpy(s, reply->data, sizeof(struct ctdb_db_statistics));
+       *stat = s;
+
+       return true;
+}
+
+struct ctdb_request *ctdb_getdbstat_send(struct ctdb_connection *ctdb,
+                                     uint32_t destnode,
+                                     uint32_t db_id,
+                                     ctdb_callback_t callback,
+                                     void *private_data)
+{
+       uint32_t indata = db_id;
+
+       return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_DB_STATISTICS, destnode,
+                                       &indata, sizeof(indata), callback, private_data);
+}
+
+void ctdb_free_dbstat(struct ctdb_db_statistics *stat)
+{
+       if (stat == NULL) {
+               return;
+       }
+       free(stat);
+}
+
 bool ctdb_getnodemap_recv(struct ctdb_connection *ctdb,
                      struct ctdb_request *req, struct ctdb_node_map **nodemap)
 {
index f2b69570785203008fa38e7400c95646e8159c85..4b68cd1e32cc1278110679b0c557b5f58ffad26e 100644 (file)
@@ -136,6 +136,24 @@ bool ctdb_getpnn(struct ctdb_connection *ctdb,
        return ret;
 }
 
+bool ctdb_getdbstat(struct ctdb_connection *ctdb,
+                   uint32_t destnode, uint32_t db_id,
+                   struct ctdb_db_statistics **stat)
+{
+       struct ctdb_request *req;
+       bool done = false;
+       bool ret = false;
+
+       req = synchronous(ctdb,
+                         ctdb_getdbstat_send(ctdb, destnode, db_id, set, &done),
+                         &done);
+       if (req != NULL) {
+               ret = ctdb_getdbstat_recv(ctdb, req, stat);
+               ctdb_request_free(req);
+       }
+       return ret;
+}
+
 bool ctdb_check_message_handlers(struct ctdb_connection *ctdb,
                      uint32_t destnode, uint32_t num,
                      uint64_t *mhs, uint8_t *result)
index 8856f2ac28cfba1deedfe65d5d15d31440e095a1..99222336909748b3a7ff40ebc72b3dd059519bc3 100644 (file)
@@ -514,6 +514,7 @@ void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
        if (header.flags & CTDB_REC_RO_REVOKE_COMPLETE) {
                header.flags &= ~(CTDB_REC_RO_HAVE_DELEGATIONS|CTDB_REC_RO_HAVE_READONLY|CTDB_REC_RO_REVOKING_READONLY|CTDB_REC_RO_REVOKE_COMPLETE);
                CTDB_INCREMENT_STAT(ctdb, total_ro_revokes);
+               CTDB_INCREMENT_DB_STAT(ctdb_db, db_ro_revokes);
                if (ctdb_ltdb_store(ctdb_db, call->key, &header, data) != 0) {
                        ctdb_fatal(ctdb, "Failed to write header with cleared REVOKE flag");
                }
@@ -621,6 +622,7 @@ void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 
                ctdb_queue_packet(ctdb, &r->hdr);
                CTDB_INCREMENT_STAT(ctdb, total_ro_delegations);
+               CTDB_INCREMENT_DB_STAT(ctdb_db, db_ro_delegations);
 
                talloc_free(r);
                return;
index 060f2c5cf3368a2f5e1b5d8e2efb189d6a340453..c73458825cd936454147c2d8d4e966694523efa2 100644 (file)
@@ -630,6 +630,10 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
                CHECK_CONTROL_DATA_SIZE(size);
                return ctdb_control_schedule_for_deletion(ctdb, indata);
        }
+       case CTDB_CONTROL_GET_DB_STATISTICS:
+               CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t));
+               return ctdb_control_get_db_statistics(ctdb, *(uint32_t *)indata.dptr, outdata);
+
        default:
                DEBUG(DEBUG_CRIT,(__location__ " Unknown CTDB control opcode %u\n", opcode));
                return -1;
index bff91c5fa6789157a837ecb9032b8f9fcffd3dc2..24369978648ebfb629f67676e1275a9701b84f8f 100644 (file)
@@ -691,6 +691,7 @@ static void daemon_request_call_from_client(struct ctdb_client *client,
        if (header.flags & CTDB_REC_RO_REVOKE_COMPLETE) {
                header.flags &= ~(CTDB_REC_RO_HAVE_DELEGATIONS|CTDB_REC_RO_HAVE_READONLY|CTDB_REC_RO_REVOKING_READONLY|CTDB_REC_RO_REVOKE_COMPLETE);
                CTDB_INCREMENT_STAT(ctdb, total_ro_revokes);
+               CTDB_INCREMENT_DB_STAT(ctdb_db, db_ro_revokes);
                if (ctdb_ltdb_store(ctdb_db, key, &header, data) != 0) {
                        ctdb_fatal(ctdb, "Failed to write header with cleared REVOKE flag");
                }
index 9a0132a362c098228c30c6541e2ba06d21d91a48..e5437b99a26a60a316a8af9919390261e6056627 100644 (file)
@@ -1464,3 +1464,20 @@ int32_t ctdb_control_set_db_priority(struct ctdb_context *ctdb, TDB_DATA indata)
        return 0;
 }
 
+int32_t ctdb_control_get_db_statistics(struct ctdb_context *ctdb,
+                               uint32_t db_id,
+                               TDB_DATA *outdata)
+{
+       struct ctdb_db_context *ctdb_db;
+
+       ctdb_db = find_ctdb_db(ctdb, db_id);
+       if (!ctdb_db) {
+               DEBUG(DEBUG_ERR,("Unknown db_id 0x%x in get_db_statistics\n", db_id));
+               return -1;
+       }
+
+       outdata->dptr  = (uint8_t *)&(ctdb_db->statistics);
+       outdata->dsize = sizeof(ctdb_db->statistics);
+
+       return 0;
+}
index 73ba155a29e6e6fb85ae7d582b90b2c6933caf10..56c007e8e3c30a60f054b49653a436f8fa09e28b 100644 (file)
@@ -487,3 +487,20 @@ void ctdb_disconnect(struct ctdb_connection *ctdb)
        free(ctdb);
 }
 
+bool ctdb_getdbstat(struct ctdb_connection *ctdb,
+                   uint32_t destnode,
+                   uint32_t db_id,
+                   struct ctdb_db_statistics **dbstatistics)
+{
+       if (!current_node_is_connected(ctdb)) {
+               return false;
+       }
+
+       *dbstatistics = malloc(sizeof(struct ctdb_db_statistics));
+       return true;
+}
+
+void ctdb_free_dbstat(struct ctdb_db_statistics *dbstatistics)
+{
+       free(dbstatistics);
+}
index ceab2979ffde4ba8f4ba05fc0ce9cddc71fca873..051a270d6ee33b61bcf73ebf40fee89b57aed115 100644 (file)
@@ -565,6 +565,55 @@ static int control_stats(struct ctdb_context *ctdb, int argc, const char **argv)
 }
 
 
+/*
+  display remote ctdb db statistics
+ */
+static int control_dbstatistics(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_db_statistics *dbstatistics;
+       struct ctdb_dbid_map *dbmap=NULL;
+       int i, ret;
+
+       if (argc < 1) {
+               usage();
+       }
+
+       ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx, &dbmap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn));
+               return ret;
+       }
+       for(i=0;i<dbmap->num;i++){
+               const char *name;
+
+               ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, tmp_ctx, &name);
+               if(!strcmp(argv[0], name)){
+                       talloc_free(discard_const(name));
+                       break;
+               }
+               talloc_free(discard_const(name));
+       }
+       if (i == dbmap->num) {
+               DEBUG(DEBUG_ERR,("No database with name '%s' found\n", argv[0]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       if (!ctdb_getdbstat(ctdb_connection, options.pnn, dbmap->dbs[i].dbid, &dbstatistics)) {
+               DEBUG(DEBUG_ERR,("Failed to read db statistics from node\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       printf("DB Statistics:\n");
+       printf("RO Delegations: %d\n", dbstatistics->db_ro_delegations);
+       printf("RO Revokes:     %d\n", dbstatistics->db_ro_revokes);
+
+       ctdb_free_dbstat(dbstatistics);
+       return 0;
+}
+
 /*
   display uptime of remote node
  */
@@ -5434,6 +5483,7 @@ static const struct {
        { "checktcpport",    control_chktcpport,        false,  true,  "check if a service is bound to a specific tcp port or not", "<port>" },
        { "getdbseqnum",     control_getdbseqnum,       false,  false, "get the sequence number off a database", "<dbid>" },
        { "nodestatus",      control_nodestatus,        true,   false,  "show and return node status" },
+       { "dbstatistics",    control_dbstatistics,      false,  false, "show db statistics", "<db>" },
 };
 
 /*