From 4fd2c69b0799dc581d270262880444584ae17599 Mon Sep 17 00:00:00 2001 From: Noriko Hosoi Date: Fri, 9 May 2014 10:10:49 -0700 Subject: [PATCH 210/225] Ticket #47764 - Problem with deletion while replicated Bug description: When checking a child entry on a node, it only checked the first position, which was normally "deleted" if there were no more children. But in some cases, a tombstoned child was placed there. If it occurred, even though there were no live child any more, _entryrdn_delete_key returned "has children" and the delete operation failed. Fix description: This patch checks all the children of the to-be- deleted node and if there is no child or all of them are tombstones, it goes to the next process. Also, the fixed a typo reported by chatfield (Thank you!!) https://fedorahosted.org/389/ticket/47764 Reviewed by chatfield and mreynolds@redhat.com (Thank you both!!) (cherry picked from commit 832253ea96b83e7ebc16fe507d77090e87aed1c2) (cherry picked from commit 86b34ca0a002715a263a56b1e8c870dd3035bce4) (cherry picked from commit efa23ced2a6e3de3389d9b801329066f511bc38c) (cherry picked from commit ca407f8a684e58067440f57049794c47255cf716) --- ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c | 92 +++++++++++++++++++--------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c index 6426fb7..38b407d 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c @@ -2795,6 +2795,8 @@ _entryrdn_delete_key(backend *be, int issuffix = 0; Slapi_RDN *tmpsrdn = NULL; int db_retry = 0; + int done = 0; + char buffer[RDN_BULK_FETCH_BUFFER_SIZE]; slapi_log_error(SLAPI_LOG_TRACE, ENTRYRDN_TAG, "--> _entryrdn_delete_key\n"); @@ -2828,45 +2830,79 @@ _entryrdn_delete_key(backend *be, /* check if the target element has a child or not */ keybuf = slapi_ch_smprintf("%c%u", RDN_INDEX_CHILD, id); key.data = (void *)keybuf; - key.size = key.ulen = strlen(nrdn) + 1; + key.size = key.ulen = strlen(keybuf) + 1; key.flags = DB_DBT_USERMEM; + /* Setting the bulk fetch buffer */ memset(&data, 0, sizeof(data)); - data.flags = DB_DBT_MALLOC; + data.ulen = sizeof(buffer); + data.size = sizeof(buffer); + data.data = buffer; + data.flags = DB_DBT_USERMEM; - for (db_retry = 0; db_retry < RETRY_TIMES; db_retry++) { - rc = cursor->c_get(cursor, &key, &data, DB_SET); - if (rc) { + done = 0; + while (!done) { + rc = cursor->c_get(cursor, &key, &data, DB_SET|DB_MULTIPLE); + if (DB_LOCK_DEADLOCK == rc) { + slapi_log_error(ENTRYRDN_LOGLEVEL(rc), ENTRYRDN_TAG, + "_entryrdn_delete_key: cursor get deadlock\n"); +#ifdef FIX_TXN_DEADLOCKS +#error if txn != NULL, have to retry the entire transaction +#endif + /* try again */ + continue; + } else if (DB_NOTFOUND == rc) { + /* no children; ok */ + done = 1; + continue; + } else if (rc) { + _entryrdn_cursor_print_error("_entryrdn_delete_key", + key.data, data.size, data.ulen, rc); + goto bail; + } + + do { + rdn_elem *childelem = NULL; + DBT dataret; + void *ptr; + DB_MULTIPLE_INIT(ptr, &data); + do { + memset(&dataret, 0, sizeof(dataret)); + DB_MULTIPLE_NEXT(ptr, &data, dataret.data, dataret.size); + if (NULL == dataret.data || NULL == ptr) { + break; + } + childelem = (rdn_elem *)dataret.data; + if (!slapi_is_special_rdn(childelem->rdn_elem_nrdn_rdn, RDN_IS_TOMBSTONE)) { + /* there's at least one live child */ + slapi_log_error(SLAPI_LOG_FATAL, ENTRYRDN_TAG, + "_entryrdn_delete_key: Failed to remove %s; " + "has a child %s\n", nrdn, + (char *)childelem->rdn_elem_nrdn_rdn); + rc = -1; + goto bail; + } + } while (NULL != dataret.data && NULL != ptr); +retry_get: + rc = cursor->c_get(cursor, &key, &data, DB_NEXT_DUP|DB_MULTIPLE); if (DB_LOCK_DEADLOCK == rc) { slapi_log_error(ENTRYRDN_LOGLEVEL(rc), ENTRYRDN_TAG, - "_entryrdn_delete_key: cursor get deadlock\n"); + "_entryrdn_delete_key: retry cursor get deadlock\n"); +#ifdef FIX_TXN_DEADLOCKS +#error if txn != NULL, have to retry the entire transaction +#endif /* try again */ - if (db_txn) { - goto bail; /* have to abort/retry the entire transaction */ - } else { - ENTRYRDN_DELAY; /* sleep for a bit then retry immediately */ - } - } else if (DB_NOTFOUND != rc) { + goto retry_get; + } else if (DB_NOTFOUND == rc) { + rc = 0; + done = 1; + break; + } else if (rc) { _entryrdn_cursor_print_error("_entryrdn_delete_key", key.data, data.size, data.ulen, rc); goto bail; - } else { - break; /* DB_NOTFOUND - ok */ } - } else { - slapi_ch_free(&data.data); - slapi_log_error(SLAPI_LOG_FATAL, ENTRYRDN_TAG, - "_entryrdn_delete_key: Failed to remove %s; " - "has children\n", nrdn); - rc = -1; - goto bail; - } - } - if (RETRY_TIMES == db_retry) { - slapi_log_error(SLAPI_LOG_FATAL, ENTRYRDN_TAG, - "_entryrdn_delete_key: failed after [%d] iterations\n", db_retry); - rc = DB_LOCK_DEADLOCK; - goto bail; + } while (0 == rc); } workid = id; -- 1.8.1.4