andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone
Blob Blame History Raw
From 4fd2c69b0799dc581d270262880444584ae17599 Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
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