dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone

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

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