Blame SOURCES/0013-Ticket-49915-Master-ns-slapd-had-100-CPU-usage-after.patch

433de7
From 6d67faa0de58cb0b66fc72d43f24b1c9669f88f8 Mon Sep 17 00:00:00 2001
433de7
From: Thierry Bordaz <tbordaz@redhat.com>
433de7
Date: Mon, 3 Sep 2018 15:36:52 +0200
433de7
Subject: [PATCH] Ticket 49915 - Master ns-slapd had 100% CPU usage after
433de7
 starting replication and replication cannot finish
433de7
433de7
Bug Description:
433de7
	During a total initialization the supplier builds a candidate list of the entries to send.
433de7
	Because of https://fedorahosted.org/389/ticket/48755, the candidate list relies on parentid attribute.
433de7
	All entries, except tombstones and suffix itself, have parentid.
433de7
	There is an assumption that the first found key (i.e. '=1') contains the suffix children.
433de7
	So when it finally finds the suffix key it adds its children to a leftover list rather to the candidate list.
433de7
	Later idl_new_range_fetch loops for ever trying to add suffix children from leftover to candidate list.
433de7
433de7
Fix Description:
433de7
	The fix consist to store the suffix_id (if it does not exist already) in the parentid index (with the key '=0').
433de7
	Then get it to detect the suffix key from the index in idl_new_range_fetch.
433de7
433de7
https://pagure.io/389-ds-base/issue/49915
433de7
433de7
Reviewed by: Ludwig Krispenz, William Brown (thanks !)
433de7
433de7
Platforms tested: F27
433de7
433de7
Flag Day: no
433de7
433de7
Doc impact: no
433de7
---
433de7
 .../plugins/replication/repl5_tot_protocol.c  |  48 ++++++++
433de7
 ldap/servers/slapd/back-ldbm/dblayer.c        |   8 ++
433de7
 ldap/servers/slapd/back-ldbm/idl_new.c        |  34 +++++-
433de7
 ldap/servers/slapd/back-ldbm/index.c          | 114 ++++++++++++++++++
433de7
 ldap/servers/slapd/slapi-plugin.h             |  10 +-
433de7
 5 files changed, 209 insertions(+), 5 deletions(-)
433de7
433de7
diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c
433de7
index ee3c9dcb0..1dbbe694f 100644
433de7
--- a/ldap/servers/plugins/replication/repl5_tot_protocol.c
433de7
+++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c
433de7
@@ -283,6 +283,53 @@ repl5_tot_waitfor_async_results(callback_data *cb_data)
433de7
     }
433de7
 }
433de7
 
433de7
+/* This routine checks that the entry id of the suffix is
433de7
+ * stored in the parentid index
433de7
+ * The entry id of the suffix is stored with the equality key 0 (i.e. '=0')
433de7
+ * It first checks if the key '=0' exists. If it does not exists or if the first value
433de7
+ * stored with that key, does not match the suffix entryid (stored in the suffix entry
433de7
+ * from id2entry.db then it updates the value
433de7
+ */
433de7
+static void
433de7
+check_suffix_entryID(Slapi_Backend *be, Slapi_Entry *suffix)
433de7
+{
433de7
+    u_int32_t entryid;
433de7
+    char *entryid_str;
433de7
+    struct _back_info_index_key bck_info;
433de7
+
433de7
+    /* we are using a specific key in parentid to store the suffix entry id: '=0' */
433de7
+    bck_info.index = SLAPI_ATTR_PARENTID;
433de7
+    bck_info.key = "0";
433de7
+
433de7
+    /* First try to retrieve from parentid index the suffix entryID */
433de7
+    if (slapi_back_get_info(be, BACK_INFO_INDEX_KEY, (void **) &bck_info)) {
433de7
+        slapi_log_err(SLAPI_LOG_REPL, "check_suffix_entryID", "Total update: fail to retrieve suffix entryID. Let's try to write it\n");
433de7
+    }
433de7
+
433de7
+    /* Second retrieve the suffix entryid from the suffix entry itself */
433de7
+    entryid_str = slapi_entry_attr_get_charptr(suffix, "entryid");
433de7
+    if (entryid_str == NULL) {
433de7
+        char *dn;
433de7
+        dn = slapi_entry_get_ndn(suffix);
433de7
+        slapi_log_err(SLAPI_LOG_ERR, "check_suffix_entryID", "Unable to retrieve entryid of the suffix entry %s\n", dn ? dn : "<unknown>");
433de7
+        slapi_ch_free_string(&entryid_str);
433de7
+        return;
433de7
+    }
433de7
+    entryid = (u_int32_t) atoi(entryid_str);
433de7
+    slapi_ch_free_string(&entryid_str);
433de7
+
433de7
+    if (!bck_info.key_found || bck_info.id != entryid) {
433de7
+        /* The suffix entryid is not present in parentid index
433de7
+         *  or differs from what is in id2entry (entry 'suffix')
433de7
+         * So write it to the parentid so that the range index used
433de7
+         * during total init will know the entryid of the suffix
433de7
+         */
433de7
+        bck_info.id = entryid;
433de7
+        if (slapi_back_set_info(be, BACK_INFO_INDEX_KEY, (void **) &bck_info)) {
433de7
+            slapi_log_err(SLAPI_LOG_ERR, "check_suffix_entryID", "Total update: fail to register suffix entryid, continue assuming suffix is the first entry\n");
433de7
+        }
433de7
+    }
433de7
+}
433de7
 
