Improve agent startup (#21260)
* Improve agent startup * Config worker for tier jv2 mrg load * Cleanup unused variables * Mark unused variable * Remove unused variable
Stelios Fragkakis committed
Nov 5, 2025 at 15:33 UTC
71c7e44206ce757a930b17f018a533189d0d3f23
8 files changed
+79
-128
src/daemon/libuv_workers.c
+1
@@ -45,6 +45,7 @@ static void register_libuv_worker_jobs_internal(void) {
45
worker_register_job_name(UV_EVENT_DBENGINE_FLUSH_DIRTY, "dbengine flush dirty");
46
worker_register_job_name(UV_EVENT_DBENGINE_QUIESCE, "dbengine quiesce");
47
worker_register_job_name(UV_EVENT_DBENGINE_SHUTDOWN, "dbengine shutdown");
48
+ worker_register_job_name(UV_EVENT_DBENGINE_MRG_LOAD, "jv2 mrg load");
49
50
// metadata
51
worker_register_job_name(UV_EVENT_HOST_CONTEXT_LOAD, "metadata load host context");
src/daemon/libuv_workers.h
+1
@@ -42,6 +42,7 @@ enum event_loop_job {
42
UV_EVENT_DBENGINE_BUFFERS_CLEANUP,
43
UV_EVENT_DBENGINE_FLUSH_DIRTY,
44
UV_EVENT_DBENGINE_QUIESCE,
45
+ UV_EVENT_DBENGINE_MRG_LOAD,
46
UV_EVENT_DBENGINE_SHUTDOWN,
47
48
// metadata
src/database/engine/journalfile.c
+12
-1
@@ -1008,6 +1008,8 @@ void journalfile_v2_populate_retention_to_mrg(struct rrdengine_instance *ctx, st
1008
time_t global_first_time_s;
1009
bool failed = false;
1010
uint32_t entries;
1011
+ // Calculate number of samples here and update once the file is loaded
1012
+ uint64_t journal_samples = 0;
1013
PROTECTED_ACCESS_SETUP(data_start, journalfile->mmap.size, path_v2, "mrg-load");
1014
if(no_signal_received) {
1015
entries = j2_header->metric_count;
@@ -1020,7 +1022,14 @@ void journalfile_v2_populate_retention_to_mrg(struct rrdengine_instance *ctx, st
1022
time_t end_time_s = header_start_time_s + metric->delta_end_s;
1023
1024
mrg_update_metric_retention_and_granularity_by_uuid(
1023
- main_mrg, (Word_t)ctx, &metric->uuid, start_time_s, end_time_s, metric->update_every_s, now_s);
1025
+ main_mrg,
1026
+ (Word_t)ctx,
1027
+ &metric->uuid,
1028
+ start_time_s,
1029
+ end_time_s,
1030
+ metric->update_every_s,
1031
+ now_s,
1032
+ &journal_samples);
1033
1034
metric++;
1035
}
@@ -1032,6 +1041,8 @@ void journalfile_v2_populate_retention_to_mrg(struct rrdengine_instance *ctx, st
1041
if (unlikely(failed))
1042
return;
1043
1044
+ __atomic_add_fetch(&ctx->atomic.samples, journal_samples, __ATOMIC_RELAXED);
1045
+
1046
usec_t ended_ut = now_monotonic_usec();
1047
1048
nd_log_daemon(NDLP_DEBUG, "DBENGINE: journal v2 of tier %d, datafile %u populated, size: %0.2f MiB, metrics: %0.2f k, %0.2f ms"
src/database/engine/mrg-unittest.c
+2
-7
@@ -38,12 +38,7 @@ static void mrg_stress(void *ptr) {
38
time_t before = __atomic_add_fetch(&e->before, 1, __ATOMIC_RELAXED);
39
40
mrg_update_metric_retention_and_granularity_by_uuid(
41
- mrg, 0x01,
42
- &e->uuid,
43
- after,
44
- before,
45
- 1,
46
- before);
41
+ mrg, 0x01, &e->uuid, after, before, 1, before, NULL);
42
43
__atomic_add_fetch(&t->updates, 1, __ATOMIC_RELAXED);
44
}
@@ -160,7 +155,7 @@ int mrg_unittest(void) {
155
e->after,
156
e->before,
157
1,
163
- e->before);
158
+ e->before, NULL);
159
}
160
}
161
netdata_log_info("stress test ready to run...");
src/database/engine/mrg.c
+12
-6
@@ -382,9 +382,14 @@ ALWAYS_INLINE bool mrg_metric_clear_writer(MRG *mrg, METRIC *metric) {
382
#endif
383
384
inline void mrg_update_metric_retention_and_granularity_by_uuid(
385
- MRG *mrg, Word_t section, nd_uuid_t *uuid,
386
- time_t first_time_s, time_t last_time_s,
387
- uint32_t update_every_s, time_t now_s)
385
+ MRG *mrg,
386
+ Word_t section,
387
+ nd_uuid_t(*uuid),
388
+ time_t first_time_s,
389
+ time_t last_time_s,
390
+ uint32_t update_every_s,
391
+ time_t now_s,
392
+ uint64_t *journal_samples)
393
{
394
if(unlikely(last_time_s > now_s)) {
395
nd_log_limit_static_global_var(erl, 1, 0);
@@ -426,7 +431,6 @@ inline void mrg_update_metric_retention_and_granularity_by_uuid(
431
metric = mrg_metric_add_and_acquire(mrg, entry, &added);
432
}
433
429
- struct rrdengine_instance *ctx = (struct rrdengine_instance *) section;
434
if (likely(!added)) {
435
uint64_t old_samples = 0;
436
@@ -439,13 +443,15 @@ inline void mrg_update_metric_retention_and_granularity_by_uuid(
443
if (update_every_s && metric->latest_update_every_s && metric->latest_time_s_clean)
444
new_samples = (metric->latest_time_s_clean - metric->first_time_s) / metric->latest_update_every_s;
445
442
- __atomic_add_fetch(&ctx->atomic.samples, new_samples - old_samples, __ATOMIC_RELAXED);
446
+ if (journal_samples)
447
+ *journal_samples += (new_samples - old_samples);
448
}
449
else {
450
// Newly added
451
if (update_every_s) {
452
uint64_t samples = (last_time_s - first_time_s) / update_every_s;
448
- __atomic_add_fetch(&ctx->atomic.samples, samples, __ATOMIC_RELAXED);
453
+ if (journal_samples)
454
+ *journal_samples += samples;
455
}
456
}
457
src/database/engine/mrg.h
+8
-3
@@ -85,9 +85,14 @@ void mrg_get_statistics(MRG *mrg, struct mrg_statistics *s);
85
struct aral_statistics *mrg_aral_stats(void);
86
87
void mrg_update_metric_retention_and_granularity_by_uuid(
88
- MRG *mrg, Word_t section, nd_uuid_t *uuid,
89
- time_t first_time_s, time_t last_time_s,
90
- uint32_t update_every_s, time_t now_s);
88
+ MRG *mrg,
89
+ Word_t section,
90
+ nd_uuid_t(*uuid),
91
+ time_t first_time_s,
92
+ time_t last_time_s,
93
+ uint32_t update_every_s,
94
+ time_t now_s,
95
+ uint64_t *journal_samples);
96
97
bool mrg_save(MRG *mrg);
98
bool mrg_load(MRG *mrg);
src/database/engine/rrdengine.c
+42
-111
@@ -1586,44 +1586,42 @@ static void *flush_dirty_pages_of_section_tp_worker(struct rrdengine_instance *c
1586
}
1587
1588
struct mrg_load_thread {
1589
- int max_threads;
1590
- ND_THREAD *thread;
1589
uv_sem_t *sem;
1592
- int tier;
1590
struct rrdengine_datafile *datafile;
1594
- bool busy;
1595
- bool finished;
1591
+ size_t *total;
1592
+ size_t *populated_datafiles;
1593
};
1594
1598
-size_t max_running_threads = 0;
1599
-size_t running_threads = 0;
1600
-
1595
void journalfile_v2_populate_retention_to_mrg_worker(void *arg)
1596
{
1597
struct mrg_load_thread *mlt = arg;
1604
- uv_sem_wait(mlt->sem);
1605
-
1598
struct rrdengine_instance *ctx = mlt->datafile->ctx;
1599
1608
- size_t current_threads = __atomic_add_fetch(&running_threads, 1, __ATOMIC_RELAXED);
1609
- size_t prev_max;
1610
- do {
1611
- prev_max = __atomic_load_n(&max_running_threads, __ATOMIC_RELAXED);
1612
- if (current_threads <= prev_max) {
1613
- break;
1614
- }
1615
- } while (!__atomic_compare_exchange_n(
1616
- &max_running_threads, &prev_max, current_threads, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
1617
-
1600
journalfile_v2_populate_retention_to_mrg(ctx, mlt->datafile->journalfile);
1601
1620
- __atomic_sub_fetch(&running_threads, 1, __ATOMIC_RELAXED);
1602
uv_sem_post(mlt->sem);
1603
+}
1604
+
1605
+static void *tier_mrg_load(
1606
+ struct rrdengine_instance *ctx __maybe_unused,
1607
+ void *data,
1608
+ struct completion *completion __maybe_unused,
1609
+ uv_work_t *req __maybe_unused)
1610
+{
1611
+ worker_is_busy(UV_EVENT_DBENGINE_MRG_LOAD);
1612
+ struct mrg_load_thread *mlt = data;
1613
+ journalfile_v2_populate_retention_to_mrg_worker(mlt);
1614
+ mlt->datafile->populate_mrg.populated = true;
1615
+ spinlock_unlock(&mlt->datafile->populate_mrg.spinlock);
1616
1623
- // Signal completion - this needs to be last
1624
- __atomic_store_n(&mlt->finished, true, __ATOMIC_RELEASE);
1617
+ __atomic_add_fetch(mlt->populated_datafiles, 1, __ATOMIC_RELAXED);
1618
+ __atomic_sub_fetch(mlt->total, 1, __ATOMIC_RELEASE);
1619
+ freez(mlt);
1620
+ worker_is_idle();
1621
+ return NULL;
1622
}
1623
1624
+
1625
static void after_populate_mrg(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1626
if (completion)
1627
completion_mark_complete(completion);
@@ -1638,12 +1636,8 @@ static void *populate_mrg_tp_worker(
1636
worker_is_busy(UV_EVENT_DBENGINE_POPULATE_MRG);
1637
1638
struct mrg_load_thread *mlt = data;
1641
- size_t max_threads = mlt->max_threads;
1639
int tier = ctx->config.tier;
1640
1644
- size_t thread_index = 0;
1645
- int rc;
1646
-
1641
netdata_rwlock_rdlock(&ctx->datafiles.rwlock);
1642
1643
size_t total_datafiles = 0;
@@ -1662,6 +1656,7 @@ static void *populate_mrg_tp_worker(
1656
return data;
1657
}
1658
1659
+ size_t total = 0;
1660
do {
1661
struct rrdengine_datafile *datafile = NULL;
1662
@@ -1689,96 +1684,29 @@ static void *populate_mrg_tp_worker(
1684
if(!datafile)
1685
break;
1686
1692
- // Datafile populate mrg spinlock is acquired
1693
- // Find an available thread slot or join finished threads
1694
- bool thread_slot_found = false;
1695
-
1696
- while (!thread_slot_found) {
1697
- // First, check for any finished threads to clean up
1698
- for (size_t index = 0; index < max_threads; index++) {
1699
- if (__atomic_load_n(&mlt[index].finished, __ATOMIC_RELAXED) &&
1700
- __atomic_load_n(&mlt[index].tier, __ATOMIC_ACQUIRE) == tier) {
1701
-
1702
- rc = nd_thread_join(mlt[index].thread);
1703
- if (rc)
1704
- nd_log_daemon(NDLP_WARNING, "Failed to join thread, rc = %d", rc);
1705
-
1706
- __atomic_store_n(&mlt[index].busy, false, __ATOMIC_RELEASE);
1707
- __atomic_store_n(&mlt[index].finished, false, __ATOMIC_RELEASE);
1708
- mlt[index].datafile->populate_mrg.populated = true;
1709
- populated_datafiles++;
1710
- spinlock_unlock(&mlt[index].datafile->populate_mrg.spinlock);
1711
-
1712
- // We've cleaned up a thread slot, but we'll still look for a free one
1713
- }
1714
- }
1715
-
1716
- // Look for a free thread slot
1717
- for (size_t index = 0; index < max_threads; index++) {
1718
- bool expected = false;
1719
- if (__atomic_compare_exchange_n(&(mlt[index].busy), &expected, true, false,
1720
- __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
1721
- thread_index = index;
1722
- thread_slot_found = true;
1723
- break;
1724
- }
1725
- }
1726
-
1727
- if (!thread_slot_found) {
1728
- // If we couldn't find a free slot after cleanup, wait a bit and try again
1729
- sleep_usec(10 * USEC_PER_MS);
1730
- }
1731
- }
1732
-
1733
- // We have a thread slot (thread_index) and a datafile to process
1734
- __atomic_store_n(&mlt[thread_index].tier, tier, __ATOMIC_RELAXED);
1735
- mlt[thread_index].datafile = datafile;
1736
-
1737
- mlt[thread_index].thread = nd_thread_create("MRGLOAD", NETDATA_THREAD_OPTION_DEFAULT, journalfile_v2_populate_retention_to_mrg_worker,
1738
- &mlt[thread_index]);
1739
-
1740
- if (!mlt[thread_index].thread) {
1741
- nd_log_daemon(NDLP_WARNING, "Failed to create thread for MRG population");
1742
- __atomic_store_n(&mlt[thread_index].busy, false, __ATOMIC_RELEASE);
1743
- spinlock_unlock(&datafile->populate_mrg.spinlock);
1744
- }
1687
+ uv_sem_wait(mlt->sem);
1688
+ struct mrg_load_thread *local_mlt = callocz(1, sizeof(struct mrg_load_thread));
1689
+ local_mlt->datafile = datafile;
1690
+ local_mlt->sem = mlt->sem;
1691
+ local_mlt->total = &total;
1692
+ local_mlt->populated_datafiles = &populated_datafiles;
1693
+ __atomic_add_fetch(local_mlt->total, 1, __ATOMIC_RELAXED);
1694
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_MRG_LOAD, local_mlt, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1695
nd_log_limit_static_thread_var(erl, 10, 0);
1696
nd_log_limit(&erl, NDLS_DAEMON, NDLP_INFO, "DBENGINE: Tier %d MRG population completed: %.2f%% (%zu/%zu)", tier, (populated_datafiles * 100.0) / total_datafiles,
1697
populated_datafiles, total_datafiles);
1698
} while(1);
1699
1700
// We've processed all datafiles. Now wait for all our threads to complete
1751
- bool threads_still_running;
1701
+ size_t pending;
1702
do {
1753
- threads_still_running = false;
1754
-
1755
- for (size_t index = 0; index < max_threads; index++) {
1756
- if (__atomic_load_n(&mlt[index].busy, __ATOMIC_ACQUIRE) &&
1757
- __atomic_load_n(&mlt[index].tier, __ATOMIC_ACQUIRE) == tier) {
1758
-
1759
- if (__atomic_load_n(&mlt[index].finished, __ATOMIC_RELAXED)) {
1760
- // Thread is finished, join it
1761
- rc = nd_thread_join((mlt[index].thread));
1762
- if (rc)
1763
- nd_log_daemon(NDLP_WARNING, "Failed to join thread, rc = %d", rc);
1764
-
1765
- __atomic_store_n(&mlt[index].busy, false, __ATOMIC_RELEASE);
1766
- __atomic_store_n(&mlt[index].finished, false, __ATOMIC_RELEASE);
1767
- mlt[index].datafile->populate_mrg.populated = true;
1768
- spinlock_unlock(&mlt[index].datafile->populate_mrg.spinlock);
1769
- } else {
1770
- // Thread is still running
1771
- threads_still_running = true;
1772
- }
1773
- }
1774
- }
1775
-
1776
- if (threads_still_running) {
1777
- // Wait a bit before checking again
1703
+ pending = __atomic_load_n(&total, __ATOMIC_ACQUIRE);
1704
+ if (pending) {
1705
+ nd_log_limit_static_thread_var(erl, 10, 0);
1706
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_INFO, "DBENGINE: Tier %d Waiting for %zu threads", tier, total);
1707
sleep_usec(10 * USEC_PER_MS);
1708
}
1780
-
1781
- } while (threads_still_running);
1709
+ } while (pending > 0);
1710
1711
worker_is_idle();
1712
return data;
@@ -2392,6 +2320,7 @@ void dbengine_event_loop(void* arg) {
2320
worker_register_job_name(RRDENG_OPCODE_CTX_QUIESCE, "ctx quiesce");
2321
worker_register_job_name(RRDENG_OPCODE_SHUTDOWN_EVLOOP, "dbengine shutdown");
2322
worker_register_job_name(RRDENG_OPCODE_PARALLEL_WEIGHT, "parallel weight");
2323
+ worker_register_job_name(RRDENG_OPCODE_MRG_LOAD, "mrg tier load");
2324
2325
2326
worker_register_job_name(RRDENG_OPCODE_MAX, "get opcode");
@@ -2407,6 +2336,7 @@ void dbengine_event_loop(void* arg) {
2336
worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_CTX_FLUSH_DIRTY, "ctx flush dirty cb");
2337
worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_CTX_QUIESCE, "ctx quiesce cb");
2338
worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_PARALLEL_WEIGHT, "parallel weight cb");
2339
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_MRG_LOAD, "mrg tier load cb");
2340
2341
// special jobs
2342
worker_register_job_name(RRDENG_RETENTION_TIMER_CB, "retention timer");
@@ -2431,9 +2361,6 @@ void dbengine_event_loop(void* arg) {
2361
struct mrg_load_thread *mlt = callocz(cpus, sizeof(*mlt));
2362
for (size_t i = 0; i < cpus; i++) {
2363
mlt[i].sem = &sem;
2434
- mlt[i].max_threads = cpus;
2435
- mlt[i].busy = false;
2436
- mlt[i].finished = false;
2364
}
2365
2366
#if defined(OS_WINDOWS)
@@ -2452,6 +2379,10 @@ void dbengine_event_loop(void* arg) {
2379
worker_is_busy(opcode);
2380
2381
switch (opcode) {
2382
+ case RRDENG_OPCODE_MRG_LOAD:
2383
+ work_dispatch(NULL, cmd.data, cmd.completion, cmd.opcode, tier_mrg_load, NULL);
2384
+ break;
2385
+
2386
case RRDENG_OPCODE_PARALLEL_WEIGHT:;
2387
2388
work_dispatch(NULL, cmd.data, cmd.completion, cmd.opcode, weights_worker, after_weights_worker);
src/database/engine/rrdengine.h
+1
@@ -284,6 +284,7 @@ enum rrdeng_opcode {
284
RRDENG_OPCODE_CTX_POPULATE_MRG,
285
RRDENG_OPCODE_SHUTDOWN_EVLOOP,
286
RRDENG_OPCODE_PARALLEL_WEIGHT,
287
+ RRDENG_OPCODE_MRG_LOAD,
288
RRDENG_OPCODE_CLEANUP,
289
290
RRDENG_OPCODE_MAX