andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 6 months ago
Clone

Blame SOURCES/0032-Issue-4943-Fix-csn-generator-to-limit-time-skew-drif.patch

10de5f
From 83dfeca912daf72b2bb73288423e634091747979 Mon Sep 17 00:00:00 2001
10de5f
From: progier389 <progier@redhat.com>
10de5f
Date: Mon, 25 Oct 2021 17:09:57 +0200
10de5f
Subject: [PATCH] Issue 4943 - Fix csn generator to limit time skew drift - PR
10de5f
 4946
10de5f
10de5f
---
10de5f
 ldap/servers/slapd/csngen.c       | 459 +++++++++++++++++-------------
10de5f
 ldap/servers/slapd/slapi-plugin.h |   9 +
10de5f
 2 files changed, 268 insertions(+), 200 deletions(-)
10de5f
10de5f
diff --git a/ldap/servers/slapd/csngen.c b/ldap/servers/slapd/csngen.c
10de5f
index b08d8b25c..c7c5c2ba8 100644
10de5f
--- a/ldap/servers/slapd/csngen.c
10de5f
+++ b/ldap/servers/slapd/csngen.c
10de5f
@@ -18,8 +18,9 @@
10de5f
 #include "prcountr.h"
10de5f
 #include "slap.h"
10de5f
 
10de5f
+
10de5f
 #define CSN_MAX_SEQNUM 0xffff              /* largest sequence number */
10de5f
-#define CSN_MAX_TIME_ADJUST 24 * 60 * 60   /* maximum allowed time adjustment (in seconds) = 1 day */
10de5f
+#define CSN_MAX_TIME_ADJUST _SEC_PER_DAY   /* maximum allowed time adjustment (in seconds) = 1 day */
10de5f
 #define ATTR_CSN_GENERATOR_STATE "nsState" /* attribute that stores csn state information */
10de5f
 #define STATE_FORMAT "%8x%8x%8x%4hx%4hx"
10de5f
 #define STATE_LENGTH 32
10de5f
@@ -27,6 +28,8 @@
10de5f
 #define CSN_CALC_TSTAMP(gen) ((gen)->state.sampled_time + \
10de5f
                               (gen)->state.local_offset + \
10de5f
                               (gen)->state.remote_offset)
10de5f
+#define TIME_DIFF_WARNING_DELAY  (30*_SEC_PER_DAY)  /* log an info message when difference
10de5f
+                                                       between clock is greater than this delay */
10de5f
 
10de5f
 /*
10de5f
  * **************************************************************************
10de5f
@@ -63,6 +66,7 @@ typedef struct csngen_state
10de5f
 struct csngen
10de5f
 {
10de5f
     csngen_state state;      /* persistent state of the generator */
10de5f
+    int32_t (*gettime)(struct timespec *tp); /* Get local time */
10de5f
     callback_list callbacks; /* list of callbacks registered with the generator */
10de5f
     Slapi_RWLock *lock;      /* concurrency control */
10de5f
 };
10de5f
@@ -78,7 +82,7 @@ static int _csngen_init_callbacks(CSNGen *gen);
10de5f
 static void _csngen_call_callbacks(const CSNGen *gen, const CSN *csn, PRBool abort);
10de5f
 static int _csngen_cmp_callbacks(const void *el1, const void *el2);
10de5f
 static void _csngen_free_callbacks(CSNGen *gen);
10de5f
-static int _csngen_adjust_local_time(CSNGen *gen, time_t cur_time);
10de5f
+static int _csngen_adjust_local_time(CSNGen *gen);
10de5f
 
10de5f
 /*
10de5f
  * **************************************************************************
10de5f
@@ -121,6 +125,7 @@ csngen_new(ReplicaId rid, Slapi_Attr *state)
10de5f
     _csngen_init_callbacks(gen);
10de5f
 
10de5f
     gen->state.rid = rid;
10de5f
+    gen->gettime = slapi_clock_utc_gettime;
10de5f
 
10de5f
     if (state) {
10de5f
         rc = _csngen_parse_state(gen, state);
10de5f
@@ -164,10 +169,7 @@ csngen_free(CSNGen **gen)
10de5f
 int
10de5f
 csngen_new_csn(CSNGen *gen, CSN **csn, PRBool notify)
10de5f
 {
10de5f
-    struct timespec now = {0};
10de5f
     int rc = CSN_SUCCESS;
10de5f
-    time_t cur_time;
10de5f
-    int delta;
10de5f
 
10de5f
     if (gen == NULL || csn == NULL) {
10de5f
         slapi_log_err(SLAPI_LOG_ERR, "csngen_new_csn", "Invalid argument\n");
10de5f
@@ -180,39 +182,13 @@ csngen_new_csn(CSNGen *gen, CSN **csn, PRBool notify)
10de5f
         return CSN_MEMORY_ERROR;
10de5f
     }
10de5f
 
10de5f
-    if ((rc = slapi_clock_gettime(&now)) != 0) {
10de5f
-        /* Failed to get system time, we must abort */
10de5f
-        slapi_log_err(SLAPI_LOG_ERR, "csngen_new_csn",
10de5f
-                "Failed to get system time (%s)\n",
10de5f
-                slapd_system_strerror(rc));
10de5f
-        return CSN_TIME_ERROR;
10de5f
-    }
10de5f
-    cur_time = now.tv_sec;
10de5f
-
10de5f
     slapi_rwlock_wrlock(gen->lock);
