ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c | 92 ++++++++++++++++++---------
1 file changed, 64 insertions(+), 28 deletions(-)
New commits:
commit efa23ced2a6e3de3389d9b801329066f511bc38c
Author: Noriko Hosoi <nhosoi(a)redhat.com>
Date: Fri May 9 10:10:49 2014 -0700
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(a)redhat.com (Thank you both!!)
(cherry picked from commit 832253ea96b83e7ebc16fe507d77090e87aed1c2)
(cherry picked from commit 86b34ca0a002715a263a56b1e8c870dd3035bce4)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
index 34b7f70..292f4fd 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
@@ -2802,6 +2802,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");
@@ -2835,45 +2837,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;