diff --git a/CHANGELOG.md b/CHANGELOG.md index c11282f..51e8b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - on connect retry, requery jwt only if it failed before - put two timestamps in connection health file; start conn and current - change health file update interval to 240sec +- use jitter in backoff delay - sendMessage to check cloud status == ONLINE before sending ## [1.0.2] - 2019-02-08 diff --git a/src/connection.c b/src/connection.c index 07ae9f0..b2e1bdd 100644 --- a/src/connection.c +++ b/src/connection.c @@ -236,23 +236,68 @@ void init_backoff_timer (backoff_timer_t *timer, int max_count) timer->start_time = time(NULL); } -void terminate_backoff_delay (void) -{ - pthread_mutex_lock (&backoff_delay_mut); - pthread_cond_signal(&backoff_delay_con); - pthread_mutex_unlock (&backoff_delay_mut); -} - -int update_backoff_delay (backoff_timer_t *timer) +unsigned update_backoff_delay (backoff_timer_t *timer) { if (timer->count < timer->max_count) { timer->count += 1; timer->delay = timer->delay + timer->delay + 1; // 3,7,15,31 .. } - return timer->delay; + return (unsigned) timer->delay; } +void add_timespec (struct timespec *t1, struct timespec *t2) +{ + t2->tv_sec += t1->tv_sec; + t2->tv_nsec += t1->tv_nsec; + if (t2->tv_nsec >= 1000000000) { + t2->tv_sec += 1; + t2->tv_nsec -= 1000000000; + } +} + +unsigned calc_random_secs (int random_num, unsigned max_secs) +{ + unsigned delay_secs = (unsigned) random_num & max_secs; + if (delay_secs < 3) + return delay_secs + 3; + else + return delay_secs; +} + +unsigned calc_random_nsecs (int random_num) +{ + /* random _num is in range 0..2147483648 */ + unsigned n = (unsigned) random_num >> 1; + /* n is in range 0..1073741824 */ + if (n < 1000000000) + return n; + return n - 1000000000; +} + +void calc_random_expiration (int random_num1, int random_num2, backoff_timer_t *timer, struct timespec *ts) +{ + unsigned max_secs = update_backoff_delay (timer); // 3,7,15,31 + struct timespec ts_delay = {3, 0}; + + if (max_secs > 3) { + ts_delay.tv_sec = calc_random_secs (random_num1, max_secs); + ts_delay.tv_nsec = calc_random_nsecs (random_num2); + } + ParodusInfo("Waiting max delay %u backoffRetryTime %ld secs %ld usecs\n", + max_secs, ts_delay.tv_sec, ts_delay.tv_nsec/1000); + + /* Add delay to expire time */ + add_timespec (&ts_delay, ts); +} + +void terminate_backoff_delay (void) +{ + pthread_mutex_lock (&backoff_delay_mut); + pthread_cond_signal(&backoff_delay_con); + pthread_mutex_unlock (&backoff_delay_mut); +} + #define BACKOFF_ERR -1 #define BACKOFF_SHUTDOWN 1 #define BACKOFF_DELAY_TAKEN 0 @@ -280,9 +325,7 @@ static int backoff_delay (backoff_timer_t *timer) timer->ts.tv_sec += UPDATE_HEALTH_FILE_INTERVAL_SECS; } - update_backoff_delay (timer); - ParodusInfo("Waiting with backoffRetryTime %d seconds\n", timer->delay); - ts.tv_sec += timer->delay; + calc_random_expiration (random(), random(), timer, &ts); pthread_mutex_lock (&backoff_delay_mut); // The condition variable will only be set if we shut down. diff --git a/src/main.c b/src/main.c index c7b9d0b..3db4feb 100644 --- a/src/main.c +++ b/src/main.c @@ -96,6 +96,9 @@ int main( int argc, char **argv) #endif ParodusCfg *cfg; + ParodusInfo ("RAND_MAX is %ld (0x%lx)\n", RAND_MAX, RAND_MAX); + srandom (getpid()); + /* TODO not ideal, but it fixes a more major problem for now. */ cfg = get_parodus_cfg(); memset(cfg,0,sizeof(ParodusCfg)); diff --git a/tests/test_connection.c b/tests/test_connection.c index bca9dbb..c52bcd1 100644 --- a/tests/test_connection.c +++ b/tests/test_connection.c @@ -37,6 +37,11 @@ extern void init_expire_timer (expire_timer_t *timer); extern int check_timer_expired (expire_timer_t *timer, long timeout_ms); extern void init_backoff_timer (backoff_timer_t *timer, int max_count); extern int update_backoff_delay (backoff_timer_t *timer); +extern void add_timespec (struct timespec *t1, struct timespec *t2); +extern unsigned calc_random_secs (int random_num, unsigned max_secs); +extern unsigned calc_random_nsecs (int random_num); +void calc_random_expiration (int random_num1, int random_num2, + backoff_timer_t *timer, struct timespec *ts); extern int init_header_info (header_info_t *header_info); extern void free_header_info (header_info_t *header_info); extern char *build_extra_hdrs (header_info_t *header_info); @@ -333,13 +338,67 @@ void test_expire_timer() void test_backoff_delay_timer() { + struct timespec t1; + struct timespec t2; backoff_timer_t btimer; + init_backoff_timer (&btimer, 5); assert_int_equal (3, update_backoff_delay (&btimer)); assert_int_equal (7, update_backoff_delay (&btimer)); assert_int_equal (15, update_backoff_delay (&btimer)); assert_int_equal (31, update_backoff_delay (&btimer)); - assert_int_equal (31, update_backoff_delay (&btimer)); + + t1.tv_sec = 3; t1.tv_nsec = 0; + t2.tv_sec = 3; t2.tv_nsec = 0; + add_timespec (&t1, &t2); + assert_int_equal (6, t2.tv_sec); + assert_int_equal (0, t2.tv_nsec); + + t1.tv_sec = 3; t1.tv_nsec = 500*1000000; + t2.tv_sec = 3; t2.tv_nsec = 499*1000000; + add_timespec (&t1, &t2); + assert_int_equal (6, t2.tv_sec); + assert_int_equal (999*1000000, t2.tv_nsec); + + t1.tv_sec = 3; t1.tv_nsec = 500*1000000; + t2.tv_sec = 3; t2.tv_nsec = 501*1000000; + add_timespec (&t1, &t2); + assert_int_equal (7, t2.tv_sec); + assert_int_equal (1000000, t2.tv_nsec); + + assert_int_equal (3, calc_random_secs (0, 7)); + assert_int_equal (4, calc_random_secs (1, 7)); + assert_int_equal (7, calc_random_secs (15, 7)); + assert_int_equal (3, calc_random_secs (16, 15)); + assert_int_equal (14, calc_random_secs (30,15)); + + assert_int_equal (250000, calc_random_nsecs (500000)); + assert_int_equal (1, calc_random_nsecs (2000000002)); + + init_backoff_timer (&btimer, 5); + t1.tv_sec = 0; t1.tv_nsec = 0; + /* max delay is 3 */ + calc_random_expiration (0, 0, &btimer, &t1); + assert_int_equal (3, t1.tv_sec); + assert_int_equal (0, t1.tv_nsec); + + t1.tv_sec = 0; t1.tv_nsec = 0; + /* max delay is 7*/ + calc_random_expiration (15, 1073741824, &btimer, &t1); + assert_int_equal (7, t1.tv_sec); + assert_int_equal (536870912, t1.tv_nsec); + + t1.tv_sec = 0; t1.tv_nsec = 0; + /* max delay is 15 */ + calc_random_expiration (30, 2000000002, &btimer, &t1); + assert_int_equal (14, t1.tv_sec); + assert_int_equal (1, t1.tv_nsec); + + t1.tv_sec = 0; t1.tv_nsec = 0; + /* max delay is 31 */ + calc_random_expiration (32, 1, &btimer, &t1); + assert_int_equal (3, t1.tv_sec); + assert_int_equal (0, t1.tv_nsec); }