dbwrap: add dbwrap_parse_record_send/recv
authorRalph Boehme <slow@samba.org>
Tue, 27 Dec 2016 08:13:37 +0000 (09:13 +0100)
committerJeremy Allison <jra@samba.org>
Tue, 18 Apr 2017 20:54:16 +0000 (22:54 +0200)
The req_state parameter tells the caller whether the async request is
blocked in a full send queue:

req_state >= DBWRAP_REQ_DISPATCHED := request is dispatched
req_state < DBWRAP_REQ_DISPATCHED := send queue is full

This is useful in a clustered Samba environment where the async dbwrap
request is sent over a socket to the local ctdbd.

If the send queue is full and the caller was issuing multiple async
dbwrap requests in a loop, the caller knows it's probably time to stop
sending requests for now and try again later.

This will be used in subsequent commits in
smbd_smb2_query_directory_send() when implementing async write time
updates. Directories may contain umpteen files so we send many requests
to ctdb without going through tevent and reading the responses which has
the potential to deadlock.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
lib/dbwrap/dbwrap.c
lib/dbwrap/dbwrap.h
lib/dbwrap/wscript_build

index 68e5608608a01ba60faf94b8293e428934f93324..025d463b4bd74827f75aa4d27604166adb902a15 100644 (file)
@@ -26,6 +26,7 @@
 #include "dbwrap/dbwrap.h"
 #include "dbwrap/dbwrap_private.h"
 #include "lib/util/util_tdb.h"
+#include "lib/util/tevent_ntstatus.h"
 
 /*
  * Fall back using fetch if no genuine exists operation is provided
@@ -368,6 +369,117 @@ NTSTATUS dbwrap_parse_record(struct db_context *db, TDB_DATA key,
        return db->parse_record(db, key, parser, private_data);
 }
 
+struct dbwrap_parse_record_state {
+       struct db_context *db;
+       TDB_DATA key;
+       uint8_t _keybuf[64];
+};
+
+static void dbwrap_parse_record_done(struct tevent_req *subreq);
+
+struct tevent_req *dbwrap_parse_record_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct db_context *db,
+       TDB_DATA key,
+       void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data),
+       void *private_data,
+       enum dbwrap_req_state *req_state)
+{
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+       struct dbwrap_parse_record_state *state = NULL;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state, struct dbwrap_parse_record_state);
+       if (req == NULL) {
+               *req_state = DBWRAP_REQ_ERROR;
+               return NULL;
+       }
+
+       *state = (struct dbwrap_parse_record_state) {
+               .db = db,
+       };
+
+       if (parser == NULL) {
+               parser = dbwrap_null_parser;
+       }
+
+       *req_state = DBWRAP_REQ_INIT;
+
+       if (db->parse_record_send == NULL) {
+               /*
+                * Backend doesn't implement async version, call sync one
+                */
+               status = db->parse_record(db, key, parser, private_data);
+               if (tevent_req_nterror(req, status)) {
+                       *req_state = DBWRAP_REQ_DONE;
+                       return tevent_req_post(req, ev);
+               }
+
+               *req_state = DBWRAP_REQ_DONE;
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * Copy the key into our state ensuring the key data buffer is always
+        * available to the all dbwrap backend over the entire lifetime of the
+        * async request. Otherwise the caller might have free'd the key buffer.
+        */
+       if (key.dsize > sizeof(state->_keybuf)) {
+               state->key.dptr = talloc_memdup(state, key.dptr, key.dsize);
+               if (tevent_req_nomem(state->key.dptr, req)) {
+                       return tevent_req_post(req, ev);
+               }
+       } else {
+               memcpy(state->_keybuf, key.dptr, key.dsize);
+               state->key.dptr = state->_keybuf;
+       }
+       state->key.dsize = key.dsize;
+
+       subreq = db->parse_record_send(state,
+                                      ev,
+                                      db,
+                                      state->key,
+                                      parser,
+                                      private_data,
+                                      req_state);
+       if (tevent_req_nomem(subreq, req)) {
+               *req_state = DBWRAP_REQ_ERROR;
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq,
+                               dbwrap_parse_record_done,
+                               req);
+       return req;
+}
+
+static void dbwrap_parse_record_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct dbwrap_parse_record_state *state = tevent_req_data(
+               req, struct dbwrap_parse_record_state);
+       NTSTATUS status;
+
+       status = state->db->parse_record_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS dbwrap_parse_record_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
 int dbwrap_wipe(struct db_context *db)
 {
        if (db->wipe == NULL) {
index 936e66247d762b7af05995e729ab3f30a684eb78..fac65ee6cfeef36d6ff47e65273aa6d00c1425de 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "replace.h"
 #include <talloc.h>
+#include <tevent.h>
 #include "libcli/util/ntstatus.h"
 #include "tdb.h"
 #include "lib/param/loadparm.h"
@@ -98,6 +99,38 @@ NTSTATUS dbwrap_parse_record(struct db_context *db, TDB_DATA key,
                             void (*parser)(TDB_DATA key, TDB_DATA data,
                                            void *private_data),
                             void *private_data);
+/**
+ * Async implementation of dbwrap_parse_record
+ *
+ * @param[in]  mem_ctx      talloc memory context to use.
+ *
+ * @param[in]  ev           tevent context to use
+ *
+ * @param[in]  db           Database to query
+ *
+ * @param[in]  key          Record key, the function makes a copy of this
+ *
+ * @param[in]  parser       Parser callback function
+ *
+ * @param[in]  private_data Private data for the callback function
+ *
+ * @param[out] req_state    Pointer to a enum dbwrap_req_state variable
+ *
+ * @note req_state is updated in the send function. To determine the final
+ * result of the request the caller should therefor not rely on req_state. The
+ * primary use case is to give the caller an indication whether the request is
+ * already sent to ctdb (DBWRAP_REQ_DISPATCHED) or if it's still stuck in the
+ * sendqueue (DBWRAP_REQ_QUEUED).
+ **/
+struct tevent_req *dbwrap_parse_record_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct db_context *db,
+       TDB_DATA key,
+       void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data),
+       void *private_data,
+       enum dbwrap_req_state *req_state);
+NTSTATUS dbwrap_parse_record_recv(struct tevent_req *req);
 int dbwrap_wipe(struct db_context *db);
 int dbwrap_check(struct db_context *db);
 int dbwrap_get_seqnum(struct db_context *db);
index b719a606e1829dcee24dffff991b2993acd786f2..83e5895a7b1caee97df0be99b5d79f2e076c4a0c 100644 (file)
@@ -1,6 +1,6 @@
 SRC = '''dbwrap.c dbwrap_util.c dbwrap_rbt.c dbwrap_cache.c dbwrap_tdb.c
          dbwrap_local_open.c'''
-DEPS= '''samba-util util_tdb samba-errors tdb tdb-wrap samba-hostconfig'''
+DEPS= '''samba-util util_tdb samba-errors tdb tdb-wrap samba-hostconfig tevent tevent-util'''
 
 bld.SAMBA_LIBRARY('dbwrap',
                   source=SRC,