Blame SOURCES/0036-Ticket-48118-At-startup-changelog-can-be-erronously-.patch

b045b9
From 127e0d954eb7741c4afdc0305f7970b7ea164e8d Mon Sep 17 00:00:00 2001
b045b9
From: Ludwig Krispenz <lkrispen@redhat.com>
b045b9
Date: Thu, 9 Nov 2017 11:28:34 +0100
b045b9
Subject: [PATCH]     Ticket 48118 - At startup, changelog can be erronously
b045b9
 rebuilt after a normal shutdown
b045b9
b045b9
    Problem: There are two problems that can lead to inconsistent database and changelog maxruv:
b045b9
    1] the database ruv is written periodically in th ehouskeeping thread and at shutdown. It
b045b9
       relies on teh ruv_dirty flag, but due to a race condition this can be reset befor writing
b045b9
       the ruv
b045b9
    2] the changelog max ruv is updated whenever an operation is commutted, but in case of internal
b045b9
       operations inside the txn for a client operation, if the operation is aborted the cl maxruv
b045b9
       is not reset. Since it is only written at shutdown this normally is no problem, but if the
b045b9
       aborted operation is the last before shutdown or is aborted by shutdown the cl ruv is incorrect
b045b9
b045b9
    Fix: the fix is in two parts:
b045b9
    1] remove the use of the dirty flag, ensure that the ruv is always written. The overhead for writing
b045b9
       a database ruv that has not changed is minimal
b045b9
    2] when writing the changelog maxruv check if the macsns it contains are really present in the
b045b9
       changelog. If not the maxruv is not written, it will be reconstructed at the next startup
b045b9
b045b9
    Reviewed by: William,Thierry - Thanks
b045b9
---
b045b9
 ldap/servers/plugins/replication/cl5_api.c         | 39 ++++++++++++++++++++++
b045b9
 ldap/servers/plugins/replication/repl5.h           |  1 -
b045b9
 ldap/servers/plugins/replication/repl5_replica.c   | 32 +-----------------
b045b9
 .../plugins/replication/repl5_replica_config.c     |  2 --
b045b9
 4 files changed, 40 insertions(+), 34 deletions(-)
b045b9
b045b9
diff --git a/ldap/servers/plugins/replication/cl5_api.c b/ldap/servers/plugins/replication/cl5_api.c
b045b9
index ec648c014..55032dfb0 100644
b045b9
--- a/ldap/servers/plugins/replication/cl5_api.c
b045b9
+++ b/ldap/servers/plugins/replication/cl5_api.c
b045b9
@@ -250,6 +250,8 @@ static void _cl5ReadBerval(struct berval *bv, char **buff);
b045b9
 static void _cl5WriteBerval(struct berval *bv, char **buff);
b045b9
 static int _cl5ReadBervals(struct berval ***bv, char **buff, unsigned int size);
b045b9
 static int _cl5WriteBervals(struct berval **bv, char **buff, u_int32_t *size);
b045b9
+static int64_t _cl5CheckMaxRUV(CL5DBFile *file, RUV *maxruv);
b045b9
+static int64_t _cl5CheckCSNinCL(const ruv_enum_data *element, void *arg);
b045b9
 
b045b9
 /* replay iteration */
b045b9
 #ifdef FOR_DEBUGGING
b045b9
@@ -2716,6 +2718,36 @@ _cl5WriteBervals(struct berval **bv, char **buff, u_int32_t *size)
b045b9
     return CL5_SUCCESS;
b045b9
 }
b045b9
 
