3
#include "replication.h"
4
#include "Judy.h"
5
6
+#define MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED 30
7
+#define MIN_SENDER_BUFFER_PERCENTAGE_ALLOWED 10
8
+
9
static time_t replicate_chart_timeframe(BUFFER *wb, RRDSET *st, time_t after, time_t before, bool enable_streaming) {
10
size_t dimensions = rrdset_number_of_dimensions(st);
11
397
return send_replay_chart_cmd(callback, callback_data, st, start_streaming, first_entry_wanted, last_entry_wanted);
398
}
399
397
-// ----------------------------------------------------------------------------
398
-
399
-static size_t sender_buffer_used_percent(struct sender_state *s) {
400
- netdata_mutex_lock(&s->mutex);
401
- size_t available = cbuffer_available_size_unsafe(s->host->sender->buffer);
402
- netdata_mutex_unlock(&s->mutex);
403
-
404
- return (s->host->sender->buffer->max_size - available) * 100 / s->host->sender->buffer->max_size;
405
-}
406
-
407
-
400
// ----------------------------------------------------------------------------
401
// replication thread
402
408
STRING *chart_id;
409
time_t after; // key for sorting (JudyL)
410
time_t before;
411
+ Word_t unique_id;
412
bool start_streaming;
413
bool found;
414
+ bool index_in_judy;
415
};
416
417
// replication sort entry in JudyL array
418
// used for sorting all requests, across all nodes
419
struct replication_sort_entry {
426
- struct replication_request req;
420
+ struct replication_request *rq;
421
428
- const void *unique_id; // used as a key to identify the sort entry - we never access its contents
429
- bool executed;
430
- struct replication_sort_entry *next;
422
+ size_t unique_id; // used as a key to identify the sort entry - we never access its contents
423
};
424
425
// the global variables for the replication thread
427
netdata_mutex_t mutex;
428
429
size_t added;
430
+ size_t executed;
431
size_t removed;
432
time_t first_time_t;
433
size_t requests_count;
434
+ Word_t next_unique_id;
435
struct replication_request *requests;
436
437
+ Word_t last_after;
438
+ Word_t last_unique_id;
439
+
440
+ size_t skipped_not_connected;
441
+ size_t skipped_no_room;
442
+ size_t sender_resets;
443
+ size_t waits;
444
+
445
Pvoid_t JudyL_array;
446
} rep = {
447
.mutex = NETDATA_MUTEX_INITIALIZER,
448
.added = 0,
447
- .removed = 0,
449
+ .executed = 0,
450
.first_time_t = 0,
451
.requests_count = 0,
452
+ .next_unique_id = 1,
453
+ .skipped_no_room = 0,
454
+ .skipped_not_connected = 0,
455
+ .sender_resets = 0,
456
+ .waits = 0,
457
.requests = NULL,
458
.JudyL_array = NULL,
459
};
483
// ----------------------------------------------------------------------------
484
// replication sort entry management
485
479
-static struct replication_sort_entry *replication_sort_entry_create(struct replication_request *r, const void *unique_id) {
480
- struct replication_sort_entry *t = mallocz(sizeof(struct replication_sort_entry));
486
+static struct replication_sort_entry *replication_sort_entry_create(struct replication_request *rq) {
487
+ struct replication_sort_entry *rse = mallocz(sizeof(struct replication_sort_entry));
488
489
// copy the request
483
- t->req = *r;
484
- t->req.chart_id = string_dup(r->chart_id);
485
-
490
+ rse->rq = rq;
491
+ rse->unique_id = rep.next_unique_id++;
492
487
- t->unique_id = unique_id;
488
- t->executed = false;
489
- t->next = NULL;
490
- return t;
493
+ // save the unique id into the request, to be able to delete it later
494
+ rq->unique_id = rse->unique_id;
495
+ rq->index_in_judy = false;
496
+ return rse;
497
}
498
493
-static void replication_sort_entry_destroy(struct replication_sort_entry *t) {
494
- string_freez(t->req.chart_id);
495
- freez(t);
499
+static void replication_sort_entry_destroy(struct replication_sort_entry *rse) {
500
+ freez(rse);
501
}
502
498
-static struct replication_sort_entry *replication_sort_entry_add(struct replication_request *r, const void *unique_id) {
499
- struct replication_sort_entry *t = replication_sort_entry_create(r, unique_id);
500
-
503
+static struct replication_sort_entry *replication_sort_entry_add(struct replication_request *rq) {
504
replication_recursive_lock();
505
506
+ struct replication_sort_entry *rse = replication_sort_entry_create(rq);
507
+
508
+ if(rq->after < (time_t)rep.last_after) {
509
+ // make it find this request first
510
+ rep.last_after = rq->after;
511
+ rep.last_unique_id = rq->unique_id;
512
+ }
513
+
514
rep.added++;
515
+ rep.requests_count++;
516
505
- Pvoid_t *PValue;
517
+ Pvoid_t *inner_judy_ptr;
518
507
- PValue = JudyLGet(rep.JudyL_array, (Word_t) r->after, PJE0);
508
- if(!PValue)
509
- PValue = JudyLIns(&rep.JudyL_array, (Word_t) r->after, PJE0);
519
+ // find the outer judy entry, using after as key
520
+ inner_judy_ptr = JudyLGet(rep.JudyL_array, (Word_t) rq->after, PJE0);
521
+ if(!inner_judy_ptr)
522
+ inner_judy_ptr = JudyLIns(&rep.JudyL_array, (Word_t) rq->after, PJE0);
523
511
- t->next = *PValue;
512
- *PValue = t;
524
+ // add it to the inner judy, using unique_id as key
525
+ Pvoid_t *item = JudyLIns(inner_judy_ptr, rq->unique_id, PJE0);
526
+ *item = rse;
527
+ rq->index_in_judy = true;
528
514
- if(!rep.first_time_t || r->after < rep.first_time_t)
515
- rep.first_time_t = r->after;
529
+ if(!rep.first_time_t || rq->after < rep.first_time_t)
530
+ rep.first_time_t = rq->after;
531
532
replication_recursive_unlock();
533
519
- return t;
534
+ return rse;
535
}
536
522
-static void replication_sort_entry_del(struct sender_state *sender, STRING *chart_id, time_t after, const DICTIONARY_ITEM *item) {
523
- Pvoid_t *PValue;
524
- struct replication_sort_entry *to_delete = NULL;
525
-
526
- replication_recursive_lock();
537
+static bool replication_sort_entry_unlink_and_free_unsafe(struct replication_sort_entry *rse, Pvoid_t **inner_judy_ppptr) {
538
+ bool inner_judy_deleted = false;
539
540
rep.removed++;
541
+ rep.requests_count--;
542
530
- PValue = JudyLGet(rep.JudyL_array, after, PJE0);
531
- if(PValue) {
532
- struct replication_sort_entry *t = *PValue;
533
- t->executed = true; // make sure we don't get it again
543
+ rse->rq->index_in_judy = false;
544
535
- if(!t->next) {
536
- // we are alone here, delete the judy entry
545
+ // delete it from the inner judy
546
+ JudyLDel(*inner_judy_ppptr, rse->rq->unique_id, PJE0);
547
538
- if(t->unique_id != item)
539
- fatal("Item to delete is not matching host '%s', chart '%s', time %ld.",
540
- rrdhost_hostname(sender->host), string2str(chart_id), after);
548
+ // if no items left, delete it from the outer judy
549
+ if(**inner_judy_ppptr == NULL) {
550
+ JudyLDel(&rep.JudyL_array, rse->rq->after, PJE0);
551
+ inner_judy_deleted = true;
552
+ }
553
542
- to_delete = t;
543
- JudyLDel(&rep.JudyL_array, after, PJE0);
544
- }
545
- else {
546
- // find our entry in the linked list
554
+ // free memory
555
+ replication_sort_entry_destroy(rse);
556
548
- struct replication_sort_entry *t_old = NULL;
549
- do {
550
- if(t->unique_id == item) {
551
- to_delete = t;
557
+ return inner_judy_deleted;
558
+}
559
553
- if(t_old)
554
- t_old->next = t->next;
555
- else
556
- *PValue = t->next;
560
+static void replication_sort_entry_del(struct replication_request *rq) {
561
+ Pvoid_t *inner_judy_pptr;
562
+ struct replication_sort_entry *rse_to_delete = NULL;
563
558
- break;
559
- }
564
+ replication_recursive_lock();
565
+ if(rq->index_in_judy) {
566
+
567
+ inner_judy_pptr = JudyLGet(rep.JudyL_array, rq->after, PJE0);
568
+ if (inner_judy_pptr) {
569
+ Pvoid_t *our_item_pptr = JudyLGet(*inner_judy_pptr, rq->unique_id, PJE0);
570
+ if (our_item_pptr) {
571
+ rse_to_delete = *our_item_pptr;
572
+ replication_sort_entry_unlink_and_free_unsafe(rse_to_delete, &inner_judy_pptr);
573
+ }
574
+ }
575
561
- t_old = t;
562
- t = t->next;
576
+ if (!rse_to_delete)
577
+ fatal("Cannot find sort entry to delete for host '%s', chart '%s', time %ld.",
578
+ rrdhost_hostname(rq->sender->host), string2str(rq->chart_id), rq->after);
579
564
- } while(t);
565
- }
580
}
581
568
- if(!to_delete)
569
- fatal("Cannot find sort entry to delete for host '%s', chart '%s', time %ld.",
570
- rrdhost_hostname(sender->host), string2str(chart_id), after);
571
-
582
replication_recursive_unlock();
573
-
574
- replication_sort_entry_destroy(to_delete);
583
}
584
585
static struct replication_request replication_request_get_first_available() {
578
- struct replication_sort_entry *found = NULL;
579
- Pvoid_t *PValue;
580
- Word_t Index;
586
+ Pvoid_t *inner_judy_pptr;
587
588
replication_recursive_lock();
589
584
- rep.requests_count = JudyLCount(rep.JudyL_array, 0, 0xFFFFFFFF, PJE0);
585
- if(!rep.requests_count) {
586
- replication_recursive_unlock();
587
- return (struct replication_request){ .found = false };
590
+ struct replication_request rq = (struct replication_request){ .found = false };
591
+
592
+
593
+ if(rep.last_after && rep.last_unique_id) {
594
+ rep.last_after--;
595
+ rep.last_unique_id--;
596
+ }
597
+ else {
598
+ rep.last_after = 0;
599
+ rep.last_unique_id = 0;
600
}
601
590
- Index = 0;
591
- PValue = JudyLFirst(rep.JudyL_array, &Index, PJE0);
592
- while(!found && PValue) {
593
- struct replication_sort_entry *t;
594
-
595
- for(t = *PValue; t ;t = t->next) {
596
- if(!t->executed
597
- && sender_buffer_used_percent(t->req.sender) <= 10
598
- && t->req.sender_last_flush_ut == __atomic_load_n(&t->req.sender->last_flush_time_ut, __ATOMIC_SEQ_CST)
599
- ) {
600
- found = t;
601
- found->executed = true;
602
- break;
602
+ while(!rq.found && (inner_judy_pptr = JudyLNext(rep.JudyL_array, &rep.last_after, PJE0))) {
603
+ Pvoid_t *our_item_pptr;
604
+
605
+ while(!rq.found && (our_item_pptr = JudyLNext(*inner_judy_pptr, &rep.last_unique_id, PJE0))) {
606
+ struct replication_sort_entry *rse = *our_item_pptr;
607
+ struct sender_state *s = rse->rq->sender;
608
+
609
+ bool sender_is_connected =
610
+ rrdhost_flag_check(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED);
611
+
612
+ bool sender_has_been_flushed_since_this_request =
613
+ rse->rq->sender_last_flush_ut != __atomic_load_n(&s->last_flush_time_ut, __ATOMIC_SEQ_CST);
614
+
615
+ bool sender_has_room_to_spare =
616
+ s->replication_sender_buffer_percent_used <= MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED;
617
+
618
+ if(unlikely(!sender_is_connected || sender_has_been_flushed_since_this_request)) {
619
+ rep.skipped_not_connected++;
620
+ if(replication_sort_entry_unlink_and_free_unsafe(rse, &inner_judy_pptr))
621
+ break;
622
}
604
- }
623
606
- if(!found)
607
- PValue = JudyLNext(rep.JudyL_array, &Index, PJE0);
608
- }
624
+ else if(sender_has_room_to_spare) {
625
+ // copy the request to return it
626
+ rq = *rse->rq;
627
+
628
+ // set the return result to found
629
+ rq.found = true;
630
610
- // copy the values we need, while we have the lock
611
- struct replication_request ret;
631
+ if(replication_sort_entry_unlink_and_free_unsafe(rse, &inner_judy_pptr))
632
+ break;
633
+ }
634
+ else
635
+ rep.skipped_no_room++;
636
+ }
637
613
- if(found) {
614
- ret = found->req;
615
- ret.chart_id = string_dup(ret.chart_id);
616
- ret.found = true;
638
+ // prepare for the next iteration on the outer loop
639
+ rep.last_unique_id = 0;
640
}
618
- else
619
- ret.found = false;
641
642
replication_recursive_unlock();
622
-
623
- return ret;
643
+ return rq;
644
}
645
646
// ----------------------------------------------------------------------------
647
// replication request management
648
629
-static void replication_request_react_callback(const DICTIONARY_ITEM *item, void *value __maybe_unused, void *sender_state __maybe_unused) {
649
+static void replication_request_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *sender_state __maybe_unused) {
650
struct sender_state *s = sender_state; (void)s;
631
- struct replication_request *r = value;
651
+ struct replication_request *rq = value;
652
653
// IMPORTANT:
654
// We use the react instead of the insert callback
660
// may see the replication sort entry, but fail to find the dictionary item
661
// related to it.
662
643
- replication_sort_entry_add(r, item);
644
- __atomic_fetch_add(&r->sender->replication_pending_requests, 1, __ATOMIC_SEQ_CST);
663
+ replication_sort_entry_add(rq);
664
+ __atomic_fetch_add(&rq->sender->replication_pending_requests, 1, __ATOMIC_SEQ_CST);
665
}
666
667
static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *sender_state) {
668
struct sender_state *s = sender_state; (void)s;
649
- struct replication_request *r = old_value; (void)r;
650
- struct replication_request *r_new = new_value;
669
+ struct replication_request *rq = old_value; (void)rq;
670
+ struct replication_request *rq_new = new_value;
671
672
internal_error(
673
true,
674
"STREAM %s [send to %s]: ignoring duplicate replication command received for chart '%s' (existing from %llu to %llu [%s], new from %llu to %llu [%s])",
675
rrdhost_hostname(s->host), s->connected_to, dictionary_acquired_item_name(item),
656
- (unsigned long long)r->after, (unsigned long long)r->before, r->start_streaming ? "true" : "false",
657
- (unsigned long long)r_new->after, (unsigned long long)r_new->before, r_new->start_streaming ? "true" : "false");
676
+ (unsigned long long)rq->after, (unsigned long long)rq->before, rq->start_streaming ? "true" : "false",
677
+ (unsigned long long)rq_new->after, (unsigned long long)rq_new->before, rq_new->start_streaming ? "true" : "false");
678
659
- string_freez(r_new->chart_id);
679
+ string_freez(rq_new->chart_id);
680
681
return false;
682
}
683
664
-static void replication_request_delete_callback(const DICTIONARY_ITEM *item, void *value, void *sender_state __maybe_unused) {
665
- struct replication_request *r = value;
684
+static void replication_request_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *sender_state __maybe_unused) {
685
+ struct replication_request *rq = value;
686
667
- replication_sort_entry_del(r->sender, r->chart_id, r->after, item);
687
+ if(rq->index_in_judy)
688
+ replication_sort_entry_del(rq);
689
669
- string_freez(r->chart_id);
670
- __atomic_fetch_sub(&r->sender->replication_pending_requests, 1, __ATOMIC_SEQ_CST);
690
+ string_freez(rq->chart_id);
691
+ __atomic_fetch_sub(&rq->sender->replication_pending_requests, 1, __ATOMIC_SEQ_CST);
692
}
693
694
696
// public API
697
698
void replication_add_request(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming) {
678
- struct replication_request tmp = {
699
+ struct replication_request rq = {
700
.sender = sender,
701
.chart_id = string_strdupz(chart_id),
702
.after = after,
705
.sender_last_flush_ut = __atomic_load_n(&sender->last_flush_time_ut, __ATOMIC_SEQ_CST),
706
};
707
687
- dictionary_set(sender->replication_requests, chart_id, &tmp, sizeof(struct replication_request));
708
+ dictionary_set(sender->replication_requests, chart_id, &rq, sizeof(struct replication_request));
709
}
710
711
void replication_flush_sender(struct sender_state *sender) {
729
replication_recursive_unlock();
730
}
731
732
+void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) {
733
+ size_t available = cbuffer_available_size_unsafe(s->host->sender->buffer);
734
+ size_t percentage = (s->buffer->max_size - available) * 100 / s->buffer->max_size;
735
+
736
+ if(percentage > MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED)
737
+ s->replication_reached_max = true;
738
+
739
+ if(s->replication_reached_max &&
740
+ percentage <= MIN_SENDER_BUFFER_PERCENTAGE_ALLOWED) {
741
+ s->replication_reached_max = false;
742
+ replication_recursive_lock();
743
+ rep.last_after = 0;
744
+ rep.last_unique_id = 0;
745
+ rep.sender_resets++;
746
+ replication_recursive_unlock();
747
+ }
748
+
749
+ s->replication_sender_buffer_percent_used = percentage;
750
+}
751
+
752
// ----------------------------------------------------------------------------
753
// replication thread
754
762
static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
763
}
764
724
-#define WORKER_JOB_ITERATION 1
725
-#define WORKER_JOB_REPLAYING 2
726
-#define WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS 3
727
-#define WORKER_JOB_CUSTOM_METRIC_COMPLETION 4
728
-#define WORKER_JOB_CUSTOM_METRIC_ADDED 5
729
-#define WORKER_JOB_CUSTOM_METRIC_DONE 6
765
+#define WORKER_JOB_FIND_NEXT 1
766
+#define WORKER_JOB_QUERYING 2
767
+#define WORKER_JOB_DELETE_ENTRY 3
768
+#define WORKER_JOB_FIND_CHART 4
769
+#define WORKER_JOB_STATISTICS 5
770
+#define WORKER_JOB_ACTIVATE_ENABLE_STREAMING 6
771
+#define WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS 7
772
+#define WORKER_JOB_CUSTOM_METRIC_COMPLETION 8
773
+#define WORKER_JOB_CUSTOM_METRIC_ADDED 9
774
+#define WORKER_JOB_CUSTOM_METRIC_DONE 10
775
+#define WORKER_JOB_CUSTOM_METRIC_SKIPPED_NOT_CONNECTED 11
776
+#define WORKER_JOB_CUSTOM_METRIC_SKIPPED_NO_ROOM 12
777
+#define WORKER_JOB_CUSTOM_METRIC_SENDER_RESETS 13
778
+#define WORKER_JOB_CUSTOM_METRIC_WAITS 14
779
780
void *replication_thread_main(void *ptr __maybe_unused) {
781
netdata_thread_cleanup_push(replication_main_cleanup, ptr);
782
783
worker_register("REPLICATION");
784
736
- worker_register_job_name(WORKER_JOB_ITERATION, "iteration");
737
- worker_register_job_name(WORKER_JOB_REPLAYING, "replaying");
785
+ worker_register_job_name(WORKER_JOB_FIND_NEXT, "find next");
786
+ worker_register_job_name(WORKER_JOB_QUERYING, "querying");
787
+ worker_register_job_name(WORKER_JOB_DELETE_ENTRY, "dict delete");
788
+ worker_register_job_name(WORKER_JOB_FIND_CHART, "find chart");
789
+ worker_register_job_name(WORKER_JOB_ACTIVATE_ENABLE_STREAMING, "enable streaming");
790
791
worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS, "pending requests", "requests", WORKER_METRIC_ABSOLUTE);
792
worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, "completion", "%", WORKER_METRIC_ABSOLUTE);
793
worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_ADDED, "added requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL);
794
worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_DONE, "finished requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL);
795
+ worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NOT_CONNECTED, "not connected requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL);
796
+ worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NO_ROOM, "no room requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL);
797
+ worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_SENDER_RESETS, "sender resets", "resets/s", WORKER_METRIC_INCREMENTAL_TOTAL);
798
+ worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_WAITS, "waits", "waits/s", WORKER_METRIC_INCREMENTAL_TOTAL);
799
744
- while(!netdata_exit) {
745
- worker_is_busy(WORKER_JOB_ITERATION);
746
-
747
- // this call also updates our statistics
748
- struct replication_request r = replication_request_get_first_available();
800
+ time_t latest_first_time_t = 0;
801
750
- if(r.found) {
751
- // delete the request from the dictionary
752
- dictionary_del(r.sender->replication_requests, string2str(r.chart_id));
753
- }
802
+ while(!netdata_exit) {
803
+ worker_is_busy(WORKER_JOB_FIND_NEXT);
804
+ struct replication_request rq = replication_request_get_first_available();
805
806
+ worker_is_busy(WORKER_JOB_STATISTICS);
807
worker_set_metric(WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS, (NETDATA_DOUBLE)rep.requests_count);
808
worker_set_metric(WORKER_JOB_CUSTOM_METRIC_ADDED, (NETDATA_DOUBLE)rep.added);
757
- worker_set_metric(WORKER_JOB_CUSTOM_METRIC_DONE, (NETDATA_DOUBLE)rep.removed);
809
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_DONE, (NETDATA_DOUBLE)rep.executed);
810
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NOT_CONNECTED, (NETDATA_DOUBLE)rep.skipped_not_connected);
811
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NO_ROOM, (NETDATA_DOUBLE)rep.skipped_no_room);
812
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_SENDER_RESETS, (NETDATA_DOUBLE)rep.sender_resets);
813
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_WAITS, (NETDATA_DOUBLE)rep.waits);
814
759
- if(!r.found && !rep.requests_count) {
760
- worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, 100.0);
761
- worker_is_idle();
762
- sleep_usec(1000 * USEC_PER_MS);
763
- continue;
815
+ if(latest_first_time_t) {
816
+ time_t now = now_realtime_sec();
817
+ time_t total = now - rep.first_time_t;
818
+ time_t done = latest_first_time_t - rep.first_time_t;
819
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, (NETDATA_DOUBLE)done * 100.0 / (NETDATA_DOUBLE)total);
820
}
821
766
- if(!r.found) {
822
+ if(!rq.found) {
823
worker_is_idle();
768
- sleep_usec(1 * USEC_PER_MS);
824
+
825
+ if(!rep.requests_count)
826
+ worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, 100.0);
827
+
828
+ // make it start from the beginning
829
+ rep.last_after = 0;
830
+ rep.last_unique_id = 0;
831
+
832
+ rep.waits++;
833
+
834
+ sleep_usec(1000 * USEC_PER_MS);
835
continue;
836
}
837
+ else {
838
+ // delete the request from the dictionary
839
+ worker_is_busy(WORKER_JOB_DELETE_ENTRY);
840
+ dictionary_del(rq.sender->replication_requests, string2str(rq.chart_id));
841
+ }
842
772
- RRDSET *st = rrdset_find(r.sender->host, string2str(r.chart_id));
843
+ worker_is_busy(WORKER_JOB_FIND_CHART);
844
+ RRDSET *st = rrdset_find(rq.sender->host, string2str(rq.chart_id));
845
if(!st) {
846
internal_error(true, "REPLAY: chart '%s' not found on host '%s'",
775
- string2str(r.chart_id), rrdhost_hostname(r.sender->host));
847
+ string2str(rq.chart_id), rrdhost_hostname(rq.sender->host));
848
849
continue;
850
}
851
780
- worker_is_busy(WORKER_JOB_REPLAYING);
852
+ worker_is_busy(WORKER_JOB_QUERYING);
853
782
- time_t latest_first_time_t = r.after;
854
+ latest_first_time_t = rq.after;
855
784
- if(r.after < r.sender->replication_first_time || !r.sender->replication_first_time)
785
- r.sender->replication_first_time = r.after;
856
+ if(rq.after < rq.sender->replication_first_time || !rq.sender->replication_first_time)
857
+ rq.sender->replication_first_time = rq.after;
858
787
- if(r.before < r.sender->replication_min_time || !r.sender->replication_min_time)
788
- r.sender->replication_min_time = r.before;
859
+ if(rq.before < rq.sender->replication_min_time || !rq.sender->replication_min_time)
860
+ rq.sender->replication_min_time = rq.before;
861
862
netdata_thread_disable_cancelability();
863
864
// send the replication data
865
bool start_streaming = replicate_chart_response(st->rrdhost, st,
794
- r.start_streaming, r.after, r.before);
866
+ rq.start_streaming, rq.after, rq.before);
867
868
netdata_thread_enable_cancelability();
869
798
- if(start_streaming && r.sender_last_flush_ut == __atomic_load_n(&r.sender->last_flush_time_ut, __ATOMIC_SEQ_CST)) {
799
- __atomic_fetch_add(&r.sender->receiving_metrics, 1, __ATOMIC_SEQ_CST);
870
+ rep.executed++;
871
+
872
+ if(start_streaming && rq.sender_last_flush_ut == __atomic_load_n(&rq.sender->last_flush_time_ut, __ATOMIC_SEQ_CST)) {
873
+ worker_is_busy(WORKER_JOB_ACTIVATE_ENABLE_STREAMING);
874
+ __atomic_fetch_add(&rq.sender->receiving_metrics, 1, __ATOMIC_SEQ_CST);
875
876
// enable normal streaming if we have to
877
// but only if the sender buffer has not been flushed since we started
878
879
debug(D_REPLICATION, "Enabling metric streaming for chart %s.%s",
805
- rrdhost_hostname(r.sender->host), rrdset_id(st));
880
+ rrdhost_hostname(rq.sender->host), rrdset_id(st));
881
882
rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
883
}
809
-
810
- // statistics
811
- {
812
- time_t now = now_realtime_sec();
813
- time_t total = now - rep.first_time_t;
814
- time_t done = latest_first_time_t - rep.first_time_t;
815
- worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, (NETDATA_DOUBLE)done * 100.0 / (NETDATA_DOUBLE)total);
816
- }
884
}
885
886
netdata_thread_cleanup_pop(1);