6
7
#define CONFIG_SECTION_GLOBAL_STATISTICS "global statistics"
8
9
+#define WORKER_JOB_GLOBAL 0
10
+#define WORKER_JOB_REGISTRY 1
11
+#define WORKER_JOB_WORKERS 2
12
+#define WORKER_JOB_DBENGINE 3
13
+#define WORKER_JOB_HEARTBEAT 4
14
+
15
+#if WORKER_UTILIZATION_MAX_JOB_TYPES < 5
16
+#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 5
17
+#endif
18
+
19
static struct global_statistics {
20
volatile uint16_t connected_clients;
21
446
}
447
448
// ----------------------------------------------------------------
449
+}
450
451
+static void dbengine_statistics_charts(void) {
452
#ifdef ENABLE_DBENGINE
441
- RRDHOST *host;
442
- unsigned long long stats_array[RRDENG_NR_STATS] = {0};
443
- unsigned long long local_stats_array[RRDENG_NR_STATS];
444
- unsigned dbengine_contexts = 0, counted_multihost_db = 0, i;
445
-
446
- rrd_rdlock();
447
- rrdhost_foreach_read(host) {
448
- if (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && !rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) {
449
- if (&multidb_ctx == host->rrdeng_ctx) {
450
- if (counted_multihost_db)
451
- continue; /* Only count multi-host DB once */
452
- counted_multihost_db = 1;
453
- }
454
- ++dbengine_contexts;
455
- /* get localhost's DB engine's statistics */
456
- rrdeng_get_37_statistics(host->rrdeng_ctx, local_stats_array);
457
- for (i = 0 ; i < RRDENG_NR_STATS ; ++i) {
458
- /* aggregate statistics across hosts */
459
- stats_array[i] += local_stats_array[i];
453
+ if(netdata_rwlock_tryrdlock(&rrd_rwlock) == 0) {
454
+ RRDHOST *host;
455
+ unsigned long long stats_array[RRDENG_NR_STATS] = {0};
456
+ unsigned long long local_stats_array[RRDENG_NR_STATS];
457
+ unsigned dbengine_contexts = 0, counted_multihost_db = 0, i;
458
+
459
+ rrdhost_foreach_read(host) {
460
+ if (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && !rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) {
461
+ if (&multidb_ctx == host->rrdeng_ctx) {
462
+ if (counted_multihost_db)
463
+ continue; /* Only count multi-host DB once */
464
+ counted_multihost_db = 1;
465
+ }
466
+ ++dbengine_contexts;
467
+ /* get localhost's DB engine's statistics */
468
+ rrdeng_get_37_statistics(host->rrdeng_ctx, local_stats_array);
469
+ for (i = 0; i < RRDENG_NR_STATS; ++i) {
470
+ /* aggregate statistics across hosts */
471
+ stats_array[i] += local_stats_array[i];
472
+ }
473
}
474
}
462
- }
463
- rrd_unlock();
464
-
465
- if (dbengine_contexts) {
466
- /* deduplicate global statistics by getting the ones from the last context */
467
- stats_array[30] = local_stats_array[30];
468
- stats_array[31] = local_stats_array[31];
469
- stats_array[32] = local_stats_array[32];
470
- stats_array[34] = local_stats_array[34];
471
- stats_array[36] = local_stats_array[36];
472
-
473
- // ----------------------------------------------------------------
474
-
475
- {
476
- static RRDSET *st_compression = NULL;
477
- static RRDDIM *rd_savings = NULL;
478
-
479
- if (unlikely(!st_compression)) {
480
- st_compression = rrdset_create_localhost(
481
- "netdata"
482
- , "dbengine_compression_ratio"
483
- , NULL
484
- , "dbengine"
485
- , NULL
486
- , "Netdata DB engine data extents' compression savings ratio"
487
- , "percentage"
488
- , "netdata"
489
- , "stats"
490
- , 130502
491
- , localhost->rrd_update_every
492
- , RRDSET_TYPE_LINE
493
- );
494
-
495
- rd_savings = rrddim_add(st_compression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
475
+ rrd_unlock();
476
+
477
+ if (dbengine_contexts) {
478
+ /* deduplicate global statistics by getting the ones from the last context */
479
+ stats_array[30] = local_stats_array[30];
480
+ stats_array[31] = local_stats_array[31];
481
+ stats_array[32] = local_stats_array[32];
482
+ stats_array[34] = local_stats_array[34];
483
+ stats_array[36] = local_stats_array[36];
484
+
485
+ // ----------------------------------------------------------------
486
+
487
+ {
488
+ static RRDSET *st_compression = NULL;
489
+ static RRDDIM *rd_savings = NULL;
490
+
491
+ if (unlikely(!st_compression)) {
492
+ st_compression = rrdset_create_localhost(
493
+ "netdata",
494
+ "dbengine_compression_ratio",
495
+ NULL,
496
+ "dbengine",
497
+ NULL,
498
+ "Netdata DB engine data extents' compression savings ratio",
499
+ "percentage",
500
+ "netdata",
501
+ "stats",
502
+ 130502,
503
+ localhost->rrd_update_every,
504
+ RRDSET_TYPE_LINE);
505
+
506
+ rd_savings = rrddim_add(st_compression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
507
+ } else
508
+ rrdset_next(st_compression);
509
+
510
+ unsigned long long ratio;
511
+ unsigned long long compressed_content_size = stats_array[12];
512
+ unsigned long long content_size = stats_array[11];
513
+
514
+ if (content_size) {
515
+ // allow negative savings
516
+ ratio = ((content_size - compressed_content_size) * 100 * 1000) / content_size;
517
+ } else {
518
+ ratio = 0;
519
+ }
520
+ rrddim_set_by_pointer(st_compression, rd_savings, ratio);
521
+
522
+ rrdset_done(st_compression);
523
}
497
- else
498
- rrdset_next(st_compression);
499
-
500
- unsigned long long ratio;
501
- unsigned long long compressed_content_size = stats_array[12];
502
- unsigned long long content_size = stats_array[11];
503
-
504
- if (content_size) {
505
- // allow negative savings
506
- ratio = ((content_size - compressed_content_size) * 100 * 1000) / content_size;
507
- } else {
508
- ratio = 0;
509
- }
510
- rrddim_set_by_pointer(st_compression, rd_savings, ratio);
524
512
- rrdset_done(st_compression);
513
- }
525
+ // ----------------------------------------------------------------
526
+
527
+ {
528
+ static RRDSET *st_pg_cache_hit_ratio = NULL;
529
+ static RRDDIM *rd_hit_ratio = NULL;
530
+
531
+ if (unlikely(!st_pg_cache_hit_ratio)) {
532
+ st_pg_cache_hit_ratio = rrdset_create_localhost(
533
+ "netdata",
534
+ "page_cache_hit_ratio",
535
+ NULL,
536
+ "dbengine",
537
+ NULL,
538
+ "Netdata DB engine page cache hit ratio",
539
+ "percentage",
540
+ "netdata",
541
+ "stats",
542
+ 130503,
543
+ localhost->rrd_update_every,
544
+ RRDSET_TYPE_LINE);
545
+
546
+ rd_hit_ratio = rrddim_add(st_pg_cache_hit_ratio, "ratio", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
547
+ } else
548
+ rrdset_next(st_pg_cache_hit_ratio);
549
+
550
+ static unsigned long long old_hits = 0;
551
+ static unsigned long long old_misses = 0;
552
+ unsigned long long hits = stats_array[7];
553
+ unsigned long long misses = stats_array[8];
554
+ unsigned long long hits_delta;
555
+ unsigned long long misses_delta;
556
+ unsigned long long ratio;
557
+
558
+ hits_delta = hits - old_hits;
559
+ misses_delta = misses - old_misses;
560
+ old_hits = hits;
561
+ old_misses = misses;
562
+
563
+ if (hits_delta + misses_delta) {
564
+ ratio = (hits_delta * 100 * 1000) / (hits_delta + misses_delta);
565
+ } else {
566
+ ratio = 0;
567
+ }
568
+ rrddim_set_by_pointer(st_pg_cache_hit_ratio, rd_hit_ratio, ratio);
569
+
570
+ rrdset_done(st_pg_cache_hit_ratio);
571
+ }
572
515
- // ----------------------------------------------------------------
516
-
517
- {
518
- static RRDSET *st_pg_cache_hit_ratio = NULL;
519
- static RRDDIM *rd_hit_ratio = NULL;
520
-
521
- if (unlikely(!st_pg_cache_hit_ratio)) {
522
- st_pg_cache_hit_ratio = rrdset_create_localhost(
523
- "netdata"
524
- , "page_cache_hit_ratio"
525
- , NULL
526
- , "dbengine"
527
- , NULL
528
- , "Netdata DB engine page cache hit ratio"
529
- , "percentage"
530
- , "netdata"
531
- , "stats"
532
- , 130503
533
- , localhost->rrd_update_every
534
- , RRDSET_TYPE_LINE
535
- );
536
-
537
- rd_hit_ratio = rrddim_add(st_pg_cache_hit_ratio, "ratio", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
573
+ // ----------------------------------------------------------------
574
+
575
+ {
576
+ static RRDSET *st_pg_cache_pages = NULL;
577
+ static RRDDIM *rd_descriptors = NULL;
578
+ static RRDDIM *rd_populated = NULL;
579
+ static RRDDIM *rd_dirty = NULL;
580
+ static RRDDIM *rd_backfills = NULL;
581
+ static RRDDIM *rd_evictions = NULL;
582
+ static RRDDIM *rd_used_by_collectors = NULL;
583
+
584
+ if (unlikely(!st_pg_cache_pages)) {
585
+ st_pg_cache_pages = rrdset_create_localhost(
586
+ "netdata",
587
+ "page_cache_stats",
588
+ NULL,
589
+ "dbengine",
590
+ NULL,
591
+ "Netdata dbengine page cache statistics",
592
+ "pages",
593
+ "netdata",
594
+ "stats",
595
+ 130504,
596
+ localhost->rrd_update_every,
597
+ RRDSET_TYPE_LINE);
598
+
599
+ rd_descriptors = rrddim_add(st_pg_cache_pages, "descriptors", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
600
+ rd_populated = rrddim_add(st_pg_cache_pages, "populated", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
601
+ rd_dirty = rrddim_add(st_pg_cache_pages, "dirty", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
602
+ rd_backfills = rrddim_add(st_pg_cache_pages, "backfills", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
603
+ rd_evictions = rrddim_add(st_pg_cache_pages, "evictions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
604
+ rd_used_by_collectors =
605
+ rrddim_add(st_pg_cache_pages, "used_by_collectors", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
606
+ } else
607
+ rrdset_next(st_pg_cache_pages);
608
+
609
+ rrddim_set_by_pointer(st_pg_cache_pages, rd_descriptors, (collected_number)stats_array[27]);
610
+ rrddim_set_by_pointer(st_pg_cache_pages, rd_populated, (collected_number)stats_array[3]);
611
+ rrddim_set_by_pointer(st_pg_cache_pages, rd_dirty, (collected_number)stats_array[0] + stats_array[4]);
612
+ rrddim_set_by_pointer(st_pg_cache_pages, rd_backfills, (collected_number)stats_array[9]);
613
+ rrddim_set_by_pointer(st_pg_cache_pages, rd_evictions, (collected_number)stats_array[10]);
614
+ rrddim_set_by_pointer(st_pg_cache_pages, rd_used_by_collectors, (collected_number)stats_array[0]);
615
+ rrdset_done(st_pg_cache_pages);
616
}
539
- else
540
- rrdset_next(st_pg_cache_hit_ratio);
541
-
542
- static unsigned long long old_hits = 0;
543
- static unsigned long long old_misses = 0;
544
- unsigned long long hits = stats_array[7];
545
- unsigned long long misses = stats_array[8];
546
- unsigned long long hits_delta;
547
- unsigned long long misses_delta;
548
- unsigned long long ratio;
549
-
550
- hits_delta = hits - old_hits;
551
- misses_delta = misses - old_misses;
552
- old_hits = hits;
553
- old_misses = misses;
554
-
555
- if (hits_delta + misses_delta) {
556
- ratio = (hits_delta * 100 * 1000) / (hits_delta + misses_delta);
557
- } else {
558
- ratio = 0;
617
+
618
+ // ----------------------------------------------------------------
619
+
620
+ {
621
+ static RRDSET *st_long_term_pages = NULL;
622
+ static RRDDIM *rd_total = NULL;
623
+ static RRDDIM *rd_insertions = NULL;
624
+ static RRDDIM *rd_deletions = NULL;
625
+ static RRDDIM *rd_flushing_pressure_deletions = NULL;
626
+
627
+ if (unlikely(!st_long_term_pages)) {
628
+ st_long_term_pages = rrdset_create_localhost(
629
+ "netdata",
630
+ "dbengine_long_term_page_stats",
631
+ NULL,
632
+ "dbengine",
633
+ NULL,
634
+ "Netdata dbengine long-term page statistics",
635
+ "pages",
636
+ "netdata",
637
+ "stats",
638
+ 130505,
639
+ localhost->rrd_update_every,
640
+ RRDSET_TYPE_LINE);
641
+
642
+ rd_total = rrddim_add(st_long_term_pages, "total", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
643
+ rd_insertions = rrddim_add(st_long_term_pages, "insertions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
644
+ rd_deletions = rrddim_add(st_long_term_pages, "deletions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
645
+ rd_flushing_pressure_deletions = rrddim_add(
646
+ st_long_term_pages, "flushing_pressure_deletions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
647
+ } else
648
+ rrdset_next(st_long_term_pages);
649
+
650
+ rrddim_set_by_pointer(st_long_term_pages, rd_total, (collected_number)stats_array[2]);
651
+ rrddim_set_by_pointer(st_long_term_pages, rd_insertions, (collected_number)stats_array[5]);
652
+ rrddim_set_by_pointer(st_long_term_pages, rd_deletions, (collected_number)stats_array[6]);
653
+ rrddim_set_by_pointer(
654
+ st_long_term_pages, rd_flushing_pressure_deletions, (collected_number)stats_array[36]);
655
+ rrdset_done(st_long_term_pages);
656
}
560
- rrddim_set_by_pointer(st_pg_cache_hit_ratio, rd_hit_ratio, ratio);
657
562
- rrdset_done(st_pg_cache_hit_ratio);
563
- }
658
+ // ----------------------------------------------------------------
659
+
660
+ {
661
+ static RRDSET *st_io_stats = NULL;
662
+ static RRDDIM *rd_reads = NULL;
663
+ static RRDDIM *rd_writes = NULL;
664
+
665
+ if (unlikely(!st_io_stats)) {
666
+ st_io_stats = rrdset_create_localhost(
667
+ "netdata",
668
+ "dbengine_io_throughput",
669
+ NULL,
670
+ "dbengine",
671
+ NULL,
672
+ "Netdata DB engine I/O throughput",
673
+ "MiB/s",
674
+ "netdata",
675
+ "stats",
676
+ 130506,
677
+ localhost->rrd_update_every,
678
+ RRDSET_TYPE_LINE);
679
+
680
+ rd_reads = rrddim_add(st_io_stats, "reads", NULL, 1, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
681
+ rd_writes = rrddim_add(st_io_stats, "writes", NULL, -1, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
682
+ } else
683
+ rrdset_next(st_io_stats);
684
+
685
+ rrddim_set_by_pointer(st_io_stats, rd_reads, (collected_number)stats_array[17]);
686
+ rrddim_set_by_pointer(st_io_stats, rd_writes, (collected_number)stats_array[15]);
687
+ rrdset_done(st_io_stats);
688
+ }
689
565
- // ----------------------------------------------------------------
566
-
567
- {
568
- static RRDSET *st_pg_cache_pages = NULL;
569
- static RRDDIM *rd_descriptors = NULL;
570
- static RRDDIM *rd_populated = NULL;
571
- static RRDDIM *rd_dirty = NULL;
572
- static RRDDIM *rd_backfills = NULL;
573
- static RRDDIM *rd_evictions = NULL;
574
- static RRDDIM *rd_used_by_collectors = NULL;
575
-
576
- if (unlikely(!st_pg_cache_pages)) {
577
- st_pg_cache_pages = rrdset_create_localhost(
578
- "netdata"
579
- , "page_cache_stats"
580
- , NULL
581
- , "dbengine"
582
- , NULL
583
- , "Netdata dbengine page cache statistics"
584
- , "pages"
585
- , "netdata"
586
- , "stats"
587
- , 130504
588
- , localhost->rrd_update_every
589
- , RRDSET_TYPE_LINE
590
- );
591
-
592
- rd_descriptors = rrddim_add(st_pg_cache_pages, "descriptors", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
593
- rd_populated = rrddim_add(st_pg_cache_pages, "populated", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
594
- rd_dirty = rrddim_add(st_pg_cache_pages, "dirty", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
595
- rd_backfills = rrddim_add(st_pg_cache_pages, "backfills", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
596
- rd_evictions = rrddim_add(st_pg_cache_pages, "evictions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
597
- rd_used_by_collectors = rrddim_add(st_pg_cache_pages, "used_by_collectors", NULL, 1, 1,
598
- RRD_ALGORITHM_ABSOLUTE);
690
+ // ----------------------------------------------------------------
691
+
692
+ {
693
+ static RRDSET *st_io_stats = NULL;
694
+ static RRDDIM *rd_reads = NULL;
695
+ static RRDDIM *rd_writes = NULL;
696
+
697
+ if (unlikely(!st_io_stats)) {
698
+ st_io_stats = rrdset_create_localhost(
699
+ "netdata",
700
+ "dbengine_io_operations",
701
+ NULL,
702
+ "dbengine",
703
+ NULL,
704
+ "Netdata DB engine I/O operations",
705
+ "operations/s",
706
+ "netdata",
707
+ "stats",
708
+ 130507,
709
+ localhost->rrd_update_every,
710
+ RRDSET_TYPE_LINE);
711
+
712
+ rd_reads = rrddim_add(st_io_stats, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
713
+ rd_writes = rrddim_add(st_io_stats, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
714
+ } else
715
+ rrdset_next(st_io_stats);
716
+
717
+ rrddim_set_by_pointer(st_io_stats, rd_reads, (collected_number)stats_array[18]);
718
+ rrddim_set_by_pointer(st_io_stats, rd_writes, (collected_number)stats_array[16]);
719
+ rrdset_done(st_io_stats);
720
}
600
- else
601
- rrdset_next(st_pg_cache_pages);
602
-
603
- rrddim_set_by_pointer(st_pg_cache_pages, rd_descriptors, (collected_number)stats_array[27]);
604
- rrddim_set_by_pointer(st_pg_cache_pages, rd_populated, (collected_number)stats_array[3]);
605
- rrddim_set_by_pointer(st_pg_cache_pages, rd_dirty, (collected_number)stats_array[0] + stats_array[4]);
606
- rrddim_set_by_pointer(st_pg_cache_pages, rd_backfills, (collected_number)stats_array[9]);
607
- rrddim_set_by_pointer(st_pg_cache_pages, rd_evictions, (collected_number)stats_array[10]);
608
- rrddim_set_by_pointer(st_pg_cache_pages, rd_used_by_collectors, (collected_number)stats_array[0]);
609
- rrdset_done(st_pg_cache_pages);
610
- }
721
612
- // ----------------------------------------------------------------
722
+ // ----------------------------------------------------------------
723
+
724
+ {
725
+ static RRDSET *st_errors = NULL;
726
+ static RRDDIM *rd_fs_errors = NULL;
727
+ static RRDDIM *rd_io_errors = NULL;
728
+ static RRDDIM *pg_cache_over_half_dirty_events = NULL;
729
+
730
+ if (unlikely(!st_errors)) {
731
+ st_errors = rrdset_create_localhost(
732
+ "netdata",
733
+ "dbengine_global_errors",
734
+ NULL,
735
+ "dbengine",
736
+ NULL,
737
+ "Netdata DB engine errors",
738
+ "errors/s",
739
+ "netdata",
740
+ "stats",
741
+ 130508,
742
+ localhost->rrd_update_every,
743
+ RRDSET_TYPE_LINE);
744
+
745
+ rd_io_errors = rrddim_add(st_errors, "io_errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
746
+ rd_fs_errors = rrddim_add(st_errors, "fs_errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
747
+ pg_cache_over_half_dirty_events =
748
+ rrddim_add(st_errors, "pg_cache_over_half_dirty_events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
749
+ } else
750
+ rrdset_next(st_errors);
751
+
752
+ rrddim_set_by_pointer(st_errors, rd_io_errors, (collected_number)stats_array[30]);
753
+ rrddim_set_by_pointer(st_errors, rd_fs_errors, (collected_number)stats_array[31]);
754
+ rrddim_set_by_pointer(st_errors, pg_cache_over_half_dirty_events, (collected_number)stats_array[34]);
755
+ rrdset_done(st_errors);
756
+ }
757
614
- {
615
- static RRDSET *st_long_term_pages = NULL;
616
- static RRDDIM *rd_total = NULL;
617
- static RRDDIM *rd_insertions = NULL;
618
- static RRDDIM *rd_deletions = NULL;
619
- static RRDDIM *rd_flushing_pressure_deletions = NULL;
758
+ // ----------------------------------------------------------------
759
+
760
+ {
761
+ static RRDSET *st_fd = NULL;
762
+ static RRDDIM *rd_fd_current = NULL;
763
+ static RRDDIM *rd_fd_max = NULL;
764
+
765
+ if (unlikely(!st_fd)) {
766
+ st_fd = rrdset_create_localhost(
767
+ "netdata",
768
+ "dbengine_global_file_descriptors",
769
+ NULL,
770
+ "dbengine",
771
+ NULL,
772
+ "Netdata DB engine File Descriptors",
773
+ "descriptors",
774
+ "netdata",
775
+ "stats",
776
+ 130509,
777
+ localhost->rrd_update_every,
778
+ RRDSET_TYPE_LINE);
779
+
780
+ rd_fd_current = rrddim_add(st_fd, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
781
+ rd_fd_max = rrddim_add(st_fd, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
782
+ } else
783
+ rrdset_next(st_fd);
784
+
785
+ rrddim_set_by_pointer(st_fd, rd_fd_current, (collected_number)stats_array[32]);
786
+ /* Careful here, modify this accordingly if the File-Descriptor budget ever changes */
787
+ rrddim_set_by_pointer(st_fd, rd_fd_max, (collected_number)rlimit_nofile.rlim_cur / 4);
788
+ rrdset_done(st_fd);
789
+ }
790
621
- if (unlikely(!st_long_term_pages)) {
622
- st_long_term_pages = rrdset_create_localhost(
623
- "netdata"
624
- , "dbengine_long_term_page_stats"
625
- , NULL
626
- , "dbengine"
627
- , NULL
628
- , "Netdata dbengine long-term page statistics"
629
- , "pages"
630
- , "netdata"
631
- , "stats"
632
- , 130505
633
- , localhost->rrd_update_every
634
- , RRDSET_TYPE_LINE
635
- );
636
-
637
- rd_total = rrddim_add(st_long_term_pages, "total", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
638
- rd_insertions = rrddim_add(st_long_term_pages, "insertions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
639
- rd_deletions = rrddim_add(st_long_term_pages, "deletions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
640
- rd_flushing_pressure_deletions = rrddim_add(st_long_term_pages, "flushing_pressure_deletions", NULL, -1,
641
- 1, RRD_ALGORITHM_INCREMENTAL);
791
+ // ----------------------------------------------------------------
792
+
793
+ {
794
+ static RRDSET *st_ram_usage = NULL;
795
+ static RRDDIM *rd_cached = NULL;
796
+ static RRDDIM *rd_pinned = NULL;
797
+ static RRDDIM *rd_metadata = NULL;
798
+
799
+ collected_number cached_pages, pinned_pages, API_producers, populated_pages, metadata, pages_on_disk,
800
+ page_cache_descriptors;
801
+
802
+ if (unlikely(!st_ram_usage)) {
803
+ st_ram_usage = rrdset_create_localhost(
804
+ "netdata",
805
+ "dbengine_ram",
806
+ NULL,
807
+ "dbengine",
808
+ NULL,
809
+ "Netdata DB engine RAM usage",
810
+ "MiB",
811
+ "netdata",
812
+ "stats",
813
+ 130510,
814
+ localhost->rrd_update_every,
815
+ RRDSET_TYPE_STACKED);
816
+
817
+ rd_cached = rrddim_add(st_ram_usage, "cache", NULL, 1, 256, RRD_ALGORITHM_ABSOLUTE);
818
+ rd_pinned = rrddim_add(st_ram_usage, "collectors", NULL, 1, 256, RRD_ALGORITHM_ABSOLUTE);
819
+ rd_metadata = rrddim_add(st_ram_usage, "metadata", NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE);
820
+ } else
821
+ rrdset_next(st_ram_usage);
822
+
823
+ API_producers = (collected_number)stats_array[0];
824
+ pages_on_disk = (collected_number)stats_array[2];
825
+ populated_pages = (collected_number)stats_array[3];
826
+ page_cache_descriptors = (collected_number)stats_array[27];
827
+
828
+ if (API_producers * 2 > populated_pages) {
829
+ pinned_pages = API_producers;
830
+ } else {
831
+ pinned_pages = API_producers * 2;
832
+ }
833
+ cached_pages = populated_pages - pinned_pages;
834
+
835
+ metadata = page_cache_descriptors * sizeof(struct page_cache_descr);
836
+ metadata += pages_on_disk * sizeof(struct rrdeng_page_descr);
837
+ /* This is an empirical estimation for Judy array indexing and extent structures */
838
+ metadata += pages_on_disk * 58;
839
+
840
+ rrddim_set_by_pointer(st_ram_usage, rd_cached, cached_pages);
841
+ rrddim_set_by_pointer(st_ram_usage, rd_pinned, pinned_pages);
842
+ rrddim_set_by_pointer(st_ram_usage, rd_metadata, metadata);
843
+ rrdset_done(st_ram_usage);
844
}
643
- else
644
- rrdset_next(st_long_term_pages);
645
-
646
- rrddim_set_by_pointer(st_long_term_pages, rd_total, (collected_number)stats_array[2]);
647
- rrddim_set_by_pointer(st_long_term_pages, rd_insertions, (collected_number)stats_array[5]);
648
- rrddim_set_by_pointer(st_long_term_pages, rd_deletions, (collected_number)stats_array[6]);
649
- rrddim_set_by_pointer(st_long_term_pages, rd_flushing_pressure_deletions,
650
- (collected_number)stats_array[36]);
651
- rrdset_done(st_long_term_pages);
845
}
846
+ }
847
+#endif
848
+}
849
+
850
+static void update_heartbeat_charts() {
851
+ RRDSET *st = rrdset_create_localhost(
852
+ "netdata"
853
+ , "heartbeat"
854
+ , NULL
855
+ , "heartbeat"
856
+ , NULL
857
+ , "System clock jitter"
858
+ , "microseconds"
859
+ , "netdata"
860
+ , "stats"
861
+ , 900000
862
+ , localhost->rrd_update_every
863
+ , RRDSET_TYPE_AREA
864
+ );
865
+
866
+ RRDDIM *rd_min = rrddim_add(st, "min", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
867
+ RRDDIM *rd_max = rrddim_add(st, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
868
+ RRDDIM *rd_avg = rrddim_add(st, "average", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
869
+
870
+ rrdset_next(st);
871
+
872
+ usec_t min, max, average;
873
+ size_t count;
874
+
875
+ heartbeat_statistics(&min, &max, &average, &count);
876
+
877
+ rrddim_set_by_pointer(st, rd_min, (collected_number)min);
878
+ rrddim_set_by_pointer(st, rd_max, (collected_number)max);
879
+ rrddim_set_by_pointer(st, rd_avg, (collected_number)average);
880
+
881
+ rrdset_done(st);
882
+}
883
+
884
+// ---------------------------------------------------------------------------------------------------------------------
885
+// worker utilization
886
+
887
+struct worker_job_type {
888
+ char name[WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH + 1];
889
+ size_t jobs_started;
890
+ usec_t busy_time;
891
+
892
+ RRDDIM *rd_jobs_started;
893
+ RRDDIM *rd_busy_time;
894
+};
895
+
896
+struct worker_thread {
897
+ pid_t pid;
898
+ int enabled;
899
+
900
+ int cpu_enabled;
901
+
902
+ kernel_uint_t utime;
903
+ kernel_uint_t stime;
904
+
905
+ kernel_uint_t utime_old;
906
+ kernel_uint_t stime_old;
907
+
908
+ usec_t collected_time;
909
+ usec_t collected_time_old;
910
+
911
+ size_t jobs_started;
912
+ usec_t busy_time;
913
+
914
+ struct worker_thread *next;
915
+};
916
+
917
+struct worker_utilization {
918
+ const char *name;
919
+ const char *family;
920
+ size_t priority;
921
+ uint32_t flags;
922
+
923
+ char *name_lowercase;
924
+
925
+ struct worker_job_type per_job_type[WORKER_UTILIZATION_MAX_JOB_TYPES];
926
+
927
+ size_t workers_registered;
928
+ size_t workers_busy;
929
+ usec_t workers_total_busy_time;
930
+ usec_t workers_total_duration;
931
+ size_t workers_total_jobs_started;
932
+ double workers_min_busy_time;
933
+ double workers_max_busy_time;
934
654
- // ----------------------------------------------------------------
935
+ struct worker_thread *threads;
936
656
- {
657
- static RRDSET *st_io_stats = NULL;
658
- static RRDDIM *rd_reads = NULL;
659
- static RRDDIM *rd_writes = NULL;
937
+ RRDSET *st_workers_time;
938
+ RRDDIM *rd_workers_time_avg;
939
+ RRDDIM *rd_workers_time_min;
940
+ RRDDIM *rd_workers_time_max;
941
661
- if (unlikely(!st_io_stats)) {
662
- st_io_stats = rrdset_create_localhost(
942
+ size_t workers_cpu_enabled;
943
+ RRDSET *st_workers_cpu;
944
+ RRDDIM *rd_workers_cpu_avg;
945
+ RRDDIM *rd_workers_cpu_min;
946
+ RRDDIM *rd_workers_cpu_max;
947
+
948
+ RRDSET *st_workers_threads;
949
+ RRDDIM *rd_workers_threads_free;
950
+ RRDDIM *rd_workers_threads_busy;
951
+
952
+ RRDSET *st_workers_jobs_per_job_type;
953
+ RRDSET *st_workers_busy_per_job_type;
954
+};
955
+
956
+static void workers_utilization_update_chart(struct worker_utilization *wu) {
957
+ if(!wu->workers_registered) return;
958
+
959
+ //fprintf(stderr, "%-12s WORKER UTILIZATION: %-3.2f%%, %zu jobs done, %zu running, on %zu workers, min %-3.02f%%, max %-3.02f%%.\n",
960
+ // wu->name,
961
+ // (double)wu->workers_total_busy_time * 100.0 / (double)wu->workers_total_duration,
962
+ // wu->workers_total_jobs_started, wu->workers_busy, wu->workers_registered,
963
+ // wu->workers_min_busy_time, wu->workers_max_busy_time);
964
+
965
+ // ----------------------------------------------------------------------
966
+
967
+ if(unlikely(!wu->st_workers_time)) {
968
+ char name[RRD_ID_LENGTH_MAX + 1];
969
+ snprintfz(name, RRD_ID_LENGTH_MAX, "workers_time_%s", wu->name_lowercase);
970
+
971
+ char context[RRD_ID_LENGTH_MAX + 1];
972
+ snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.time", wu->name_lowercase);
973
+
974
+ wu->st_workers_time = rrdset_create_localhost(
975
+ "netdata"
976
+ , name
977
+ , NULL
978
+ , wu->family
979
+ , context
980
+ , "Netdata Workers Busy Time (100% = all workers busy)"
981
+ , "%"
982
+ , "netdata"
983
+ , "stats"
984
+ , wu->priority
985
+ , localhost->rrd_update_every
986
+ , RRDSET_TYPE_AREA
987
+ );
988
+ }
989
+
990
+ // we add the min and max dimensions only when we have multiple workers
991
+
992
+ if(unlikely(!wu->rd_workers_time_min && wu->workers_registered > 1))
993
+ wu->rd_workers_time_min = rrddim_add(wu->st_workers_time, "min", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
994
+
995
+ if(unlikely(!wu->rd_workers_time_max && wu->workers_registered > 1))
996
+ wu->rd_workers_time_max = rrddim_add(wu->st_workers_time, "max", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
997
+
998
+ if(unlikely(!wu->rd_workers_time_avg))
999
+ wu->rd_workers_time_avg = rrddim_add(wu->st_workers_time, "average", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
1000
+
1001
+ rrdset_next(wu->st_workers_time);
1002
+
1003
+ if(wu->rd_workers_time_min)
1004
+ rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_min, (collected_number)((double)wu->workers_min_busy_time * 10000.0));
1005
+
1006
+ if(wu->rd_workers_time_max)
1007
+ rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_max, (collected_number)((double)wu->workers_max_busy_time * 10000.0));
1008
+
1009
+ rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_avg, (collected_number)((double)wu->workers_total_busy_time * 100.0 * 10000.0 / (double)wu->workers_total_duration));
1010
+ rrdset_done(wu->st_workers_time);
1011
+
1012
+ // ----------------------------------------------------------------------
1013
+
1014
+#ifdef __linux__
1015
+ if(wu->workers_cpu_enabled || wu->st_workers_cpu) {
1016
+ if(unlikely(!wu->st_workers_cpu)) {
1017
+ char name[RRD_ID_LENGTH_MAX + 1];
1018
+ snprintfz(name, RRD_ID_LENGTH_MAX, "workers_cpu_%s", wu->name_lowercase);
1019
+
1020
+ char context[RRD_ID_LENGTH_MAX + 1];
1021
+ snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.cpu", wu->name_lowercase);
1022
+
1023
+ wu->st_workers_cpu = rrdset_create_localhost(
1024
"netdata"
664
- , "dbengine_io_throughput"
665
- , NULL
666
- , "dbengine"
1025
+ , name
1026
, NULL
668
- , "Netdata DB engine I/O throughput"
669
- , "MiB/s"
1027
+ , wu->family
1028
+ , context
1029
+ , "Netdata Workers CPU Utilization (100% = all workers busy)"
1030
+ , "%"
1031
, "netdata"
1032
, "stats"
672
- , 130506
1033
+ , wu->priority + 1
1034
, localhost->rrd_update_every
674
- , RRDSET_TYPE_LINE
675
- );
1035
+ , RRDSET_TYPE_AREA
1036
+ );
1037
+ }
1038
677
- rd_reads = rrddim_add(st_io_stats, "reads", NULL, 1, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
678
- rd_writes = rrddim_add(st_io_stats, "writes", NULL, -1, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
679
- }
680
- else
681
- rrdset_next(st_io_stats);
1039
+ if (unlikely(!wu->rd_workers_cpu_min && wu->workers_registered > 1))
1040
+ wu->rd_workers_cpu_min = rrddim_add(wu->st_workers_cpu, "min", NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE);
1041
683
- rrddim_set_by_pointer(st_io_stats, rd_reads, (collected_number)stats_array[17]);
684
- rrddim_set_by_pointer(st_io_stats, rd_writes, (collected_number)stats_array[15]);
685
- rrdset_done(st_io_stats);
686
- }
1042
+ if (unlikely(!wu->rd_workers_cpu_max && wu->workers_registered > 1))
1043
+ wu->rd_workers_cpu_max = rrddim_add(wu->st_workers_cpu, "max", NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE);
1044
688
- // ----------------------------------------------------------------
689
-
690
- {
691
- static RRDSET *st_io_stats = NULL;
692
- static RRDDIM *rd_reads = NULL;
693
- static RRDDIM *rd_writes = NULL;
694
-
695
- if (unlikely(!st_io_stats)) {
696
- st_io_stats = rrdset_create_localhost(
697
- "netdata"
698
- , "dbengine_io_operations"
699
- , NULL
700
- , "dbengine"
701
- , NULL
702
- , "Netdata DB engine I/O operations"
703
- , "operations/s"
704
- , "netdata"
705
- , "stats"
706
- , 130507
707
- , localhost->rrd_update_every
708
- , RRDSET_TYPE_LINE
709
- );
710
-
711
- rd_reads = rrddim_add(st_io_stats, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
712
- rd_writes = rrddim_add(st_io_stats, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
713
- }
714
- else
715
- rrdset_next(st_io_stats);
1045
+ if(unlikely(!wu->rd_workers_cpu_avg))
1046
+ wu->rd_workers_cpu_avg = rrddim_add(wu->st_workers_cpu, "average", NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE);
1047
717
- rrddim_set_by_pointer(st_io_stats, rd_reads, (collected_number)stats_array[18]);
718
- rrddim_set_by_pointer(st_io_stats, rd_writes, (collected_number)stats_array[16]);
719
- rrdset_done(st_io_stats);
720
- }
1048
+ rrdset_next(wu->st_workers_cpu);
1049
722
- // ----------------------------------------------------------------
723
-
724
- {
725
- static RRDSET *st_errors = NULL;
726
- static RRDDIM *rd_fs_errors = NULL;
727
- static RRDDIM *rd_io_errors = NULL;
728
- static RRDDIM *pg_cache_over_half_dirty_events = NULL;
729
-
730
- if (unlikely(!st_errors)) {
731
- st_errors = rrdset_create_localhost(
732
- "netdata"
733
- , "dbengine_global_errors"
734
- , NULL
735
- , "dbengine"
736
- , NULL
737
- , "Netdata DB engine errors"
738
- , "errors/s"
739
- , "netdata"
740
- , "stats"
741
- , 130508
742
- , localhost->rrd_update_every
743
- , RRDSET_TYPE_LINE
744
- );
745
-
746
- rd_io_errors = rrddim_add(st_errors, "io_errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
747
- rd_fs_errors = rrddim_add(st_errors, "fs_errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
748
- pg_cache_over_half_dirty_events = rrddim_add(st_errors, "pg_cache_over_half_dirty_events", NULL, 1, 1,
749
- RRD_ALGORITHM_INCREMENTAL);
750
- }
751
- else
752
- rrdset_next(st_errors);
1050
+ size_t count = 0;
1051
+ calculated_number min = 1000.0, max = 0.0, total = 0.0;
1052
+ struct worker_thread *wt;
1053
+ for(wt = wu->threads; wt ; wt = wt->next) {
1054
+ if(!wt->cpu_enabled) continue;
1055
+ count++;
1056
754
- rrddim_set_by_pointer(st_errors, rd_io_errors, (collected_number)stats_array[30]);
755
- rrddim_set_by_pointer(st_errors, rd_fs_errors, (collected_number)stats_array[31]);
756
- rrddim_set_by_pointer(st_errors, pg_cache_over_half_dirty_events, (collected_number)stats_array[34]);
757
- rrdset_done(st_errors);
1057
+ usec_t delta = wt->collected_time - wt->collected_time_old;
1058
+ calculated_number utime = (calculated_number)(wt->utime - wt->utime_old) / (calculated_number)system_hz * 100.0 * (calculated_number)USEC_PER_SEC / (calculated_number)delta;
1059
+ calculated_number stime = (calculated_number)(wt->stime - wt->stime_old) / (calculated_number)system_hz * 100.0 * (calculated_number)USEC_PER_SEC / (calculated_number)delta;
1060
+ calculated_number cpu_util = utime + stime;
1061
+
1062
+ total += cpu_util;
1063
+ if(cpu_util < min) min = cpu_util;
1064
+ if(cpu_util > max) max = cpu_util;
1065
}
1066
+ if(unlikely(min == 1000.0)) min = 0.0;
1067
+
1068
+ if(wu->rd_workers_cpu_min)
1069
+ rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_min, (collected_number)(min * 10000ULL));
1070
+
1071
+ if(wu->rd_workers_cpu_max)
1072
+ rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_max, (collected_number)(max * 10000ULL));
1073
760
- // ----------------------------------------------------------------
761
-
762
- {
763
- static RRDSET *st_fd = NULL;
764
- static RRDDIM *rd_fd_current = NULL;
765
- static RRDDIM *rd_fd_max = NULL;
766
-
767
- if (unlikely(!st_fd)) {
768
- st_fd = rrdset_create_localhost(
769
- "netdata"
770
- , "dbengine_global_file_descriptors"
771
- , NULL
772
- , "dbengine"
773
- , NULL
774
- , "Netdata DB engine File Descriptors"
775
- , "descriptors"
776
- , "netdata"
777
- , "stats"
778
- , 130509
779
- , localhost->rrd_update_every
780
- , RRDSET_TYPE_LINE
781
- );
782
-
783
- rd_fd_current = rrddim_add(st_fd, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
784
- rd_fd_max = rrddim_add(st_fd, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1074
+ rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_avg, (collected_number)( total * 10000ULL / (calculated_number)count ));
1075
+ rrdset_done(wu->st_workers_cpu);
1076
+ }
1077
+#endif
1078
+
1079
+ // ----------------------------------------------------------------------
1080
+
1081
+ if(unlikely(!wu->st_workers_jobs_per_job_type)) {
1082
+ char name[RRD_ID_LENGTH_MAX + 1];
1083
+ snprintfz(name, RRD_ID_LENGTH_MAX, "workers_jobs_by_type_%s", wu->name_lowercase);
1084
+
1085
+ char context[RRD_ID_LENGTH_MAX + 1];
1086
+ snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.jobs_started_by_type", wu->name_lowercase);
1087
+
1088
+ wu->st_workers_jobs_per_job_type = rrdset_create_localhost(
1089
+ "netdata"
1090
+ , name
1091
+ , NULL
1092
+ , wu->family
1093
+ , context
1094
+ , "Netdata Workers Jobs Started by Type"
1095
+ , "jobs"
1096
+ , "netdata"
1097
+ , "stats"
1098
+ , wu->priority + 2
1099
+ , localhost->rrd_update_every
1100
+ , RRDSET_TYPE_STACKED
1101
+ );
1102
+ }
1103
+
1104
+ rrdset_next(wu->st_workers_jobs_per_job_type);
1105
+
1106
+ {
1107
+ size_t i;
1108
+ for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
1109
+ if (wu->per_job_type[i].name[0]) {
1110
+
1111
+ if(unlikely(!wu->per_job_type[i].rd_jobs_started))
1112
+ wu->per_job_type[i].rd_jobs_started = rrddim_add(wu->st_workers_jobs_per_job_type, wu->per_job_type[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1113
+
1114
+ rrddim_set_by_pointer(wu->st_workers_jobs_per_job_type, wu->per_job_type[i].rd_jobs_started, (collected_number)(wu->per_job_type[i].jobs_started));
1115
}
786
- else
787
- rrdset_next(st_fd);
1116
+ }
1117
+ }
1118
789
- rrddim_set_by_pointer(st_fd, rd_fd_current, (collected_number)stats_array[32]);
790
- /* Careful here, modify this accordingly if the File-Descriptor budget ever changes */
791
- rrddim_set_by_pointer(st_fd, rd_fd_max, (collected_number)rlimit_nofile.rlim_cur / 4);
792
- rrdset_done(st_fd);
1119
+ rrdset_done(wu->st_workers_jobs_per_job_type);
1120
+
1121
+ // ----------------------------------------------------------------------
1122
+
1123
+ if(unlikely(!wu->st_workers_busy_per_job_type)) {
1124
+ char name[RRD_ID_LENGTH_MAX + 1];
1125
+ snprintfz(name, RRD_ID_LENGTH_MAX, "workers_busy_time_by_type_%s", wu->name_lowercase);
1126
+
1127
+ char context[RRD_ID_LENGTH_MAX + 1];
1128
+ snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.time_by_type", wu->name_lowercase);
1129
+
1130
+ wu->st_workers_busy_per_job_type = rrdset_create_localhost(
1131
+ "netdata"
1132
+ , name
1133
+ , NULL
1134
+ , wu->family
1135
+ , context
1136
+ , "Netdata Workers Busy Time by Type"
1137
+ , "ms"
1138
+ , "netdata"
1139
+ , "stats"
1140
+ , wu->priority + 3
1141
+ , localhost->rrd_update_every
1142
+ , RRDSET_TYPE_STACKED
1143
+ );
1144
+ }
1145
+
1146
+ rrdset_next(wu->st_workers_busy_per_job_type);
1147
+
1148
+ {
1149
+ size_t i;
1150
+ for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
1151
+ if (wu->per_job_type[i].name[0]) {
1152
+
1153
+ if(unlikely(!wu->per_job_type[i].rd_busy_time))
1154
+ wu->per_job_type[i].rd_busy_time = rrddim_add(wu->st_workers_busy_per_job_type, wu->per_job_type[i].name, NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE);
1155
+
1156
+ rrddim_set_by_pointer(wu->st_workers_busy_per_job_type, wu->per_job_type[i].rd_busy_time, (collected_number)(wu->per_job_type[i].busy_time));
1157
+ }
1158
}
1159
+ }
1160
795
- // ----------------------------------------------------------------
1161
+ rrdset_done(wu->st_workers_busy_per_job_type);
1162
797
- {
798
- static RRDSET *st_ram_usage = NULL;
799
- static RRDDIM *rd_cached = NULL;
800
- static RRDDIM *rd_pinned = NULL;
801
- static RRDDIM *rd_metadata = NULL;
1163
+ // ----------------------------------------------------------------------
1164
803
- collected_number cached_pages, pinned_pages, API_producers, populated_pages, metadata, pages_on_disk,
804
- page_cache_descriptors;
1165
+ if(wu->st_workers_threads || wu->workers_registered > 1) {
1166
+ if(unlikely(!wu->st_workers_threads)) {
1167
+ char name[RRD_ID_LENGTH_MAX + 1];
1168
+ snprintfz(name, RRD_ID_LENGTH_MAX, "workers_threads_%s", wu->name_lowercase);
1169
806
- if (unlikely(!st_ram_usage)) {
807
- st_ram_usage = rrdset_create_localhost(
1170
+ char context[RRD_ID_LENGTH_MAX + 1];
1171
+ snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.threads", wu->name_lowercase);
1172
+
1173
+ wu->st_workers_threads = rrdset_create_localhost(
1174
"netdata"
809
- , "dbengine_ram"
810
- , NULL
811
- , "dbengine"
1175
+ , name
1176
, NULL
813
- , "Netdata DB engine RAM usage"
814
- , "MiB"
1177
+ , wu->family
1178
+ , context
1179
+ , "Netdata Workers Threads"
1180
+ , "threads"
1181
, "netdata"
1182
, "stats"
817
- , 130510
1183
+ , wu->priority + 4
1184
, localhost->rrd_update_every
1185
, RRDSET_TYPE_STACKED
820
- );
1186
+ );
1187
822
- rd_cached = rrddim_add(st_ram_usage, "cache", NULL, 1, 256, RRD_ALGORITHM_ABSOLUTE);
823
- rd_pinned = rrddim_add(st_ram_usage, "collectors", NULL, 1, 256, RRD_ALGORITHM_ABSOLUTE);
824
- rd_metadata = rrddim_add(st_ram_usage, "metadata", NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE);
825
- }
826
- else
827
- rrdset_next(st_ram_usage);
828
-
829
- API_producers = (collected_number)stats_array[0];
830
- pages_on_disk = (collected_number)stats_array[2];
831
- populated_pages = (collected_number)stats_array[3];
832
- page_cache_descriptors = (collected_number)stats_array[27];
833
-
834
- if (API_producers * 2 > populated_pages) {
835
- pinned_pages = API_producers;
836
- } else{
837
- pinned_pages = API_producers * 2;
838
- }
839
- cached_pages = populated_pages - pinned_pages;
1188
+ wu->rd_workers_threads_free = rrddim_add(wu->st_workers_threads, "free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1189
+ wu->rd_workers_threads_busy = rrddim_add(wu->st_workers_threads, "busy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
1190
+ }
1191
+ else
1192
+ rrdset_next(wu->st_workers_threads);
1193
841
- metadata = page_cache_descriptors * sizeof(struct page_cache_descr);
842
- metadata += pages_on_disk * sizeof(struct rrdeng_page_descr);
843
- /* This is an empirical estimation for Judy array indexing and extent structures */
844
- metadata += pages_on_disk * 58;
1194
+ rrddim_set_by_pointer(wu->st_workers_threads, wu->rd_workers_threads_free, (collected_number)(wu->workers_registered - wu->workers_busy));
1195
+ rrddim_set_by_pointer(wu->st_workers_threads, wu->rd_workers_threads_busy, (collected_number)(wu->workers_busy));
1196
+ rrdset_done(wu->st_workers_threads);
1197
+ }
1198
+}
1199
846
- rrddim_set_by_pointer(st_ram_usage, rd_cached, cached_pages);
847
- rrddim_set_by_pointer(st_ram_usage, rd_pinned, pinned_pages);
848
- rrddim_set_by_pointer(st_ram_usage, rd_metadata, metadata);
849
- rrdset_done(st_ram_usage);
1200
+static void workers_utilization_reset_statistics(struct worker_utilization *wu) {
1201
+ wu->workers_registered = 0;
1202
+ wu->workers_busy = 0;
1203
+ wu->workers_total_busy_time = 0;
1204
+ wu->workers_total_duration = 0;
1205
+ wu->workers_total_jobs_started = 0;
1206
+ wu->workers_min_busy_time = 100.0;
1207
+ wu->workers_max_busy_time = 0;
1208
+ wu->workers_cpu_enabled = 0;
1209
+
1210
+ size_t i;
1211
+ for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
1212
+ if(unlikely(!wu->name_lowercase)) {
1213
+ wu->name_lowercase = strdupz(wu->name);
1214
+ char *s = wu->name_lowercase;
1215
+ for( ; *s ; s++) *s = tolower(*s);
1216
}
1217
+
1218
+ wu->per_job_type[i].jobs_started = 0;
1219
+ wu->per_job_type[i].busy_time = 0;
1220
+ }
1221
+
1222
+ struct worker_thread *wt;
1223
+ for(wt = wu->threads; wt ; wt = wt->next) {
1224
+ wt->enabled = 0;
1225
+ wt->cpu_enabled = 0;
1226
}
1227
+}
1228
+
1229
+static int read_thread_cpu_time_from_proc_stat(pid_t pid __maybe_unused, kernel_uint_t *utime __maybe_unused, kernel_uint_t *stime __maybe_unused) {
1230
+#ifdef __linux__
1231
+ char filename[200 + 1];
1232
+ snprintfz(filename, 200, "/proc/self/task/%d/stat", pid);
1233
+
1234
+ procfile *ff = procfile_open(filename, " ", PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
1235
+ if(!ff) return -1;
1236
+
1237
+ ff = procfile_readall(ff);
1238
+ if(!ff) return -1;
1239
+
1240
+ *utime = str2kernel_uint_t(procfile_lineword(ff, 0, 13));
1241
+ *stime = str2kernel_uint_t(procfile_lineword(ff, 0, 14));
1242
+
1243
+ procfile_close(ff);
1244
+ return 0;
1245
+#else
1246
+ // TODO: add here cpu time detection per thread, for FreeBSD and MacOS
1247
+ *utime = 0;
1248
+ *stime = 0;
1249
+ return 1;
1250
#endif
1251
+}
1252
+
1253
+static void workers_threads_cleanup(struct worker_utilization *wu) {
1254
+ struct worker_thread *t;
1255
+
1256
+ // free threads at the beginning of the linked list
1257
+ while(wu->threads && !wu->threads->enabled) {
1258
+ t = wu->threads;
1259
+ wu->threads = t->next;
1260
+ t->next = NULL;
1261
+ freez(t);
1262
+ }
1263
+
1264
+ // free threads in the middle of the linked list
1265
+ for(t = wu->threads; t && t->next ; t = t->next) {
1266
+ if(t->next->enabled) continue;
1267
+
1268
+ struct worker_thread *to_remove = t->next;
1269
+ t->next = to_remove->next;
1270
+ to_remove->next = NULL;
1271
+ freez(to_remove);
1272
+ }
1273
+}
1274
+
1275
+static struct worker_thread *worker_thread_find(struct worker_utilization *wu, pid_t pid) {
1276
+ struct worker_thread *wt;
1277
+ for(wt = wu->threads; wt && wt->pid != pid ; wt = wt->next) ;
1278
+ return wt;
1279
+}
1280
+
1281
+static struct worker_thread *worker_thread_create(struct worker_utilization *wu, pid_t pid) {
1282
+ struct worker_thread *wt;
1283
+
1284
+ wt = (struct worker_thread *)callocz(1, sizeof(struct worker_thread));
1285
+ wt->pid = pid;
1286
+
1287
+ // link it
1288
+ wt->next = wu->threads;
1289
+ wu->threads = wt;
1290
1291
+ return wt;
1292
}
1293
1294
+static struct worker_thread *worker_thread_find_or_create(struct worker_utilization *wu, pid_t pid) {
1295
+ struct worker_thread *wt;
1296
+ wt = worker_thread_find(wu, pid);
1297
+ if(!wt) wt = worker_thread_create(wu, pid);
1298
+
1299
+ return wt;
1300
+}
1301
+
1302
+static void worker_utilization_charts_callback(void *ptr, pid_t pid __maybe_unused, const char *thread_tag __maybe_unused, size_t utilization_usec __maybe_unused, size_t duration_usec __maybe_unused, size_t jobs_started __maybe_unused, size_t is_running __maybe_unused, const char **job_types_names __maybe_unused, size_t *job_types_jobs_started __maybe_unused, usec_t *job_types_busy_time __maybe_unused) {
1303
+ struct worker_utilization *wu = (struct worker_utilization *)ptr;
1304
+
1305
+ // find the worker_thread in the list
1306
+ struct worker_thread *wt = worker_thread_find_or_create(wu, pid);
1307
+
1308
+ wt->enabled = 1;
1309
+ wt->busy_time = utilization_usec;
1310
+ wt->jobs_started = jobs_started;
1311
+
1312
+ wt->utime_old = wt->utime;
1313
+ wt->stime_old = wt->stime;
1314
+ wt->collected_time_old = wt->collected_time;
1315
+
1316
+ wu->workers_total_busy_time += utilization_usec;
1317
+ wu->workers_total_duration += duration_usec;
1318
+ wu->workers_total_jobs_started += jobs_started;
1319
+ wu->workers_busy += is_running;
1320
+ wu->workers_registered++;
1321
+
1322
+ double util = (double)utilization_usec * 100.0 / (double)duration_usec;
1323
+ if(util > wu->workers_max_busy_time)
1324
+ wu->workers_max_busy_time = util;
1325
+
1326
+ if(util < wu->workers_min_busy_time)
1327
+ wu->workers_min_busy_time = util;
1328
+
1329
+ // accumulate per job type statistics
1330
+ size_t i;
1331
+ for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
1332
+ wu->per_job_type[i].jobs_started += job_types_jobs_started[i];
1333
+ wu->per_job_type[i].busy_time += job_types_busy_time[i];
1334
+
1335
+ // new job type found
1336
+ if(unlikely(!wu->per_job_type[i].name[0] && job_types_names[i]))
1337
+ strncpyz(wu->per_job_type[i].name, job_types_names[i], WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH);
1338
+ }
1339
+
1340
+ // find its CPU utilization
1341
+ if((!read_thread_cpu_time_from_proc_stat(pid, &wt->utime, &wt->stime))) {
1342
+ wt->cpu_enabled = 1;
1343
+ wt->collected_time = now_realtime_usec();
1344
+ }
1345
+ wu->workers_cpu_enabled += wt->cpu_enabled;
1346
+}
1347
+
1348
+static struct worker_utilization all_workers_utilization[] = {
1349
+ { .name = "STATS", .family = "workers global statistics", .priority = 1000000 },
1350
+ { .name = "HEALTH", .family = "workers health alarms", .priority = 1000000 },
1351
+ { .name = "MLTRAIN", .family = "workers ML training", .priority = 1000000 },
1352
+ { .name = "MLDETECT", .family = "workers ML detection", .priority = 1000000 },
1353
+ { .name = "STREAMRCV", .family = "workers streaming receive", .priority = 1000000 },
1354
+ { .name = "STREAMSND", .family = "workers streaming send", .priority = 1000000 },
1355
+ { .name = "DBENGINE", .family = "workers dbengine instances", .priority = 1000000 },
1356
+ { .name = "WEB", .family = "workers web server", .priority = 1000000 },
1357
+ { .name = "ACLKQUERY", .family = "workers aclk query", .priority = 1000000 },
1358
+ { .name = "ACLKSYNC", .family = "workers aclk host sync", .priority = 1000000 },
1359
+ { .name = "PLUGINSD", .family = "workers plugins.d", .priority = 1000000 },
1360
+ { .name = "STATSD", .family = "workers plugin statsd", .priority = 1000000 },
1361
+ { .name = "STATSDFLUSH", .family = "workers plugin statsd flush", .priority = 1000000 },
1362
+ { .name = "PROC", .family = "workers plugin proc", .priority = 1000000 },
1363
+ { .name = "FREEBSD", .family = "workers plugin freebsd", .priority = 1000000 },
1364
+ { .name = "MACOS", .family = "workers plugin macos", .priority = 1000000 },
1365
+ { .name = "CGROUPS", .family = "workers plugin cgroups", .priority = 1000000 },
1366
+ { .name = "CGROUPSDISC", .family = "workers plugin cgroups find", .priority = 1000000 },
1367
+ { .name = "DISKSPACE", .family = "workers plugin diskspace", .priority = 1000000 },
1368
+ { .name = "TC", .family = "workers plugin tc", .priority = 1000000 },
1369
+ { .name = "TIMEX", .family = "workers plugin timex", .priority = 1000000 },
1370
+ { .name = "IDLEJITTER", .family = "workers plugin idlejitter", .priority = 1000000 },
1371
+
1372
+ // has to be terminated with a NULL
1373
+ { .name = NULL, .family = NULL }
1374
+};
1375
+
1376
+static void worker_utilization_charts(void) {
1377
+ static size_t iterations = 0;
1378
+ iterations++;
1379
+
1380
+ int i;
1381
+ for(i = 0; all_workers_utilization[i].name ;i++) {
1382
+ workers_utilization_reset_statistics(&all_workers_utilization[i]);
1383
+ workers_foreach(all_workers_utilization[i].name, worker_utilization_charts_callback, &all_workers_utilization[i]);
1384
+
1385
+ // skip the first iteration, so that we don't accumulate startup utilization to our charts
1386
+ if(likely(iterations > 1))
1387
+ workers_utilization_update_chart(&all_workers_utilization[i]);
1388
+
1389
+ workers_threads_cleanup(&all_workers_utilization[i]);
1390
+ }
1391
+}
1392
+
1393
+static void worker_utilization_finish(void) {
1394
+ int i;
1395
+ for(i = 0; all_workers_utilization[i].name ;i++) {
1396
+ struct worker_utilization *wu = &all_workers_utilization[i];
1397
+
1398
+ if(wu->name_lowercase) {
1399
+ freez(wu->name_lowercase);
1400
+ wu->name_lowercase = NULL;
1401
+ }
1402
+
1403
+ // mark all threads as not enabled
1404
+ struct worker_thread *t;
1405
+ for(t = wu->threads; t ; t = t->next) t->enabled = 0;
1406
+
1407
+ // let the cleanup job free them
1408
+ workers_threads_cleanup(wu);
1409
+ }
1410
+}
1411
+
1412
+// ---------------------------------------------------------------------------------------------------------------------
1413
+
1414
static void global_statistics_cleanup(void *ptr)
1415
{
1416
+ worker_unregister();
1417
+
1418
struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
1419
static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
1420
1421
info("cleaning up...");
1422
1423
+ worker_utilization_finish();
1424
+
1425
static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
1426
}
1427
1428
void *global_statistics_main(void *ptr)
1429
{
1430
+ worker_register("STATS");
1431
+ worker_register_job_name(WORKER_JOB_GLOBAL, "global");
1432
+ worker_register_job_name(WORKER_JOB_REGISTRY, "registry");
1433
+ worker_register_job_name(WORKER_JOB_WORKERS, "workers");
1434
+ worker_register_job_name(WORKER_JOB_DBENGINE, "dbengine");
1435
+
1436
netdata_thread_cleanup_push(global_statistics_cleanup, ptr);
1437
1438
int update_every =
1444
heartbeat_t hb;
1445
heartbeat_init(&hb);
1446
while (!netdata_exit) {
1447
+ worker_is_idle();
1448
heartbeat_next(&hb, step);
1449
1450
+ worker_is_busy(WORKER_JOB_WORKERS);
1451
+ worker_utilization_charts();
1452
+
1453
+ worker_is_busy(WORKER_JOB_GLOBAL);
1454
global_statistics_charts();
1455
+
1456
+ worker_is_busy(WORKER_JOB_REGISTRY);
1457
registry_statistics();
1458
+
1459
+ worker_is_busy(WORKER_JOB_DBENGINE);
1460
+ dbengine_statistics_charts();
1461
+
1462
+ worker_is_busy(WORKER_JOB_HEARTBEAT);
1463
+ update_heartbeat_charts();
1464
}
1465
1466
netdata_thread_cleanup_pop(1);