b045b9
+static int64_t
b045b9
+_cl5CheckCSNinCL(const ruv_enum_data *element, void *arg)
b045b9
+{
b045b9
+    CL5DBFile *file = (CL5DBFile *)arg;
b045b9
+    int rc = 0;
b045b9
+
b045b9
+    DBT key = {0}, data = {0};
b045b9
+    char csnStr[CSN_STRSIZE];
b045b9
+
b045b9
+    /* construct the key */
b045b9
+    key.data = csn_as_string(element->csn, PR_FALSE, csnStr);
b045b9
+    key.size = CSN_STRSIZE;
b045b9
+
b045b9
+    data.flags = DB_DBT_MALLOC;
b045b9
+
b045b9
+    rc = file->db->get(file->db, NULL /*txn*/, &key, &data, 0);
b045b9
+
b045b9
+    slapi_ch_free(&(data.data));
b045b9
+    return rc;
b045b9
+}
b045b9
+
b045b9
+static int64_t
b045b9
+_cl5CheckMaxRUV(CL5DBFile *file, RUV *maxruv)
b045b9
+{
b045b9
+    int rc = 0;
b045b9
+
b045b9
+    rc = ruv_enumerate_elements(maxruv, _cl5CheckCSNinCL, (void *)file);
b045b9
+
b045b9
+    return rc;
b045b9
+}
b045b9
 /* upgrade from db33 to db41
b045b9
  * 1. Run recovery on the database environment using the DB_ENV->open method
b045b9
  * 2. Remove any Berkeley DB environment using the DB_ENV->remove method
b045b9
@@ -4010,6 +4042,13 @@ _cl5WriteRUV(CL5DBFile *file, PRBool purge)
b045b9
         rc = ruv_to_bervals(file->maxRUV, &vals);
b045b9
     }
b045b9
 
b045b9
+    if (!purge && _cl5CheckMaxRUV(file, file->maxRUV)) {
b045b9
+        slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name_cl,
b045b9
+                      "_cl5WriteRUV - changelog maxRUV not found in changelog for file %s\n",
b045b9
+                      file->name);
b045b9
+        return CL5_DB_ERROR;
b045b9
+    }
b045b9
+
b045b9
     key.size = CSN_STRSIZE;
b045b9
 
b045b9
     rc = _cl5WriteBervals(vals, &buff, &data.size);
b045b9
diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
b045b9
index c6e79b7e2..4e206a0fc 100644
b045b9
--- a/ldap/servers/plugins/replication/repl5.h
b045b9
+++ b/ldap/servers/plugins/replication/repl5.h
b045b9
@@ -725,7 +725,6 @@ Object *replica_get_for_backend(const char *be_name);
b045b9
 void replica_set_purge_delay(Replica *r, uint32_t purge_delay);
b045b9
 void replica_set_tombstone_reap_interval(Replica *r, long interval);
b045b9
 void replica_update_ruv_consumer(Replica *r, RUV *supplier_ruv);
b045b9
-void replica_set_ruv_dirty(Replica *r);
b045b9
 Slapi_Entry *get_in_memory_ruv(Slapi_DN *suffix_sdn);
b045b9
 int replica_write_ruv(Replica *r);
b045b9
 char *replica_get_dn(Replica *r);
b045b9
diff --git a/ldap/servers/plugins/replication/repl5_replica.c b/ldap/servers/plugins/replication/repl5_replica.c
b045b9
index e5296bf1c..77f4f18e4 100644
b045b9
--- a/ldap/servers/plugins/replication/repl5_replica.c
b045b9
+++ b/ldap/servers/plugins/replication/repl5_replica.c
b045b9
@@ -41,7 +41,6 @@ struct replica
b045b9
     ReplicaType repl_type;             /* is this replica read-only ? */
b045b9
     ReplicaId repl_rid;                /* replicaID */
b045b9
     Object *repl_ruv;                  /* replica update vector */
b045b9
-    PRBool repl_ruv_dirty;             /* Dirty flag for ruv */
b045b9
     CSNPL *min_csn_pl;                 /* Pending list for minimal CSN */
b045b9
     void *csn_pl_reg_id;               /* registration assignment for csn callbacks */
b045b9
     unsigned long repl_state_flags;    /* state flags */
b045b9
@@ -788,7 +787,6 @@ replica_set_ruv(Replica *r, RUV *ruv)
b045b9
     }
b045b9
 
b045b9
     r->repl_ruv = object_new((void *)ruv, (FNFree)ruv_destroy);
b045b9
-    r->repl_ruv_dirty = PR_TRUE;
b045b9
 
b045b9
     replica_unlock(r->repl_lock);
b045b9
 }
b045b9
@@ -860,9 +858,6 @@ replica_update_ruv(Replica *r, const CSN *updated_csn, const char *replica_purl)
b045b9
                                                     "to update RUV for replica %s, csn = %s\n",
b045b9
                                   slapi_sdn_get_dn(r->repl_root),
b045b9
                                   csn_as_string(updated_csn, PR_FALSE, csn_str));
b045b9
-                } else {
b045b9
-                    /* RUV updated - mark as dirty */
b045b9
-                    r->repl_ruv_dirty = PR_TRUE;
b045b9
                 }
b045b9
             } else {
b045b9
                 slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
b045b9
@@ -1347,8 +1342,6 @@ replica_dump(Replica *r)
b045b9
     slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "\tupdate dn: %s\n",
b045b9
                   updatedn_list ? updatedn_list : "not configured");
b045b9
     slapi_ch_free_string(&updatedn_list);
b045b9
-    slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "\truv: %s configured and is %sdirty\n",
b045b9
-                  r->repl_ruv ? "" : "not", r->repl_ruv_dirty ? "" : "not ");
b045b9
     slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "\tCSN generator: %s configured\n",
b045b9
                   r->repl_csngen ? "" : "not");
b045b9
     /* JCMREPL - Dump Referrals */
b045b9
@@ -1675,7 +1668,6 @@ replica_check_for_data_reload(Replica *r, void *arg __attribute__((unused)))
b045b9
 
b045b9
                     ruv_force_csn_update_from_ruv(upper_bound_ruv, r_ruv,
b045b9
                                                   "Force update of database RUV (from CL RUV) -> ", SLAPI_LOG_NOTICE);
b045b9
-                    replica_set_ruv_dirty(r);
b045b9
                 }
b045b9
 