433de7
 /*
433de7
  * Completely refresh a replica. The basic protocol interaction goes
433de7
@@ -467,6 +514,7 @@ retry:
433de7
         replica_subentry_check(area_sdn, rid);
433de7
 
433de7
         /* Send the subtree of the suffix in the order of parentid index plus ldapsubentry and nstombstone. */
433de7
+        check_suffix_entryID(be, suffix);
433de7
         slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(area_sdn),
433de7
                                      LDAP_SCOPE_SUBTREE, "(parentid>=1)", NULL, 0, ctrls, NULL,
433de7
                                      repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), OP_FLAG_BULK_IMPORT);
433de7
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c
433de7
index e84cb7695..fa931ccbf 100644
433de7
--- a/ldap/servers/slapd/back-ldbm/dblayer.c
433de7
+++ b/ldap/servers/slapd/back-ldbm/dblayer.c
433de7
@@ -7295,6 +7295,10 @@ ldbm_back_get_info(Slapi_Backend *be, int cmd, void **info)
433de7
         *(int *)info = entryrdn_get_switch();
433de7
         break;
433de7
     }
433de7
+    case BACK_INFO_INDEX_KEY : {
433de7
+        rc = get_suffix_key(be, (struct _back_info_index_key *)info);
433de7
+        break;
433de7
+    }
433de7
     default:
433de7
         break;
433de7
     }
433de7
@@ -7311,6 +7315,10 @@ ldbm_back_set_info(Slapi_Backend *be, int cmd, void *info)
433de7
     }
433de7
 
433de7
     switch (cmd) {
433de7
+    case BACK_INFO_INDEX_KEY : {
433de7
+        rc = set_suffix_key(be, (struct _back_info_index_key *)info);
433de7
+        break;
433de7
+    }
433de7
     default:
433de7
         break;
433de7
     }
433de7
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
433de7
index 4e28e3fc2..102265c47 100644
433de7
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
433de7
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
433de7
@@ -320,6 +320,9 @@ typedef struct _range_id_pair
433de7
  * In the total update (bulk import), an entry requires its ancestors already added.
433de7
  * To guarantee it, the range search with parentid is used with setting the flag
433de7
  * SLAPI_OP_RANGE_NO_IDL_SORT in operator.
433de7
+ * In bulk import the range search is parentid>=1 to retrieve all the entries
433de7
+ * But we need to order the IDL with the parents first => retrieve the suffix entry ID
433de7
+ * to store the children
433de7
  *
433de7
  * If the flag is set,
433de7
  * 1. the IDList is not sorted by the ID.
433de7
@@ -366,6 +369,23 @@ idl_new_range_fetch(
433de7
     if (NULL == flag_err) {
433de7
         return NULL;
433de7
     }
433de7
+    if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
433de7
+            struct _back_info_index_key bck_info;
433de7
+            int rc;
433de7
+            /* We are doing a bulk import
433de7
+             * try to retrieve the suffix entry id from the index
433de7
+             */
433de7
+
433de7
+            bck_info.index = SLAPI_ATTR_PARENTID;
433de7
+            bck_info.key = "0";
433de7
+
433de7
+            if (rc = slapi_back_get_info(be, BACK_INFO_INDEX_KEY, (void **)&bck_info)) {
433de7
+                slapi_log_err(SLAPI_LOG_WARNING, "idl_new_range_fetch", "Total update: fail to retrieve suffix entryID, continue assuming it is the first entry\n");
433de7
+            }
433de7
+            if (bck_info.key_found) {
433de7
+                suffix = bck_info.id;
433de7
+            }
433de7
+    }
433de7
 