10de5f
 
10de5f
-    /* check if the time should be adjusted */
10de5f
-    delta = cur_time - gen->state.sampled_time;
10de5f
-    if (delta > _SEC_PER_DAY || delta < (-1 * _SEC_PER_DAY)) {
10de5f
-        /* We had a jump larger than a day */
10de5f
-        slapi_log_err(SLAPI_LOG_INFO, "csngen_new_csn",
10de5f
-                "Detected large jump in CSN time.  Delta: %d (current time: %ld  vs  previous time: %ld)\n",
10de5f
-                delta, cur_time, gen->state.sampled_time);
10de5f
-    }
10de5f
-    if (delta > 0) {
10de5f
-        rc = _csngen_adjust_local_time(gen, cur_time);
10de5f
-        if (rc != CSN_SUCCESS) {
10de5f
-            slapi_rwlock_unlock(gen->lock);
10de5f
-            return rc;
10de5f
-        }
10de5f
+    rc = _csngen_adjust_local_time(gen);
10de5f
+    if (rc != CSN_SUCCESS) {
10de5f
+        slapi_rwlock_unlock(gen->lock);
10de5f
+        return rc;
10de5f
     }
10de5f
-    /* if (delta < 0) this means the local system time was set back
10de5f
-     * the new csn will be generated based on sampled time, which is
10de5f
-     * ahead of system time and previously generated csns.
10de5f
-     * the time stamp of the csn will not change until system time
10de5f
-     * catches up or is corrected by remote csns.
10de5f
-     * But we need to ensure that the seq_num does not overflow.
10de5f
-     */
10de5f
 
10de5f
     if (gen->state.seq_num == CSN_MAX_SEQNUM) {
10de5f
         slapi_log_err(SLAPI_LOG_INFO, "csngen_new_csn", "Sequence rollover; "
10de5f
@@ -261,13 +237,36 @@ csngen_rewrite_rid(CSNGen *gen, ReplicaId rid)
10de5f
 }
10de5f
 
10de5f
 /* this function should be called when a remote CSN for the same part of
10de5f
-   the dit becomes known to the server (for instance, as part of RUV during
10de5f
-   replication session. In response, the generator would adjust its notion
10de5f
-   of time so that it does not generate smaller csns */
10de5f
+ * the dit becomes known to the server (for instance, as part of RUV during
10de5f
+ * replication session. In response, the generator would adjust its notion
10de5f
+ * of time so that it does not generate smaller csns
10de5f
+ *
10de5f
+ * The following counters are updated
10de5f
+ *   - when a new csn is generated
10de5f
+ *   - when csngen is adjusted (beginning of a incoming (extop) or outgoing
10de5f
+ *     (inc_protocol) session)
10de5f
+ *
10de5f
+ * sampled_time: It takes the value of current system time.
10de5f
+ *
10de5f
+ * remote offset: it is updated when 'csn' argument is ahead of the next csn
10de5f
+ * that the csn generator will generate. It is the MAX jump ahead, it is not
10de5f
+ * cumulative counter (e.g. if remote_offset=7 and 'csn' is 5sec ahead
10de5f
+ * remote_offset stays the same. The jump ahead (5s) pour into the local offset.
10de5f
+ * It is not clear of the interest of this counter. It gives an indication of
10de5f
+ * the maximum jump ahead but not much.
10de5f
+ *
10de5f
+ * local offset: it is increased if
10de5f
+ *   - system time is going backward (compare sampled_time)
10de5f
+ *   - if 'csn' argument is ahead of csn that the csn generator would generate
10de5f
+ *     AND diff('csn', csngen.new_csn) < remote_offset
10de5f
+ *     then the diff "pour" into local_offset
10de5f
+ *  It is decreased as the clock is ticking, local offset is "consumed" as
10de5f
+ *  sampled_time progresses.
10de5f
+ */
10de5f
 int
10de5f
 csngen_adjust_time(CSNGen *gen, const CSN *csn)
10de5f
 {
10de5f
-    time_t remote_time, remote_offset, cur_time;
10de5f
+    time_t remote_time, remote_offset, cur_time, old_time, new_time;
10de5f
     PRUint16 remote_seqnum;
10de5f
     int rc;
10de5f
     extern int config_get_ignore_time_skew(void);
10de5f
@@ -281,6 +280,11 @@ csngen_adjust_time(CSNGen *gen, const CSN *csn)
10de5f
 
10de5f
     slapi_rwlock_wrlock(gen->lock);
10de5f
 
10de5f
+    /* Get last local csn time */
10de5f
+    old_time = CSN_CALC_TSTAMP(gen);
10de5f
+    /* update local offset and sample_time */
10de5f
+    rc = _csngen_adjust_local_time(gen);
10de5f
+
10de5f
     if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
         cur_time = CSN_CALC_TSTAMP(gen);
10de5f
         slapi_log_err(SLAPI_LOG_REPL, "csngen_adjust_time",
10de5f
@@ -290,79 +294,60 @@ csngen_adjust_time(CSNGen *gen, const CSN *csn)
10de5f
                       gen->state.local_offset,
10de5f
                       gen->state.remote_offset);
10de5f
     }
10de5f
-    /* make sure we have the current time */
10de5f
-    cur_time = slapi_current_utc_time();
10de5f
-
10de5f
-    /* make sure sampled_time is current */
10de5f
-    /* must only call adjust_local_time if the current time is greater than
10de5f
-       the generator state time */
10de5f
-    if ((cur_time > gen->state.sampled_time) &&
10de5f
-        (CSN_SUCCESS != (rc = _csngen_adjust_local_time(gen, cur_time)))) {
10de5f
+    if (rc != CSN_SUCCESS) {
10de5f
         /* _csngen_adjust_local_time will log error */
10de5f
         slapi_rwlock_unlock(gen->lock);
10de5f
-        csngen_dump_state(gen);
10de5f
+        csngen_dump_state(gen, SLAPI_LOG_DEBUG);
10de5f
         return rc;
10de5f
     }
10de5f
 
10de5f
-    cur_time = CSN_CALC_TSTAMP(gen);
10de5f
-    if (remote_time >= cur_time) {
10de5f
-        time_t new_time = 0;
10de5f
-
10de5f
-        if (remote_seqnum > gen->state.seq_num) {
10de5f
-            if (remote_seqnum < CSN_MAX_SEQNUM) {
10de5f
-                gen->state.seq_num = remote_seqnum + 1;
10de5f
-            } else {
10de5f
-                remote_time++;
10de5f
-            }
10de5f
-        }
10de5f
-
10de5f
-        remote_offset = remote_time - cur_time;
10de5f
-        if (remote_offset > gen->state.remote_offset) {
10de5f
-            if (ignore_time_skew || (remote_offset <= CSN_MAX_TIME_ADJUST)) {
10de5f
-                gen->state.remote_offset = remote_offset;
10de5f
-            } else /* remote_offset > CSN_MAX_TIME_ADJUST */
10de5f
-            {
10de5f
-                slapi_log_err(SLAPI_LOG_ERR, "csngen_adjust_time",
10de5f
-                              "Adjustment limit exceeded; value - %ld, limit - %ld\n",
10de5f
-                              remote_offset, (long)CSN_MAX_TIME_ADJUST);
10de5f
-                slapi_rwlock_unlock(gen->lock);
10de5f
-                csngen_dump_state(gen);
10de5f
-                return CSN_LIMIT_EXCEEDED;
10de5f
-            }
10de5f
-        } else if (remote_offset > 0) { /* still need to account for this */
10de5f
-            gen->state.local_offset += remote_offset;
10de5f
+    remote_offset = remote_time - CSN_CALC_TSTAMP(gen);
10de5f
+    if (remote_offset > 0) {
10de5f
+        if (!ignore_time_skew && (gen->state.remote_offset + remote_offset > CSN_MAX_TIME_ADJUST)) {
10de5f
+            slapi_log_err(SLAPI_LOG_ERR, "csngen_adjust_time",
10de5f
+                          "Adjustment limit exceeded; value - %ld, limit - %ld\n",
10de5f
+                          remote_offset, (long)CSN_MAX_TIME_ADJUST);
10de5f
+            slapi_rwlock_unlock(gen->lock);
10de5f
+            csngen_dump_state(gen, SLAPI_LOG_DEBUG);
10de5f
+            return CSN_LIMIT_EXCEEDED;
10de5f
         }
10de5f
-
10de5f
-        new_time = CSN_CALC_TSTAMP(gen);
10de5f
-        /* let's revisit the seq num - if the new time is > the old
10de5f
-           tiem, we should reset the seq number to remote + 1 if
10de5f
-           this won't cause a wrap around */
10de5f
-        if (new_time >= cur_time) {
10de5f
-            /* just set seq_num regardless of whether the current one
10de5f
-               is < or > than the remote one - the goal of this function
10de5f
-               is to make sure we generate CSNs > the remote CSN - if
10de5f
-               we have increased the time, we can decrease the seqnum
10de5f
-               and still guarantee that any new CSNs generated will be
10de5f
-               > any current CSNs we have generated */
10de5f
-            if (remote_seqnum < gen->state.seq_num) {
10de5f
-                gen->state.seq_num ++;
10de5f
-            } else {
10de5f
-                gen->state.seq_num = remote_seqnum + 1;
10de5f
-            }
10de5f
+        gen->state.remote_offset += remote_offset;
10de5f
+        /* To avoid beat phenomena between suppliers let put 1 second in local_offset
10de5f
+         * it will be eaten at next clock tick rather than increasing remote offset
10de5f
+         * If we do not do that we will have a time skew drift of 1 second per 2 seconds
10de5f
+         * if suppliers are desynchronized by 0.5 second 
10de5f
+         */
10de5f
+        if (gen->state.local_offset == 0) {
10de5f
+            gen->state.local_offset++;
10de5f
+            gen->state.remote_offset--;
10de5f
         }
10de5f
-        if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
-            slapi_log_err(SLAPI_LOG_REPL, "csngen_adjust_time",
10de5f
-                          "gen state after %08lx%04x:%ld:%ld:%ld\n",
10de5f
-                          new_time, gen->state.seq_num,
10de5f
-                          gen->state.sampled_time,
10de5f
-                          gen->state.local_offset,
10de5f
-                          gen->state.remote_offset);
10de5f
+    }
10de5f
+    /* Time to compute seqnum so that 
10de5f
+     *   new csn >= remote csn and new csn >= old local csn 
10de5f
+     */
10de5f
+    new_time = CSN_CALC_TSTAMP(gen);
10de5f
+    PR_ASSERT(new_time >= old_time);
10de5f
+    PR_ASSERT(new_time >= remote_time);
10de5f
+    if (new_time > old_time) {
10de5f
+        /* Can reset (local) seqnum */
10de5f
+        gen->state.seq_num = 0;
10de5f
+    }
10de5f
+    if (new_time == remote_time && remote_seqnum >= gen->state.seq_num) {
10de5f
+        if (remote_seqnum >= CSN_MAX_SEQNUM) {
10de5f
+            gen->state.seq_num = 0;
10de5f
+            gen->state.local_offset++;
10de5f
+        } else {
10de5f
+            gen->state.seq_num = remote_seqnum + 1;
10de5f
         }
10de5f
-    } else if (gen->state.remote_offset > 0) {
10de5f
-        /* decrease remote offset? */
10de5f
-        /* how to decrease remote offset but ensure that we don't
10de5f
-           generate a duplicate CSN, or a CSN smaller than one we've already
10de5f
-           generated? */
10de5f
+    }
10de5f
+
10de5f
+    if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
+        slapi_log_err(SLAPI_LOG_REPL, "csngen_adjust_time",
10de5f
+                      "gen state after %08lx%04x:%ld:%ld:%ld\n",
10de5f
+                      new_time, gen->state.seq_num,
10de5f
+                      gen->state.sampled_time,
10de5f
+                      gen->state.local_offset,
10de5f
+                      gen->state.remote_offset);
10de5f
     }
10de5f
 
10de5f
     slapi_rwlock_unlock(gen->lock);
10de5f
@@ -435,16 +420,16 @@ csngen_unregister_callbacks(CSNGen *gen, void *cookie)
10de5f
 
10de5f
 /* debugging function */
10de5f
 void
10de5f
-csngen_dump_state(const CSNGen *gen)
10de5f
+csngen_dump_state(const CSNGen *gen, int severity)
10de5f
 {
10de5f
     if (gen) {
10de5f
         slapi_rwlock_rdlock(gen->lock);
10de5f
-        slapi_log_err(SLAPI_LOG_DEBUG, "csngen_dump_state", "CSN generator's state:\n");
10de5f
-        slapi_log_err(SLAPI_LOG_DEBUG, "csngen_dump_state", "\treplica id: %d\n", gen->state.rid);
10de5f
-        slapi_log_err(SLAPI_LOG_DEBUG, "csngen_dump_state", "\tsampled time: %ld\n", gen->state.sampled_time);
10de5f
-        slapi_log_err(SLAPI_LOG_DEBUG, "csngen_dump_state", "\tlocal offset: %ld\n", gen->state.local_offset);
10de5f
-        slapi_log_err(SLAPI_LOG_DEBUG, "csngen_dump_state", "\tremote offset: %ld\n", gen->state.remote_offset);
10de5f
-        slapi_log_err(SLAPI_LOG_DEBUG, "csngen_dump_state", "\tsequence number: %d\n", gen->state.seq_num);
10de5f
+        slapi_log_err(severity, "csngen_dump_state", "CSN generator's state:\n");
10de5f
+        slapi_log_err(severity, "csngen_dump_state", "\treplica id: %d\n", gen->state.rid);
10de5f
+        slapi_log_err(severity, "csngen_dump_state", "\tsampled time: %ld\n", gen->state.sampled_time);
10de5f
+        slapi_log_err(severity, "csngen_dump_state", "\tlocal offset: %ld\n", gen->state.local_offset);
10de5f
+        slapi_log_err(severity, "csngen_dump_state", "\tremote offset: %ld\n", gen->state.remote_offset);
10de5f
+        slapi_log_err(severity, "csngen_dump_state", "\tsequence number: %d\n", gen->state.seq_num);
10de5f
         slapi_rwlock_unlock(gen->lock);
10de5f
     }
10de5f
 }
10de5f
@@ -459,15 +444,17 @@ csngen_test()
10de5f
     CSNGen *gen = csngen_new(255, NULL);
10de5f
 
10de5f
     slapi_log_err(SLAPI_LOG_DEBUG, "csngen_test", "staring csn generator test ...");
10de5f
-    csngen_dump_state(gen);
10de5f
+    csngen_dump_state(gen, SLAPI_LOG_INFO);
10de5f
 
10de5f
     rc = _csngen_start_test_threads(gen);
10de5f
     if (rc == 0) {
10de5f
-        DS_Sleep(PR_SecondsToInterval(TEST_TIME));
10de5f
+        for (size_t i = 0; i < TEST_TIME && !slapi_is_shutting_down(); i++) {
10de5f
+            DS_Sleep(PR_SecondsToInterval(1));
10de5f
+        }
10de5f
     }
10de5f
 
10de5f
     _csngen_stop_test_threads();
10de5f
-    csngen_dump_state(gen);
10de5f
+    csngen_dump_state(gen, SLAPI_LOG_INFO);
10de5f
     slapi_log_err(SLAPI_LOG_DEBUG, "csngen_test", "csn generator test is complete...");
10de5f
 }
10de5f
 
10de5f
@@ -572,94 +559,93 @@ _csngen_cmp_callbacks(const void *el1, const void *el2)
10de5f
         return 1;
10de5f
 }
10de5f
 
10de5f
+/* Get time and adjust local offset */
10de5f
 static int
10de5f
-_csngen_adjust_local_time(CSNGen *gen, time_t cur_time)
10de5f
+_csngen_adjust_local_time(CSNGen *gen)
10de5f
 {
10de5f
     extern int config_get_ignore_time_skew(void);
10de5f
     int ignore_time_skew = config_get_ignore_time_skew();
10de5f
-    time_t time_diff = cur_time - gen->state.sampled_time;
10de5f
+    struct timespec now = {0};
10de5f
+    time_t time_diff;
10de5f
+    time_t cur_time;
10de5f
+    int rc;
10de5f
 
10de5f
+    
10de5f
+    if ((rc = gen->gettime(&now)) != 0) {
10de5f
+        /* Failed to get system time, we must abort */
10de5f
+        slapi_log_err(SLAPI_LOG_ERR, "csngen_new_csn",
10de5f
+                "Failed to get system time (%s)\n",
10de5f
+                slapd_system_strerror(rc));
10de5f
+        return CSN_TIME_ERROR;
10de5f
+    }
10de5f
+    cur_time = now.tv_sec;
10de5f
+    time_diff = cur_time - gen->state.sampled_time;
10de5f
+
10de5f
+    /* check if the time should be adjusted */
10de5f
     if (time_diff == 0) {
10de5f
         /* This is a no op - _csngen_adjust_local_time should never be called
10de5f
            in this case, because there is nothing to adjust - but just return
10de5f
            here to protect ourselves
10de5f
         */
10de5f
         return CSN_SUCCESS;
10de5f
-    } else if (time_diff > 0) {
10de5f
-        time_t ts_before = CSN_CALC_TSTAMP(gen);
10de5f
-        time_t ts_after = 0;
10de5f
-        if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
-            time_t new_time = CSN_CALC_TSTAMP(gen);
10de5f
-            slapi_log_err(SLAPI_LOG_REPL, "_csngen_adjust_local_time",
10de5f
-                          "gen state before %08lx%04x:%ld:%ld:%ld\n",
10de5f
-                          new_time, gen->state.seq_num,
10de5f
-                          gen->state.sampled_time,
10de5f
-                          gen->state.local_offset,
10de5f
-                          gen->state.remote_offset);
10de5f
-        }
10de5f
-
10de5f
-        gen->state.sampled_time = cur_time;
10de5f
-        if (time_diff > gen->state.local_offset)
10de5f
-            gen->state.local_offset = 0;
10de5f
-        else
10de5f
-            gen->state.local_offset = gen->state.local_offset - time_diff;
10de5f
-
10de5f
-        /* only reset the seq_num if the new timestamp part of the CSN
10de5f
-           is going to be greater than the old one - if they are the
10de5f
-           same after the above adjustment (which can happen if
10de5f
-           csngen_adjust_time has to store the offset in the
10de5f
-           local_offset field) we must not allow the CSN to regress or
10de5f
-           generate duplicate numbers */
10de5f
-        ts_after = CSN_CALC_TSTAMP(gen);
10de5f
-        if (ts_after > ts_before) {
10de5f
-            gen->state.seq_num = 0; /* only reset if new time > old time */
10de5f
-        }
10de5f
-
10de5f
-        if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
-            time_t new_time = CSN_CALC_TSTAMP(gen);
10de5f
-            slapi_log_err(SLAPI_LOG_REPL, "_csngen_adjust_local_time",
10de5f
-                          "gen state after %08lx%04x:%ld:%ld:%ld\n",
10de5f
-                          new_time, gen->state.seq_num,
10de5f
-                          gen->state.sampled_time,
10de5f
-                          gen->state.local_offset,
10de5f
-                          gen->state.remote_offset);
10de5f
-        }
10de5f
-        return CSN_SUCCESS;
10de5f
-    } else /* time was turned back */
10de5f
-    {
10de5f
-        if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
-            time_t new_time = CSN_CALC_TSTAMP(gen);
10de5f
-            slapi_log_err(SLAPI_LOG_REPL, "_csngen_adjust_local_time",
10de5f
-                          "gen state back before %08lx%04x:%ld:%ld:%ld\n",
10de5f
-                          new_time, gen->state.seq_num,
10de5f
-                          gen->state.sampled_time,
10de5f
-                          gen->state.local_offset,
10de5f
-                          gen->state.remote_offset);
10de5f
-        }
10de5f
+    }
10de5f
+    if (labs(time_diff) > TIME_DIFF_WARNING_DELAY) {
10de5f
+        /* We had a jump larger than a day */
10de5f
+        slapi_log_err(SLAPI_LOG_INFO, "csngen_new_csn",
10de5f
+                "Detected large jump in CSN time.  Delta: %ld (current time: %ld  vs  previous time: %ld)\n",
10de5f
+                time_diff, cur_time, gen->state.sampled_time);
10de5f
+    }
10de5f
+    if (!ignore_time_skew && (gen->state.local_offset - time_diff > CSN_MAX_TIME_ADJUST)) {
10de5f
+        slapi_log_err(SLAPI_LOG_ERR, "_csngen_adjust_local_time",
10de5f
+                      "Adjustment limit exceeded; value - %ld, limit - %d\n",
10de5f
+                      gen->state.local_offset - time_diff, CSN_MAX_TIME_ADJUST);
10de5f
+        return CSN_LIMIT_EXCEEDED;
10de5f
+    }
10de5f
 
10de5f
-        if (!ignore_time_skew && (labs(time_diff) > CSN_MAX_TIME_ADJUST)) {
10de5f
-            slapi_log_err(SLAPI_LOG_ERR, "_csngen_adjust_local_time",
10de5f
-                          "Adjustment limit exceeded; value - %ld, limit - %d\n",
10de5f
-                          labs(time_diff), CSN_MAX_TIME_ADJUST);
10de5f
-            return CSN_LIMIT_EXCEEDED;
10de5f
-        }
10de5f
+    time_t ts_before = CSN_CALC_TSTAMP(gen);
10de5f
+    time_t ts_after = 0;
10de5f
+    if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
+        time_t new_time = CSN_CALC_TSTAMP(gen);
10de5f
+        slapi_log_err(SLAPI_LOG_REPL, "_csngen_adjust_local_time",
10de5f
+                      "gen state before %08lx%04x:%ld:%ld:%ld\n",
10de5f
+                      new_time, gen->state.seq_num,
10de5f
+                      gen->state.sampled_time,
10de5f
+                      gen->state.local_offset,
10de5f
+                      gen->state.remote_offset);
10de5f
+    }
10de5f
 
10de5f
-        gen->state.sampled_time = cur_time;
10de5f
-        gen->state.local_offset = MAX_VAL(gen->state.local_offset, labs(time_diff));
10de5f
-        gen->state.seq_num = 0;
10de5f
+    gen->state.sampled_time = cur_time;
10de5f
+    gen->state.local_offset = MAX_VAL(0, gen->state.local_offset - time_diff);
10de5f
+    /* new local_offset = MAX_VAL(0, old sample_time + old local_offset - cur_time)
10de5f
+     * ==> new local_offset >= 0 and 
10de5f
+     *     new local_offset + cur_time >= old sample_time + old local_offset
10de5f
+     * ==> new local_offset + cur_time + remote_offset >=
10de5f
+     *            sample_time + old local_offset + remote_offset
10de5f
+     * ==> CSN_CALC_TSTAMP(new gen) >= CSN_CALC_TSTAMP(old gen)
10de5f
+     */
10de5f
 
10de5f
-        if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
-            time_t new_time = CSN_CALC_TSTAMP(gen);
10de5f
-            slapi_log_err(SLAPI_LOG_REPL, "_csngen_adjust_local_time",
10de5f
-                          "gen state back after %08lx%04x:%ld:%ld:%ld\n",
10de5f
-                          new_time, gen->state.seq_num,
10de5f
-                          gen->state.sampled_time,
10de5f
-                          gen->state.local_offset,
10de5f
-                          gen->state.remote_offset);
10de5f
-        }
10de5f
+    /* only reset the seq_num if the new timestamp part of the CSN
10de5f
+       is going to be greater than the old one - if they are the
10de5f
+       same after the above adjustment (which can happen if
10de5f
+       csngen_adjust_time has to store the offset in the
10de5f
+       local_offset field) we must not allow the CSN to regress or
10de5f
+       generate duplicate numbers */
10de5f
+    ts_after = CSN_CALC_TSTAMP(gen);
10de5f
+    PR_ASSERT(ts_after >= ts_before);
10de5f
+    if (ts_after > ts_before) {
10de5f
+        gen->state.seq_num = 0; /* only reset if new time > old time */
10de5f
+    }
10de5f
 
10de5f
-        return CSN_SUCCESS;
10de5f
+    if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
10de5f
+        time_t new_time = CSN_CALC_TSTAMP(gen);
10de5f
+        slapi_log_err(SLAPI_LOG_REPL, "_csngen_adjust_local_time",
10de5f
+                      "gen state after %08lx%04x:%ld:%ld:%ld\n",
10de5f
+                      new_time, gen->state.seq_num,
10de5f
+                      gen->state.sampled_time,
10de5f
+                      gen->state.local_offset,
10de5f
+                      gen->state.remote_offset);
10de5f
     }
