Blame SOURCES/0054-BE-Send-refresh-requests-in-batches.patch

5cd47f
From 84268efb9de2befd87b5e251bf2c99eb583923b6 Mon Sep 17 00:00:00 2001
5cd47f
From: Jakub Hrozek <jhrozek@redhat.com>
5cd47f
Date: Wed, 8 May 2019 23:16:07 +0200
5cd47f
Subject: [PATCH 54/64] BE: Send refresh requests in batches
5cd47f
5cd47f
As we extend the background refresh into larger domains, the amount of
5cd47f
data that SSSD refreshes on the background might be larger. And
5cd47f
refreshing all expired entries in a single request might block sssd_be
5cd47f
for a long time, either triggering the watchdog or starving other
5cd47f
legitimate requests.
5cd47f
5cd47f
Therefore the background refresh will be done in batches of 200 entries.
5cd47f
The first batch of every type (up to 200 users, up to 200 groups, ...)
5cd47f
will be scheduled imediatelly and subsequent batches with a 0.5 second
5cd47f
delay.
5cd47f
5cd47f
Related:
5cd47f
https://pagure.io/SSSD/sssd/issue/4012
5cd47f
5cd47f
Reviewed-by: Sumit Bose <sbose@redhat.com>
5cd47f
(cherry picked from commit 7443498cc074c323e3b307f47ed49d59a5001f64)
5cd47f
5cd47f
Reviewed-by: Sumit Bose <sbose@redhat.com>
5cd47f
---
5cd47f
 src/providers/be_refresh.c            | 131 ++++++++++++++++++++++----
5cd47f
 src/tests/cmocka/test_expire_common.c |   6 +-
5cd47f
 src/tests/sss_idmap-tests.c           |   8 +-
5cd47f
 src/util/util.h                       |   8 ++
5cd47f
 4 files changed, 128 insertions(+), 25 deletions(-)
5cd47f
5cd47f
diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
5cd47f
index c4ff71e1f..5d86509bb 100644
5cd47f
--- a/src/providers/be_refresh.c
5cd47f
+++ b/src/providers/be_refresh.c
5cd47f
@@ -204,8 +204,21 @@ struct be_refresh_state {
5cd47f
     struct sss_domain_info *domain;
5cd47f
     enum be_refresh_type index;
5cd47f
     time_t period;
5cd47f
+
5cd47f
+    char **refresh_values;
5cd47f
+    size_t refresh_val_size;
5cd47f
+    size_t refresh_index;
5cd47f
+
5cd47f
+    size_t batch_size;
5cd47f
+    char **refresh_batch;
5cd47f
 };
5cd47f
 
5cd47f
+static errno_t be_refresh_batch_step(struct tevent_req *req,
5cd47f
+                                     uint32_t msec_delay);
5cd47f
+static void be_refresh_batch_step_wakeup(struct tevent_context *ev,
5cd47f
+                                         struct tevent_timer *tt,
5cd47f
+                                         struct timeval tv,
5cd47f
+                                         void *pvt);
5cd47f
 static errno_t be_refresh_step(struct tevent_req *req);
5cd47f
 static void be_refresh_done(struct tevent_req *subreq);
5cd47f
 
5cd47f
@@ -236,6 +249,13 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx,
5cd47f
         goto immediately;
5cd47f
     }
5cd47f
 
5cd47f
+    state->batch_size = 200;
5cd47f
+    state->refresh_batch = talloc_zero_array(state, char *, state->batch_size+1);
5cd47f
+    if (state->refresh_batch == NULL) {
5cd47f
+        ret = ENOMEM;
5cd47f
+        goto immediately;
5cd47f
+    }
5cd47f
+
5cd47f
     ret = be_refresh_step(req);
5cd47f
     if (ret == EOK) {
5cd47f
         goto immediately;
5cd47f
@@ -261,8 +281,6 @@ immediately:
5cd47f
 static errno_t be_refresh_step(struct tevent_req *req)
5cd47f
 {
5cd47f
     struct be_refresh_state *state = NULL;
5cd47f
-    struct tevent_req *subreq = NULL;
5cd47f
-    char **values = NULL;
5cd47f
     errno_t ret;
5cd47f
 
5cd47f
     state = tevent_req_data(req, struct be_refresh_state);
5cd47f
@@ -289,42 +307,103 @@ static errno_t be_refresh_step(struct tevent_req *req)
5cd47f
             goto done;
5cd47f
         }
5cd47f
 
5cd47f
+        talloc_zfree(state->refresh_values);
5cd47f
         ret = be_refresh_get_values(state, state->index, state->ctx->attr_name,
5cd47f
-                                    state->domain, state->period, &values);
5cd47f
+                                    state->domain, state->period,
5cd47f
+                                    &state->refresh_values);
5cd47f
         if (ret != EOK) {
5cd47f
             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain DN list [%d]: %s\n",
5cd47f
                                         ret, sss_strerror(ret));
5cd47f
             goto done;
5cd47f
         }
5cd47f
 
