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