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

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