5cd47f
-        DEBUG(SSSDBG_TRACE_FUNC, "Refreshing %s in domain %s\n",
5cd47f
-              state->cb->name, state->domain->name);
5cd47f
+        for (state->refresh_val_size = 0;
5cd47f
+             state->refresh_values[state->refresh_val_size] != NULL;
5cd47f
+             state->refresh_val_size++);
5cd47f
+
5cd47f
+        DEBUG(SSSDBG_TRACE_FUNC, "Refreshing %zu %s in domain %s\n",
5cd47f
+              state->refresh_val_size, state->cb->name, state->domain->name);
5cd47f
 
5cd47f
-        subreq = state->cb->send_fn(state, state->ev, state->be_ctx,
5cd47f
-                                    state->domain, values, state->cb->pvt);
5cd47f
-        if (subreq == NULL) {
5cd47f
-            ret = ENOMEM;
5cd47f
+        ret = be_refresh_batch_step(req, 0);
5cd47f
+        if (ret == EOK) {
5cd47f
+            state->index++;
5cd47f
+            continue;
5cd47f
+        } else if (ret != EAGAIN) {
5cd47f
             goto done;
5cd47f
         }
5cd47f
-
5cd47f
-        /* make the list disappear with subreq */
5cd47f
-        talloc_steal(subreq, values);
5cd47f
-
5cd47f
-        tevent_req_set_callback(subreq, be_refresh_done, req);
5cd47f
+        /* EAGAIN only, refreshing something.. */
5cd47f
 
5cd47f
         state->index++;
5cd47f
-        ret = EAGAIN;
5cd47f
         goto done;
5cd47f
     }
5cd47f
 
5cd47f
     ret = EOK;
5cd47f
 
5cd47f
 done:
5cd47f
-    if (ret != EOK && ret != EAGAIN) {
5cd47f
-        talloc_free(values);
5cd47f
+    return ret;
5cd47f
+}
5cd47f
+
5cd47f
+static errno_t be_refresh_batch_step(struct tevent_req *req,
5cd47f
+                                     uint32_t msec_delay)
5cd47f
+{
5cd47f
+    struct be_refresh_state *state = tevent_req_data(req, struct be_refresh_state);
5cd47f
+    struct timeval tv;
5cd47f
+    struct tevent_timer *timeout = NULL;
5cd47f
+
5cd47f
+    size_t remaining;
5cd47f
+    size_t batch_size;
5cd47f
+
5cd47f
+    memset(state->refresh_batch, 0, sizeof(char *) * state->batch_size);
5cd47f
+
5cd47f
+    if (state->refresh_index >= state->refresh_val_size) {
5cd47f
+        DEBUG(SSSDBG_FUNC_DATA, "The batch is done\n");
5cd47f
+        state->refresh_index = 0;
5cd47f
+        return EOK;
5cd47f
     }
5cd47f
 
5cd47f
-    return ret;
5cd47f
+    remaining = state->refresh_val_size - state->refresh_index;
5cd47f
+    batch_size = MIN(remaining, state->batch_size);
5cd47f
+    DEBUG(SSSDBG_FUNC_DATA,
5cd47f
+          "This batch will refresh %zu entries (so far %zu/%zu)\n",
5cd47f
+          batch_size, state->refresh_index, state->refresh_val_size);
5cd47f
+
5cd47f
+    for (size_t i = 0; i < batch_size; i++) {
5cd47f
+        state->refresh_batch[i] = state->refresh_values[state->refresh_index];
5cd47f
+        state->refresh_index++;
5cd47f
+    }
5cd47f
+
5cd47f
+    tv = tevent_timeval_current_ofs(0, msec_delay * 1000);
5cd47f
+    timeout = tevent_add_timer(state->be_ctx->ev, req, tv,
5cd47f
+                               be_refresh_batch_step_wakeup, req);
5cd47f
+    if (timeout == NULL) {
5cd47f
+        return ENOMEM;
5cd47f
+    }
5cd47f
+
5cd47f
+    return EAGAIN;
5cd47f
+}
5cd47f
+
5cd47f
+static void be_refresh_batch_step_wakeup(struct tevent_context *ev,
5cd47f
+                                         struct tevent_timer *tt,
5cd47f
+                                         struct timeval tv,
5cd47f
+                                         void *pvt)
5cd47f
+{
5cd47f
+    struct tevent_req *req;
5cd47f
+    struct tevent_req *subreq = NULL;
5cd47f
+    struct be_refresh_state *state = NULL;
5cd47f
+
5cd47f
+    req = talloc_get_type(pvt, struct tevent_req);
5cd47f
+    state = tevent_req_data(req, struct be_refresh_state);
5cd47f
+
5cd47f
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Issuing refresh\n");
5cd47f
+    subreq = state->cb->send_fn(state, state->ev, state->be_ctx,
5cd47f
+                                state->domain,
5cd47f
+                                state->refresh_batch,
5cd47f
+                                state->cb->pvt);
5cd47f
+    if (subreq == NULL) {
5cd47f
+        tevent_req_error(req, ENOMEM);
5cd47f
+        return;
5cd47f
+    }
5cd47f
+    tevent_req_set_callback(subreq, be_refresh_done, req);
5cd47f
 }