b045b9
             } else {
b045b9
@@ -2778,11 +2770,6 @@ replica_write_ruv(Replica *r)
b045b9
 
b045b9
     replica_lock(r->repl_lock);
b045b9
 
b045b9
-    if (!r->repl_ruv_dirty) {
b045b9
-        replica_unlock(r->repl_lock);
b045b9
-        return rc;
b045b9
-    }
b045b9
-
b045b9
     PR_ASSERT(r->repl_ruv);
b045b9
 
b045b9
     ruv_to_smod((RUV *)object_get_data(r->repl_ruv), &smod);
b045b9
@@ -2817,14 +2804,10 @@ replica_write_ruv(Replica *r)
b045b9
     /* ruv does not exist - create one */
b045b9
     replica_lock(r->repl_lock);
b045b9
 
b045b9
-    if (rc == LDAP_SUCCESS) {
b045b9
-        r->repl_ruv_dirty = PR_FALSE;
b045b9
-    } else if (rc == LDAP_NO_SUCH_OBJECT) {
b045b9
+    if (rc == LDAP_NO_SUCH_OBJECT) {
b045b9
         /* this includes an internal operation - but since this only happens
b045b9
            during server startup - its ok that we have lock around it */
b045b9
         rc = _replica_configure_ruv(r, PR_TRUE);
b045b9
-        if (rc == 0)
b045b9
-            r->repl_ruv_dirty = PR_FALSE;
b045b9
     } else /* error */
b045b9
     {
b045b9
         slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,
b045b9
@@ -3325,7 +3308,6 @@ replica_create_ruv_tombstone(Replica *r)
b045b9
 
b045b9
             if (ruv_init_new(csnstr, r->repl_rid, purl, &ruv) == RUV_SUCCESS) {
b045b9
                 r->repl_ruv = object_new((void *)ruv, (FNFree)ruv_destroy);
b045b9
-                r->repl_ruv_dirty = PR_TRUE;
b045b9
                 return_value = LDAP_SUCCESS;
b045b9
             } else {
b045b9
                 slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "replica_create_ruv_tombstone - "
b045b9
@@ -3365,8 +3347,6 @@ replica_create_ruv_tombstone(Replica *r)
b045b9
     slapi_add_internal_pb(pb);
b045b9
     e = NULL; /* add consumes e, upon success or failure */
b045b9
     slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &return_value);
b045b9
-    if (return_value == LDAP_SUCCESS)
b045b9
-        r->repl_ruv_dirty = PR_FALSE;
b045b9
 
b045b9
 done:
b045b9
     slapi_entry_free(e);
b045b9
@@ -3630,7 +3610,6 @@ replica_strip_cleaned_rids(Replica *r)
b045b9
     ruv_get_cleaned_rids(ruv, rid);
b045b9
     while (rid[i] != 0) {
b045b9
         ruv_delete_replica(ruv, rid[i]);
b045b9
-        replica_set_ruv_dirty(r);
b045b9
         if (replica_write_ruv(r)) {
b045b9
             slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,
b045b9
                           "replica_strip_cleaned_rids - Failed to write RUV\n");
b045b9
@@ -3744,15 +3723,6 @@ replica_update_ruv_consumer(Replica *r, RUV *supplier_ruv)
b045b9
     }
b045b9
 }
b045b9
 
b045b9
-void
b045b9
-replica_set_ruv_dirty(Replica *r)
b045b9
-{
b045b9
-    PR_ASSERT(r);
b045b9
-    replica_lock(r->repl_lock);
b045b9
-    r->repl_ruv_dirty = PR_TRUE;
b045b9
-    replica_unlock(r->repl_lock);
b045b9
-}
b045b9
-
b045b9
 PRBool
b045b9
 replica_is_state_flag_set(Replica *r, int32_t flag)
b045b9
 {
b045b9
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
b045b9
index 9c8d6adbb..e025f34d8 100644
b045b9
--- a/ldap/servers/plugins/replication/repl5_replica_config.c
b045b9
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
b045b9
@@ -937,7 +937,6 @@ replica_config_change_type_and_id(Replica *r, const char *new_type, const char *
b045b9
                     replica_reset_csn_pl(r);
b045b9
                 }
b045b9
                 ruv_delete_replica(ruv, oldrid);
b045b9
-                replica_set_ruv_dirty(r);
b045b9
                 cl5CleanRUV(oldrid);
b045b9
                 replica_set_csn_assigned(r);
b045b9
             }
b045b9
@@ -1323,7 +1322,6 @@ replica_execute_cleanruv_task(Object *r, ReplicaId rid, char *returntext __attri
b045b9
         return LDAP_UNWILLING_TO_PERFORM;
b045b9
     }
b045b9
     rc = ruv_delete_replica(local_ruv, rid);
b045b9
-    replica_set_ruv_dirty(replica);
b045b9
     if (replica_write_ruv(replica)) {
b045b9
         slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "cleanAllRUV_task - Could not write RUV\n");
b045b9
     }
b045b9
-- 
b045b9
2.13.6
b045b9