433de7
     if (NEW_IDL_NOOP == *flag_err) {
433de7
         return NULL;
433de7
@@ -455,7 +475,7 @@ idl_new_range_fetch(
433de7
             *flag_err = LDAP_TIMELIMIT_EXCEEDED;
433de7
             goto error;
433de7
         }
433de7
-        if (operator&SLAPI_OP_RANGE_NO_IDL_SORT) {
433de7
+        if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
433de7
             key = (ID)strtol((char *)cur_key.data + 1, (char **)NULL, 10);
433de7
         }
433de7
         while (PR_TRUE) {
433de7
@@ -487,9 +507,13 @@ idl_new_range_fetch(
433de7
             /* note the last id read to check for dups */
433de7
             lastid = id;
433de7
             /* we got another ID, add it to our IDL */
433de7
-            if (operator&SLAPI_OP_RANGE_NO_IDL_SORT) {
433de7
-                if (count == 0) {
433de7
-                    /* First time.  Keep the suffix ID. */
433de7
+            if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
433de7
+                if ((count == 0) && (suffix == 0)) {
433de7
+                    /* First time.  Keep the suffix ID. 
433de7
+                     * note that 'suffix==0' mean we did not retrieve the suffix entry id
433de7
+                     * from the parentid index (key '=0'), so let assume the first
433de7
+                     * found entry is the one from the suffix
433de7
+                     */
433de7
                     suffix = key;
433de7
                     idl_rc = idl_append_extend(&idl, id);
433de7
                 } else if ((key == suffix) || idl_id_is_in_idlist(idl, key)) {
433de7
@@ -615,9 +639,11 @@ error:
433de7
     }
433de7
     if (operator&SLAPI_OP_RANGE_NO_IDL_SORT) {
433de7
         size_t remaining = leftovercnt;
433de7
+
433de7
         while(remaining > 0) {
433de7
             for (size_t i = 0; i < leftovercnt; i++) {
433de7
                 if (leftover[i].key > 0 && idl_id_is_in_idlist(idl, leftover[i].key) != 0) {
433de7
+                    /* if the leftover key has its parent in the idl */
433de7
                     idl_rc = idl_append_extend(&idl, leftover[i].id);
433de7
                     if (idl_rc) {
433de7
                         slapi_log_err(SLAPI_LOG_ERR, "idl_new_range_fetch",
433de7
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
433de7
index 222f64dff..dea6e9a3e 100644
433de7
--- a/ldap/servers/slapd/back-ldbm/index.c
433de7
+++ b/ldap/servers/slapd/back-ldbm/index.c
433de7
@@ -1236,6 +1236,120 @@ error:
433de7
     return ret;
433de7
 }
433de7
 
433de7
+/* This routine add in a given index (parentid)
433de7
+ * the key/value = '=0'/<suffix entryID>
433de7
+ * Input: 
433de7
+ *      info->key contains the key to lookup (i.e. '0')
433de7
+ *      info->index index name used to retrieve syntax and db file
433de7
+ *      info->id  the entryID of the suffix
433de7
+ */
433de7
+int
433de7
+set_suffix_key(Slapi_Backend *be, struct _back_info_index_key *info)
433de7
+{
433de7
+    struct ldbminfo *li;
433de7
+    int rc;
433de7
+    back_txn txn;
433de7
+    Slapi_Value *sv_key[2];
433de7
+    Slapi_Value tmpval;
433de7
+
433de7
+    if (info->index== NULL || info->key == NULL) {
433de7
+        slapi_log_err(SLAPI_LOG_ERR, "set_suffix_key", "Invalid index %s or key %s\n",
433de7
+                info->index ? info->index : "NULL",
433de7
+                info->key ? info->key : "NULL");
433de7
+        return -1;
433de7
+    }
433de7
+    
433de7
+    /* Start a txn */
433de7
+    li = (struct ldbminfo *)be->be_database->plg_private;
433de7
+    dblayer_txn_init(li, &txn);
433de7
+    if (rc = dblayer_txn_begin(be, txn.back_txn_txn, &txn)) {
433de7
+        slapi_log_err(SLAPI_LOG_ERR, "set_suffix_key", "Fail to update %s index with  %s/%d (key/ID): txn begin fails\n",
433de7
+                  info->index, info->key, info->id);
433de7
+        return rc;
433de7
+    }
433de7
+
433de7
+    sv_key[0] = &tmpval;
433de7
+    sv_key[1] = NULL;
433de7
+    slapi_value_init_string(sv_key[0], info->key);
433de7
+
433de7
+    if (rc = index_addordel_values_sv(be, info->index, sv_key, NULL, info->id, BE_INDEX_ADD, &txn)) {
433de7
+        value_done(sv_key[0]);
433de7
+        dblayer_txn_abort(be, &txn);
433de7
+        slapi_log_err(SLAPI_LOG_ERR, "set_suffix_key", "Fail to update %s index with  %s/%d (key/ID): index_addordel_values_sv fails\n",
433de7
+                  info->index, info->key, info->id);
433de7
+        return rc;
433de7
+    }
433de7
+
433de7
+    value_done(sv_key[0]);
433de7
+    if (rc = dblayer_txn_commit(be, &txn)) {
433de7
+        slapi_log_err(SLAPI_LOG_ERR, "set_suffix_key", "Fail to update %s index with  %s/%d (key/ID): commit fails\n",
433de7
+                  info->index, info->key, info->id);
433de7
+        return rc;
433de7
+    }
433de7
+
433de7
+    return 0;
433de7
+}
433de7
+/* This routine retrieves from a given index (parentid)
433de7
+ * the key/value = '=0'/<suffix entryID>
433de7
+ * Input: 
433de7
+ *      info->key contains the key to lookup (i.e. '0')
433de7
+ *      info->index index name used to retrieve syntax and db file
433de7
+ * Output
433de7
+ *      info->id It returns the first id that is found for the key.
433de7
+ *               If the key is not found, or there is no value for the key
433de7
+ *               it contains '0'
433de7
+ *      info->key_found  Boolean that says if the key leads to a valid ID in info->id
433de7
+ */
433de7
+int
433de7
+get_suffix_key(Slapi_Backend *be, struct _back_info_index_key *info)
433de7
+{
433de7
+    struct berval bv;
433de7
+    int err;
433de7
+    IDList *idl = NULL;
433de7
+    ID id;
433de7
+    int rc = 0;
433de7
+
433de7
+    if (info->index== NULL || info->key == NULL) {
433de7
+        slapi_log_err(SLAPI_LOG_ERR, "get_suffix_key", "Invalid index %s or key %s\n",
433de7
+                info->index ? info->index : "NULL",
433de7
+                info->key ? info->key : "NULL");
433de7
+        return -1;
433de7
+    }
433de7
+
433de7
+    /* This is the key to retrieve */
433de7
+    bv.bv_val = info->key;
433de7
+    bv.bv_len = strlen(bv.bv_val);
433de7
+
433de7
+    /* Assuming we are not going to find the key*/
433de7
+    info->key_found = PR_FALSE;
433de7
+    id = 0;
433de7
+    idl = index_read(be, info->index, indextype_EQUALITY, &bv, NULL, &err;;
433de7
+
433de7
+    if (idl == NULL) {
433de7
+        if (err != 0 && err != DB_NOTFOUND) {
433de7
+            slapi_log_err(SLAPI_LOG_ERR, "get_suffix_key", "Fail to read key %s (err=%d)\n",
433de7
+                    info->key ? info->key : "NULL",
433de7
+                    err);
433de7
+            rc = err;
433de7
+        }
433de7
+    } else {
433de7
+        /* info->key was found */
433de7
+        id = idl_firstid(idl);
433de7
+        if (id != NOID) {
433de7
+            info->key_found = PR_TRUE;
433de7
+        } else {
433de7
+            /* there is no ID in that key, make it as it was not found */
433de7
+            id = 0;
433de7
+        }
433de7
+        idl_free(&idl);
433de7
+    }
433de7
+
433de7
+    /* now set the returned id */
433de7
+    info->id = id;
433de7
+
433de7
+    return rc;
433de7
+}
433de7
+
433de7
 IDList *
433de7
 index_range_read_ext(
433de7
     Slapi_PBlock *pb,
433de7
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
433de7
index 0646cdfdd..4b75654e7 100644
433de7
--- a/ldap/servers/slapd/slapi-plugin.h
433de7
+++ b/ldap/servers/slapd/slapi-plugin.h
433de7
@@ -7763,9 +7763,17 @@ enum
433de7
     BACK_INFO_CRYPT_DECRYPT_VALUE, /* Ctrl: clcrypt_decrypt_value */
433de7
     BACK_INFO_DIRECTORY,           /* Get the directory path */
433de7
     BACK_INFO_LOG_DIRECTORY,       /* Get the txn log directory */
433de7
-    BACK_INFO_IS_ENTRYRDN          /* Get the flag for entryrdn */
433de7
+    BACK_INFO_IS_ENTRYRDN,         /* Get the flag for entryrdn */
433de7
+    BACK_INFO_INDEX_KEY            /* Get the status of a key in an index */
433de7
 };
433de7
 
433de7
+struct _back_info_index_key
433de7
+{
433de7
+    char *index;              /* input: name of the index (parentid) */
433de7
+    char *key;                /* input: searched key (0) with equality -> '=0' */
433de7
+    PRBool key_found;         /* output: TRUE if '=0' is found in the index */
433de7
+    u_int32_t id;             /* output: if key_found it is the first value (suffix entryID) */
433de7
+};
433de7
 struct _back_info_crypt_init
433de7
 {
433de7
     char *dn;                  /* input -- entry to store nsSymmetricKey */
433de7
-- 
433de7
2.17.2
433de7