10de5f
+    return CSN_SUCCESS;
10de5f
 }
10de5f
 
10de5f
 /*
10de5f
@@ -670,8 +656,8 @@ _csngen_adjust_local_time(CSNGen *gen, time_t cur_time)
10de5f
 #define DEFAULT_THREAD_STACKSIZE 0
10de5f
 
10de5f
 #define GEN_TREAD_COUNT 20
10de5f
-int s_thread_count;
10de5f
-int s_must_exit;
10de5f
+static int s_thread_count;
10de5f
+static int s_must_exit;
10de5f
 
10de5f
 static int
10de5f
 _csngen_start_test_threads(CSNGen *gen)
10de5f
@@ -736,8 +722,8 @@ _csngen_stop_test_threads(void)
10de5f
     s_must_exit = 1;
10de5f
 
10de5f
     while (s_thread_count > 0) {
10de5f
-        /* sleep for 30 seconds */
10de5f
-        DS_Sleep(PR_SecondsToInterval(20));
10de5f
+        /* sleep for 5 seconds */
10de5f
+        DS_Sleep(PR_SecondsToInterval(5));
10de5f
     }
10de5f
 }
10de5f
 
10de5f
@@ -752,7 +738,7 @@ _csngen_gen_tester_main(void *data)
10de5f
 
