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