5cd47f
 
5cd47f
 static void be_refresh_done(struct tevent_req *subreq)
5cd47f
@@ -342,8 +421,24 @@ static void be_refresh_done(struct tevent_req *subreq)
5cd47f
         goto done;
5cd47f
     }
5cd47f
 
5cd47f
+    ret = be_refresh_batch_step(req, 500);
5cd47f
+    if (ret == EAGAIN) {
5cd47f
+        DEBUG(SSSDBG_TRACE_INTERNAL,
5cd47f
+              "Another batch in this step in progress\n");
5cd47f
+        return;
5cd47f
+    } else if (ret != EOK) {
5cd47f
+        DEBUG(SSSDBG_OP_FAILURE,
5cd47f
+              "be_refresh_batch_step failed [%d]: %s\n",
5cd47f
+              ret, sss_strerror(ret));
5cd47f
+        goto done;
5cd47f
+    }
5cd47f
+
5cd47f
+    DEBUG(SSSDBG_TRACE_INTERNAL, "All batches in this step refreshed\n");
5cd47f
+
5cd47f
+    /* Proceed to the next step */
5cd47f
     ret = be_refresh_step(req);
5cd47f
     if (ret == EAGAIN) {
5cd47f
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Another step in progress\n");
5cd47f
         return;
5cd47f
     }
5cd47f
 
5cd47f
diff --git a/src/tests/cmocka/test_expire_common.c b/src/tests/cmocka/test_expire_common.c
5cd47f
index 5d3ea02f3..4f6168190 100644
5cd47f
--- a/src/tests/cmocka/test_expire_common.c
5cd47f
+++ b/src/tests/cmocka/test_expire_common.c
5cd47f
@@ -32,7 +32,7 @@
5cd47f
 #include "tests/common_check.h"
5cd47f
 #include "tests/cmocka/test_expire_common.h"
5cd47f
 
5cd47f
-#define MAX 100
5cd47f
+#define MAX_VAL 100
5cd47f
 
5cd47f
 static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s)
5cd47f
 {
5cd47f
@@ -41,10 +41,10 @@ static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s)
5cd47f
     size_t len;
5cd47f
     char *timestr;
5cd47f
 
5cd47f
-    timestr = talloc_array(mem_ctx, char, MAX);
5cd47f
+    timestr = talloc_array(mem_ctx, char, MAX_VAL);
5cd47f
 
5cd47f
     tm = gmtime(&t);
5cd47f
-    len = strftime(timestr, MAX, format, tm);
5cd47f
+    len = strftime(timestr, MAX_VAL, format, tm);
5cd47f
     if (len == 0) {
5cd47f
         return NULL;
5cd47f
     }
5cd47f
diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
5cd47f
index 885913645..ef6843403 100644
5cd47f
--- a/src/tests/sss_idmap-tests.c
5cd47f
+++ b/src/tests/sss_idmap-tests.c
5cd47f
@@ -140,8 +140,8 @@ void idmap_add_domain_with_sec_slices_setup_cb_fail(void)
5cd47f
 }
5cd47f
 
5cd47f
 
5cd47f
-#define MAX 1000
5cd47f
-char data[MAX];
5cd47f
+#define DATA_MAX 1000
5cd47f
+char data[DATA_MAX];
5cd47f
 
5cd47f
 enum idmap_error_code cb2(const char *dom_name,
5cd47f
                           const char *dom_sid,
5cd47f
@@ -154,10 +154,10 @@ enum idmap_error_code cb2(const char *dom_name,
5cd47f
     char *p = (char*)pvt;
5cd47f
     size_t len;
5cd47f
 
5cd47f
-    len = snprintf(p, MAX, "%s, %s %s, %"PRIu32", %"PRIu32", %" PRIu32,
5cd47f
+    len = snprintf(p, DATA_MAX, "%s, %s %s, %"PRIu32", %"PRIu32", %" PRIu32,
5cd47f
                    dom_name, dom_sid, range_id, min_id, max_id, first_rid);
5cd47f
 
5cd47f
-    if (len >= MAX) {
5cd47f
+    if (len >= DATA_MAX) {
5cd47f
         return IDMAP_OUT_OF_MEMORY;
5cd47f
     }
5cd47f
     return IDMAP_SUCCESS;
5cd47f
diff --git a/src/util/util.h b/src/util/util.h
5cd47f
index 3003583b7..fce7e42c3 100644
5cd47f
--- a/src/util/util.h
5cd47f
+++ b/src/util/util.h
5cd47f
@@ -68,6 +68,14 @@
5cd47f
 
5cd47f
 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
5cd47f
 
5cd47f
+#ifndef MIN
5cd47f
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
5cd47f
+#endif
5cd47f
+
5cd47f
+#ifndef MAX
5cd47f
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
5cd47f
+#endif
5cd47f
+
5cd47f
 #define SSSD_MAIN_OPTS SSSD_DEBUG_OPTS
5cd47f
 
5cd47f
 #define SSSD_SERVER_OPTS(uid, gid) \
5cd47f
-- 
5cd47f
2.20.1
5cd47f