10de5f
     PR_ASSERT(gen);
10de5f
 
10de5f
-    while (!s_must_exit) {
10de5f
+    while (!s_must_exit && !slapi_is_shutting_down()) {
10de5f
         rc = csngen_new_csn(gen, &csn, PR_FALSE);
10de5f
         if (rc != CSN_SUCCESS) {
10de5f
             slapi_log_err(SLAPI_LOG_ERR, "_csngen_gen_tester_main",
10de5f
@@ -764,7 +750,7 @@ _csngen_gen_tester_main(void *data)
10de5f
         csn_free(&csn;;
10de5f
 
10de5f
         /* sleep for 30 seconds */
10de5f
-        DS_Sleep(PR_SecondsToInterval(10));
10de5f
+        DS_Sleep(PR_SecondsToInterval(30));
10de5f
     }
10de5f
 
10de5f
     PR_AtomicDecrement(&s_thread_count);
10de5f
@@ -782,7 +768,7 @@ _csngen_remote_tester_main(void *data)
10de5f
 
10de5f
     PR_ASSERT(gen);
10de5f
 
10de5f
-    while (!s_must_exit) {
10de5f
+    while (!s_must_exit && !slapi_is_shutting_down()) {
10de5f
         rc = csngen_new_csn(gen, &csn, PR_FALSE);
10de5f
         if (rc != CSN_SUCCESS) {
10de5f
             slapi_log_err(SLAPI_LOG_ERR, "_csngen_remote_tester_main",
10de5f
@@ -797,12 +783,12 @@ _csngen_remote_tester_main(void *data)
10de5f
                               "Failed to adjust generator's time; csn error - %d\n", rc);
10de5f
             }
10de5f
 
10de5f
-            csngen_dump_state(gen);
10de5f
+            csngen_dump_state(gen, SLAPI_LOG_INFO);
10de5f
         }
10de5f
         csn_free(&csn;;
10de5f
 
10de5f
         /* sleep for 30 seconds */
10de5f
-        DS_Sleep(PR_SecondsToInterval(60));
10de5f
+        DS_Sleep(PR_SecondsToInterval(30));
10de5f
     }
10de5f
 
10de5f
     PR_AtomicDecrement(&s_thread_count);
10de5f
@@ -816,17 +802,90 @@ _csngen_local_tester_main(void *data)
10de5f
 
10de5f
     PR_ASSERT(gen);
10de5f
 
10de5f
-
10de5f
-    while (!s_must_exit) {
10de5f
+    while (!s_must_exit && !slapi_is_shutting_down()) {
10de5f
         /* sleep for 30 seconds */
10de5f
-        DS_Sleep(PR_SecondsToInterval(60));
10de5f
+        DS_Sleep(PR_SecondsToInterval(30));
10de5f
 
10de5f
         /*
10de5f
          * g_sampled_time -= slapi_rand () % 100;
10de5f
          */
10de5f
-
10de5f
-        csngen_dump_state(gen);
10de5f
+        csngen_dump_state(gen, SLAPI_LOG_INFO);
10de5f
     }
10de5f
 
10de5f
     PR_AtomicDecrement(&s_thread_count);
10de5f
 }
10de5f
+
10de5f
+int _csngen_tester_state;
10de5f
+int _csngen_tester_state_rid;
10de5f
+
10de5f
+static int
10de5f
+_mynoise(int time, int len, double height)
10de5f
+{
10de5f
+   if (((time/len) % 2) == 0) {
10de5f
+        return -height + 2 * height * ( time % len ) / (len-1);
10de5f
+   } else {
10de5f
+        return height - 2 * height * ( time % len ) / (len-1);
10de5f
+   }
10de5f
+}
10de5f
+
10de5f
+
10de5f
+int32_t _csngen_tester_gettime(struct timespec *tp)
10de5f
+{
10de5f
+    int vtime = _csngen_tester_state ;
10de5f
+    tp->tv_sec = 0x1000000 + vtime + 2 * _csngen_tester_state_rid;
10de5f
+    if (_csngen_tester_state_rid == 3) {
10de5f
+        /* tp->tv_sec += _mynoise(vtime, 10, 1.5); */
10de5f
+        tp->tv_sec += _mynoise(vtime, 30, 15);
10de5f
+    }
10de5f
+    return 0;
10de5f
+}
10de5f
+
10de5f
+/* Mimic a fully meshed multi suplier topology */
10de5f
+void csngen_multi_suppliers_test(void)
10de5f
+{
10de5f
+#define NB_TEST_MASTERS	6
10de5f
+#define NB_TEST_STATES	500
10de5f
+    CSNGen *gen[NB_TEST_MASTERS];
10de5f
+    struct timespec now = {0};
10de5f
+    CSN *last_csn = NULL;
10de5f
+    CSN *csn = NULL;
10de5f
+    int i,j,rc;
10de5f
+
10de5f
+    _csngen_tester_gettime(&now;;
10de5f
+
10de5f
+    for (i=0; i< NB_TEST_MASTERS; i++) {
10de5f
+        gen[i] = csngen_new(i+1, NULL);
10de5f
+        gen[i]->gettime = _csngen_tester_gettime;
10de5f
+        gen[i]->state.sampled_time = now.tv_sec;
10de5f
+    }
10de5f
+
10de5f
+    for (_csngen_tester_state=0; _csngen_tester_state < NB_TEST_STATES; _csngen_tester_state++) {
10de5f
+        for (i=0; i< NB_TEST_MASTERS; i++) {
10de5f
+            _csngen_tester_state_rid = i+1;
10de5f
+            rc = csngen_new_csn(gen[i], &csn, PR_FALSE);
10de5f
+            if (rc) {
10de5f
+                continue;
10de5f
+            }
10de5f
+            csngen_dump_state(gen[i], SLAPI_LOG_INFO);
10de5f
+
10de5f
+            if (csn_compare(csn, last_csn) <= 0) {
10de5f
+                slapi_log_err(SLAPI_LOG_ERR, "csngen_multi_suppliers_test",
10de5f
+                              "CSN generated in disorder state=%d rid=%d\n", _csngen_tester_state, _csngen_tester_state_rid);
10de5f
+                _csngen_tester_state = NB_TEST_STATES;
10de5f
+                break;
10de5f
+            }
10de5f
+            last_csn = csn;
10de5f
+
10de5f
+            for (j=0; j< NB_TEST_MASTERS; j++) {
10de5f
+                if (i==j) {
10de5f
+                    continue;
10de5f
+                }
10de5f
+                _csngen_tester_state_rid = j+1;
10de5f
+                rc = csngen_adjust_time(gen[j], csn);
10de5f
+                if (rc) {
10de5f
+                    continue;
10de5f
+                }
10de5f
+            }
10de5f
+        }
10de5f
+    }
10de5f
+}
10de5f
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
10de5f
index 9f7971ec1..3ba3df910 100644
10de5f
--- a/ldap/servers/slapd/slapi-plugin.h
10de5f
+++ b/ldap/servers/slapd/slapi-plugin.h
10de5f
@@ -6778,8 +6778,17 @@ time_t slapi_current_time(void) __attribute__((deprecated));
10de5f
  *
10de5f
  * \param tp - a timespec struct where the system time is set
10de5f
  * \return result code, upon success tp is set to the system time
10de5f
+ * as a clock in UTC timezone. This clock adjusts with ntp steps,
10de5f
+ * and should NOT be used for timer information.
10de5f
  */
10de5f
 int32_t slapi_clock_gettime(struct timespec *tp);
10de5f
+/* 
10de5f
+ * slapi_clock_gettime should have better been called
10de5f
+ * slapi_clock_utc_gettime but sice the function pre-existed
10de5f
+ * we are just adding an alias (to avoid risking to break
10de5f
+ * some custom plugins)
10de5f
+ */
10de5f
+#define slapi_clock_utc_gettime slapi_clock_gettime
10de5f
 
10de5f
 /**
10de5f
  * Returns the current system time as a hr clock relative to uptime
10de5f
-- 
10de5f
2.31.1
10de5f