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