Rework datafiles (#20581)
* Datafiles in judyL * Use CLOSE_FILE macro for clarity * Fix compilation error
Stelios Fragkakis committed
Jul 7, 2025 at 21:28 UTC
a2e24cd939ff34b172b292859606382d179d0d48
6 files changed
+189
-109
src/database/engine/datafile.c
+33
-55
@@ -6,7 +6,12 @@ void datafile_list_insert(struct rrdengine_instance *ctx, struct rrdengine_dataf
6
if(!having_lock)
7
uv_rwlock_wrlock(&ctx->datafiles.rwlock);
8
9
- DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(ctx->datafiles.first, datafile, prev, next);
9
+ Pvoid_t *Pvalue = JudyLIns(&ctx->datafiles.JudyL, (Word_t ) datafile->fileno, PJE0);
10
+ if(!Pvalue || Pvalue == PJERR)
11
+ fatal("DBENGINE: cannot insert datafile %u of tier %d into the datafiles list",
12
+ datafile->fileno, ctx->config.tier);
13
+
14
+ *Pvalue = datafile;
15
16
if(!having_lock)
17
uv_rwlock_wrunlock(&ctx->datafiles.rwlock);
@@ -14,7 +19,7 @@ void datafile_list_insert(struct rrdengine_instance *ctx, struct rrdengine_dataf
19
20
void datafile_list_delete_unsafe(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile)
21
{
17
- DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(ctx->datafiles.first, datafile, prev, next);
22
+ (void) JudyLDel(&ctx->datafiles.JudyL, (Word_t)datafile->fileno, PJE0);
23
}
24
25
@@ -174,19 +179,10 @@ void generate_datafilepath(struct rrdengine_datafile *datafile, char *str, size_
179
int close_data_file(struct rrdengine_datafile *datafile)
180
{
181
struct rrdengine_instance *ctx = datafile_ctx(datafile);
177
- uv_fs_t req;
182
int ret;
183
char path[RRDENG_PATH_MAX];
180
-
184
generate_datafilepath(datafile, path, sizeof(path));
182
-
183
- ret = uv_fs_close(NULL, &req, datafile->file, NULL);
184
- if (ret < 0) {
185
- netdata_log_error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
186
- ctx_fs_error(ctx);
187
- }
188
- uv_fs_req_cleanup(&req);
189
-
185
+ CLOSE_FILE(ctx, path, datafile->file, ret);
186
return ret;
187
}
188
@@ -208,34 +204,13 @@ int unlink_data_file(struct rrdengine_datafile *datafile)
204
int destroy_data_file_unsafe(struct rrdengine_datafile *datafile)
205
{
206
struct rrdengine_instance *ctx = datafile_ctx(datafile);
211
- uv_fs_t req;
207
int ret;
208
char path[RRDENG_PATH_MAX];
209
210
generate_datafilepath(datafile, path, sizeof(path));
211
217
- ret = uv_fs_ftruncate(NULL, &req, datafile->file, 0, NULL);
218
- if (ret < 0) {
219
- netdata_log_error("DBENGINE: uv_fs_ftruncate(%s): %s", path, uv_strerror(ret));
220
- ctx_fs_error(ctx);
221
- }
222
- uv_fs_req_cleanup(&req);
223
-
224
- ret = uv_fs_close(NULL, &req, datafile->file, NULL);
225
- if (ret < 0) {
226
- netdata_log_error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
227
- ctx_fs_error(ctx);
228
- }
229
- uv_fs_req_cleanup(&req);
230
-
231
- ret = uv_fs_unlink(NULL, &req, path, NULL);
232
- if (ret < 0) {
233
- netdata_log_error("DBENGINE: uv_fs_fsunlink(%s): %s", path, uv_strerror(ret));
234
- ctx_fs_error(ctx);
235
- }
236
- uv_fs_req_cleanup(&req);
237
-
238
- __atomic_add_fetch(&ctx->stats.datafile_deletions, 1, __ATOMIC_RELAXED);
212
+ CLOSE_FILE(ctx, path, datafile->file, ret);
213
+ ret = unlink_data_file(datafile);
214
215
return ret;
216
}
@@ -280,7 +255,7 @@ int create_data_file(struct rrdengine_datafile *datafile)
255
256
posix_memalign_freez(superblock);
257
if (ret < 0) {
283
- destroy_data_file_unsafe(datafile);
258
+ (void) destroy_data_file_unsafe(datafile);
259
ctx_io_error(ctx);
260
nd_log_limit_static_global_var(dbengine_erl, 10, 0);
261
nd_log_limit(&dbengine_erl, NDLS_DAEMON, NDLP_ERR, "DBENGINE: Failed to create datafile %s", path);
@@ -329,7 +304,6 @@ static int check_data_file_superblock(uv_file file)
304
static int load_data_file(struct rrdengine_datafile *datafile)
305
{
306
struct rrdengine_instance *ctx = datafile_ctx(datafile);
332
- uv_fs_t req;
307
uv_file file;
308
int ret, fd, error;
309
uint64_t file_size;
@@ -346,12 +320,12 @@ static int load_data_file(struct rrdengine_datafile *datafile)
320
321
ret = check_file_properties(file, &file_size, sizeof(struct rrdeng_df_sb));
322
if (ret)
349
- goto error;
323
+ goto err_exit;
324
file_size = ALIGN_BYTES_CEILING(file_size);
325
326
ret = check_data_file_superblock(file);
327
if (ret)
354
- goto error;
328
+ goto err_exit;
329
330
ctx_io_read_op_bytes(ctx, sizeof(struct rrdeng_df_sb));
331
@@ -362,14 +336,9 @@ static int load_data_file(struct rrdengine_datafile *datafile)
336
337
return 0;
338
365
- error:
339
+err_exit:
340
error = ret;
367
- ret = uv_fs_close(NULL, &req, file, NULL);
368
- if (ret < 0) {
369
- netdata_log_error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
370
- ctx_fs_error(ctx);
371
- }
372
- uv_fs_req_cleanup(&req);
341
+ CLOSE_FILE(ctx, path, file, ret);
342
return error;
343
}
344
@@ -593,7 +562,7 @@ int create_new_datafile_pair(struct rrdengine_instance *ctx, bool having_lock)
562
return 0;
563
564
error_after_journalfile:
596
- destroy_data_file_unsafe(datafile);
565
+ (void) destroy_data_file_unsafe(datafile);
566
freez(journalfile);
567
568
error_after_datafile:
@@ -625,8 +594,14 @@ int init_data_files(struct rrdengine_instance *ctx)
594
if (ctx->loading.create_new_datafile_pair)
595
create_new_datafile_pair(ctx, false);
596
628
- while(rrdeng_ctx_tier_cap_exceeded(ctx))
629
- datafile_delete(ctx, ctx->datafiles.first, false, true, false);
597
+ while(rrdeng_ctx_tier_cap_exceeded(ctx)) {
598
+ Word_t Index = 0;
599
+ Pvoid_t *PValue = JudyLFirst(ctx->datafiles.JudyL, &Index, PJE0);
600
+ if (PValue && *PValue) {
601
+ struct rrdengine_datafile *datafile = *PValue;
602
+ datafile_delete(ctx, datafile, false, true, false);
603
+ }
604
+ }
605
}
606
607
pgc_reset_hot_max(open_cache);
@@ -654,7 +629,7 @@ void finalize_data_files(struct rrdengine_instance *ctx)
629
{
630
bool logged = false;
631
657
- if (!ctx->datafiles.first)
632
+ if (!ctx->datafiles.JudyL)
633
return;
634
635
while(__atomic_load_n(&ctx->atomic.extents_currently_being_flushed, __ATOMIC_RELAXED)) {
@@ -665,13 +640,17 @@ void finalize_data_files(struct rrdengine_instance *ctx)
640
sleep_usec(100 * USEC_PER_MS);
641
}
642
668
- do {
669
- struct rrdengine_datafile *datafile = ctx->datafiles.first;
643
+ bool first_then_next = true;
644
+ Pvoid_t *PValue;
645
+ Word_t Index = 0;
646
+
647
+ while ((PValue = JudyLFirstThenNext(ctx->datafiles.JudyL, &Index, &first_then_next))) {
648
+ struct rrdengine_datafile *datafile = *PValue;
649
struct rrdengine_journalfile *journalfile = datafile->journalfile;
650
651
logged = false;
652
size_t iterations = 10;
674
- while(!datafile_acquire_for_deletion(datafile, true) && datafile != ctx->datafiles.first->prev && --iterations > 0) {
653
+ while(!datafile_acquire_for_deletion(datafile, true) && --iterations > 0) {
654
if(!logged) {
655
netdata_log_info("Waiting to acquire data file %u of tier %d to close it...", datafile->fileno, ctx->config.tier);
656
logged = true;
@@ -711,6 +690,5 @@ void finalize_data_files(struct rrdengine_instance *ctx)
690
691
freez(journalfile);
692
freez(datafile);
714
-
715
- } while(ctx->datafiles.first);
693
+ }
694
}
src/database/engine/datafile.h
-2
@@ -59,8 +59,6 @@ struct rrdengine_datafile {
59
uv_rwlock_t extent_rwlock;
60
struct rrdengine_instance *ctx;
61
struct rrdengine_journalfile *journalfile;
62
- struct rrdengine_datafile *prev;
63
- struct rrdengine_datafile *next;
62
63
struct {
64
SPINLOCK spinlock;
src/database/engine/journalfile.c
+15
-16
@@ -296,7 +296,16 @@ void journalfile_v2_data_unmount_cleanup(time_t now_s) {
296
if(uv_rwlock_tryrdlock(&ctx->datafiles.rwlock) != 0)
297
continue;
298
299
- for (datafile = ctx->datafiles.first; datafile; datafile = datafile->next) {
299
+ bool first_then_next = true;
300
+ Pvoid_t *Pvalue = NULL;
301
+ Word_t Index = 0;
302
+
303
+ while((Pvalue = JudyLFirstThenNext(ctx->datafiles.JudyL, &Index, &first_then_next))) {
304
+
305
+ datafile = *Pvalue;
306
+ if (!datafile)
307
+ continue;
308
+
309
struct rrdengine_journalfile *journalfile = datafile->journalfile;
310
311
if(!spinlock_trylock(&journalfile->data_spinlock))
@@ -477,14 +486,10 @@ static int close_uv_file(struct rrdengine_datafile *datafile, uv_file file)
486
int ret;
487
char path[RRDENG_PATH_MAX];
488
480
- uv_fs_t req;
481
- ret = uv_fs_close(NULL, &req, file, NULL);
482
- if (ret < 0) {
483
- journalfile_v1_generate_path(datafile, path, sizeof(path));
484
- netdata_log_error("DBENGINE: uv_fs_close(\"%s\"): %s", path, uv_strerror(ret));
485
- ctx_fs_error(datafile_ctx(datafile));
486
- }
487
- uv_fs_req_cleanup(&req);
489
+ struct rrdengine_instance *ctx = datafile_ctx(datafile);
490
+ journalfile_v1_generate_path(datafile, path, sizeof(path));
491
+
492
+ CLOSE_FILE(ctx, path, file, ret);
493
return ret;
494
}
495
@@ -1524,7 +1529,6 @@ bool journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1529
int journalfile_load(struct rrdengine_instance *ctx, struct rrdengine_journalfile *journalfile,
1530
struct rrdengine_datafile *datafile)
1531
{
1527
- uv_fs_t req;
1532
uv_file file;
1533
int ret, fd, error;
1534
uint64_t file_size, max_id;
@@ -1600,11 +1604,6 @@ int journalfile_load(struct rrdengine_instance *ctx, struct rrdengine_journalfil
1604
return 0;
1605
1606
cleanup:
1603
- ret = uv_fs_close(NULL, &req, file, NULL);
1604
- if (ret < 0) {
1605
- netdata_log_error("DBENGINE: uv_fs_close(\"%s\"): %s", path, uv_strerror(ret));
1606
- ctx_fs_error(ctx);
1607
- }
1608
- uv_fs_req_cleanup(&req);
1607
+ CLOSE_FILE(ctx, path, file, ret);
1608
return error;
1609
}
src/database/engine/rrdengine.c
+118
-28
@@ -698,12 +698,89 @@ static bool datafile_is_full(struct rrdengine_instance *ctx, struct rrdengine_da
698
return ret;
699
}
700
701
+size_t datafile_count(struct rrdengine_instance *ctx, bool with_lock)
702
+{
703
+ size_t count = 0;
704
+
705
+ if (!with_lock)
706
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
707
+
708
+ count = JudyLCount(ctx->datafiles.JudyL, 0, -1, PJE0);
709
+
710
+ if (!with_lock)
711
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
712
+
713
+ return count;
714
+}
715
+
716
+struct rrdengine_datafile *
717
+get_next_datafile(struct rrdengine_datafile *this_datafile, struct rrdengine_instance *ctx, bool with_lock)
718
+{
719
+ struct rrdengine_datafile *datafile = NULL;
720
+
721
+ ctx = this_datafile ? this_datafile->ctx : ctx;
722
+ if (!ctx)
723
+ return NULL;
724
+
725
+ if (!with_lock)
726
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
727
+
728
+ Word_t Index = this_datafile ? this_datafile->fileno : 0;
729
+ Pvoid_t *Pvalue;
730
+
731
+ Pvalue = JudyLNext(ctx->datafiles.JudyL, &Index, PJE0);
732
+
733
+ if (Pvalue)
734
+ datafile = *Pvalue;
735
+
736
+ if (!with_lock)
737
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
738
+
739
+ return datafile;
740
+}
741
+
742
+static struct rrdengine_datafile *get_ctx_datafile_first_or_last(struct rrdengine_instance *ctx, bool first, bool with_lock)
743
+{
744
+ struct rrdengine_datafile *datafile = NULL;
745
+
746
+ if (!with_lock)
747
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
748
+
749
+ Word_t Index = 0;
750
+ Pvoid_t *Pvalue;
751
+
752
+ if (first)
753
+ Pvalue = JudyLFirst(ctx->datafiles.JudyL, &Index, PJE0);
754
+ else {
755
+ Index = -1;
756
+ Pvalue = JudyLLast(ctx->datafiles.JudyL, &Index, PJE0);
757
+ }
758
+
759
+ if (Pvalue)
760
+ datafile = *Pvalue;
761
+
762
+ if (!with_lock)
763
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
764
+
765
+ return datafile;
766
+}
767
+
768
+struct rrdengine_datafile *get_first_ctx_datafile(struct rrdengine_instance *ctx, bool with_lock) {
769
+ return get_ctx_datafile_first_or_last(ctx, true, with_lock);
770
+}
771
+
772
+struct rrdengine_datafile *get_last_ctx_datafile(struct rrdengine_instance *ctx, bool with_lock) {
773
+ return get_ctx_datafile_first_or_last(ctx, false, with_lock);
774
+}
775
+
776
+
777
static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_instance *ctx) {
778
struct rrdengine_datafile *datafile;
779
780
// get the latest datafile
781
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
706
- datafile = ctx->datafiles.first->prev;
782
+
783
+ datafile = get_last_ctx_datafile(ctx, true);
784
// become a writer on this datafile, to prevent it from vanishing
785
spinlock_lock(&datafile->writers.spinlock);
786
datafile->writers.running++;
@@ -719,9 +796,7 @@ static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_
796
netdata_mutex_lock(&mutex);
797
798
// take the latest datafile again - without this, multiple threads may create multiple files
722
- uv_rwlock_rdlock(&ctx->datafiles.rwlock);
723
- datafile = ctx->datafiles.first->prev;
724
- uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
799
+ datafile = get_last_ctx_datafile(ctx, false);
800
801
if(datafile_is_full(ctx, datafile) && create_new_datafile_pair(ctx, true) == 0)
802
__atomic_store_n(&ctx->atomic.needs_indexing, true, __ATOMIC_RELAXED);
@@ -730,7 +805,7 @@ static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_
805
806
// get the new latest datafile again, like above
807
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
733
- datafile = ctx->datafiles.first->prev;
808
+ datafile = get_last_ctx_datafile(ctx, true);
809
// become a writer on this datafile, to prevent it from vanishing
810
spinlock_lock(&datafile->writers.spinlock);
811
datafile->writers.running++;
@@ -958,10 +1033,10 @@ struct rrdengine_datafile *datafile_release_and_acquire_next_for_retention(struc
1033
1034
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1035
961
- struct rrdengine_datafile *next_datafile = datafile->next;
1036
+ struct rrdengine_datafile *next_datafile = get_next_datafile(datafile, NULL, true);
1037
1038
while(next_datafile && !datafile_acquire(next_datafile, DATAFILE_ACQUIRE_RETENTION))
964
- next_datafile = next_datafile->next;
1039
+ next_datafile = get_next_datafile(next_datafile, NULL, true);
1040
1041
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1042
@@ -981,7 +1056,8 @@ static time_t find_uuid_first_time(
1056
// acquire the datafile to work with it
1057
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1058
while(datafile && !datafile_acquire(datafile, DATAFILE_ACQUIRE_RETENTION))
984
- datafile = datafile->next;
1059
+ datafile = get_next_datafile(datafile, NULL, true);
1060
+
1061
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1062
1063
if (unlikely(!datafile))
@@ -1313,7 +1389,7 @@ void datafile_delete(
1389
DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1390
"' to be available for deletion, "
1391
"it is in use currently by %u users.",
1316
- ctx->config.dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno, datafile->users.lockers);
1392
+ ctx->config.dbfiles_path, datafile->tier, datafile->fileno, datafile->users.lockers);
1393
1394
__atomic_add_fetch(&rrdeng_cache_efficiency_stats.datafile_deletion_spin, 1, __ATOMIC_RELAXED);
1395
sleep_usec(1 * USEC_PER_SEC);
@@ -1323,10 +1399,10 @@ void datafile_delete(
1399
netdata_log_info("DBENGINE: acquired data file \"%s/"
1400
DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1401
"\" for deletion.",
1326
- ctx->config.dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1402
+ ctx->config.dbfiles_path, datafile->tier, datafile->fileno);
1403
1404
if (update_retention)
1329
- update_metrics_first_time_s(ctx, datafile, datafile->next, worker);
1405
+ update_metrics_first_time_s(ctx, datafile, get_next_datafile(datafile, NULL, true), worker);
1406
1407
// if (!ctx_is_available_for_queries(ctx)) {
1408
// // agent is shutting down, we cannot continue
@@ -1339,7 +1415,7 @@ void datafile_delete(
1415
netdata_log_info("DBENGINE: deleting data file \"%s/"
1416
DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1417
"\".",
1342
- ctx->config.dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1418
+ ctx->config.dbfiles_path, datafile->tier, datafile->fileno);
1419
1420
if(worker)
1421
worker_is_busy(UV_EVENT_DBENGINE_DATAFILE_DELETE);
@@ -1391,7 +1467,9 @@ void datafile_delete(
1467
}
1468
1469
static void *database_rotate_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1394
- datafile_delete(ctx, ctx->datafiles.first, ctx_is_available_for_queries(ctx), true, true);
1470
+
1471
+ struct rrdengine_datafile *datafile = get_first_ctx_datafile(ctx, false);
1472
+ datafile_delete(ctx, datafile, ctx_is_available_for_queries(ctx), true, true);
1473
1474
rrdcontext_db_rotation();
1475
@@ -1487,9 +1565,11 @@ static void *populate_mrg_tp_worker(
1565
int rc;
1566
1567
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1568
+
1569
size_t total_datafiles = 0;
1570
size_t populated_datafiles = 0;
1492
- for (struct rrdengine_datafile *df = ctx->datafiles.first; df; df = df->next) {
1571
+ struct rrdengine_datafile *df = NULL;
1572
+ while ((df = get_next_datafile(df, ctx, true))) {
1573
total_datafiles++;
1574
if (df->populate_mrg.populated)
1575
populated_datafiles++;
@@ -1507,16 +1587,21 @@ static void *populate_mrg_tp_worker(
1587
1588
// find a datafile to work on
1589
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1510
- for(datafile = ctx->datafiles.first; datafile; datafile = datafile->next) {
1511
- if(!spinlock_trylock(&datafile->populate_mrg.spinlock))
1590
+ bool first_then_next = true;
1591
+ Pvoid_t *Pvalue = NULL;
1592
+ Word_t Index = 0;
1593
+ while((Pvalue = JudyLFirstThenNext(ctx->datafiles.JudyL, &Index, &first_then_next))) {
1594
+ datafile = *Pvalue;
1595
+ if(!spinlock_trylock(&datafile->populate_mrg.spinlock)) {
1596
+ datafile = NULL;
1597
continue;
1598
+ }
1599
1600
if(datafile->populate_mrg.populated) {
1601
spinlock_unlock(&datafile->populate_mrg.spinlock);
1602
+ datafile = NULL;
1603
continue;
1604
}
1518
-
1519
- // we have the spinlock and it is not populated
1605
break;
1606
}
1607
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
@@ -1704,7 +1789,7 @@ time_t get_datafile_end_time(struct rrdengine_instance *ctx)
1789
time_t last_time_s = 0;
1790
1791
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1707
- struct rrdengine_datafile *datafile = ctx->datafiles.first;
1792
+ struct rrdengine_datafile *datafile = get_last_ctx_datafile(ctx, true);
1793
1794
if (datafile) {
1795
last_time_s = datafile->journalfile->v2.last_time_s;
@@ -1770,15 +1855,15 @@ static struct rrdengine_datafile *release_and_aquire_next_datafile_for_indexing(
1855
1856
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1857
if (release_datafile) {
1773
- datafile = release_datafile->next;
1858
+ datafile = get_next_datafile(release_datafile, NULL, true);
1859
datafile_release(release_datafile, DATAFILE_ACQUIRE_INDEXING);
1860
}
1861
else
1777
- datafile = ctx->datafiles.first;
1862
+ datafile = get_first_ctx_datafile(ctx, true);
1863
1864
while (datafile && datafile->fileno != ctx_last_fileno_get(ctx) && datafile->fileno != ctx_last_flush_fileno_get(ctx)) {
1865
if(journalfile_v2_data_available(datafile->journalfile)) {
1781
- datafile = datafile->next;
1866
+ datafile = get_next_datafile(datafile, NULL, true);
1867
continue;
1868
}
1869
@@ -1795,7 +1880,7 @@ static struct rrdengine_datafile *release_and_aquire_next_datafile_for_indexing(
1880
return datafile;
1881
}
1882
nd_log_daemon(NDLP_INFO, "DBENGINE: Datafile %u CANNOT be locked for indexing after retries; skipping", datafile->fileno);
1798
- datafile = datafile->next;
1883
+ datafile = get_next_datafile(datafile, NULL, true);
1884
}
1885
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1886
return NULL;
@@ -1942,8 +2027,11 @@ uint64_t rrdeng_get_used_disk_space(struct rrdengine_instance *ctx, bool having_
2027
if (!having_lock)
2028
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
2029
1945
- if (ctx->datafiles.first && ctx->datafiles.first->prev)
1946
- active_space = ctx->datafiles.first->prev->pos;
2030
+ struct rrdengine_datafile *first_datafile = get_first_ctx_datafile(ctx, true);
2031
+ struct rrdengine_datafile *last_datafile = get_last_ctx_datafile(ctx, true);
2032
+
2033
+ if (first_datafile && last_datafile)
2034
+ active_space = last_datafile->pos;
2035
2036
if (!having_lock)
2037
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
@@ -1978,7 +2066,9 @@ static time_t get_tier_retention(struct rrdengine_instance *ctx)
2066
bool rrdeng_ctx_tier_cap_exceeded(struct rrdengine_instance *ctx)
2067
{
2068
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1981
- if (!ctx->datafiles.first || !ctx->datafiles.first->next) {
2069
+ struct rrdengine_datafile *first_datafile = get_first_ctx_datafile(ctx, true);
2070
+
2071
+ if (!first_datafile || datafile_count(ctx, true) < 2) {
2072
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
2073
return false;
2074
}
@@ -2337,8 +2427,8 @@ void *dbengine_event_loop(void* arg) {
2427
case RRDENG_OPCODE_DATABASE_ROTATE: {
2428
struct rrdengine_instance *ctx = cmd.ctx;
2429
ctx->datafiles.pending_rotate = false;
2340
- if (NOT_DELETING_FILES(ctx) && ctx->datafiles.first->next != NULL &&
2341
- ctx->datafiles.first->next->next != NULL && rrdeng_ctx_tier_cap_exceeded(ctx)) {
2430
+ if (NOT_DELETING_FILES(ctx) && datafile_count(ctx, false) > 2 &&
2431
+ rrdeng_ctx_tier_cap_exceeded(ctx)) {
2432
__atomic_store_n(&ctx->atomic.now_deleting_files, true, __ATOMIC_RELAXED);
2433
work_dispatch(ctx, NULL, NULL, opcode, database_rotate_tp_worker, after_database_rotate);
2434
}
src/database/engine/rrdengine.h
+19
-2
@@ -35,6 +35,17 @@ extern unsigned rrdeng_pages_per_extent;
35
uv_fs_req_cleanup(&(_req)); \
36
} while (0)
37
38
+#define CLOSE_FILE(ctx, path, file, ret_var) \
39
+ do { \
40
+ uv_fs_t _req; \
41
+ (ret_var) = uv_fs_close(NULL, &(_req), (file), NULL); \
42
+ if ((ret_var) < 0) { \
43
+ netdata_log_error("DBENGINE: uv_fs_close(\"%s\"): %s", (path), uv_strerror(ret_var)); \
44
+ ctx_fs_error(ctx); \
45
+ } \
46
+ uv_fs_req_cleanup(&(_req)); \
47
+ } while (0)
48
+
49
/* Forward declarations */
50
struct rrdengine_instance;
51
struct rrdeng_cmd;
@@ -386,11 +397,11 @@ struct rrdengine_instance {
397
TIER_CONFIG_PROTOTYPE config;
398
399
struct {
389
- uv_rwlock_t rwlock; // the linked list of datafiles is protected by this lock
400
+ uv_rwlock_t rwlock; // the JudyL of datafiles is protected by this lock
401
bool disk_time; // true: delete for disk quota, false: delete for retention
402
bool pending_rotate; // Change from event loop
403
bool pending_index; // Change from event loop
393
- struct rrdengine_datafile *first; // oldest - the newest with ->first->prev
404
+ Pvoid_t JudyL; // the datafiles, indexed by fileno
405
} datafiles;
406
407
struct {
@@ -570,5 +581,11 @@ uint64_t rrdeng_get_used_disk_space(struct rrdengine_instance *ctx, bool having_
581
void rrdeng_calculate_tier_disk_space_percentage(void);
582
uint64_t rrdeng_get_directory_free_bytes_space(struct rrdengine_instance *ctx);
583
void dbengine_shutdown();
584
+size_t datafile_count(struct rrdengine_instance *ctx, bool with_lock);
585
+struct rrdengine_datafile *get_first_ctx_datafile(struct rrdengine_instance *ctx, bool with_lock);
586
+struct rrdengine_datafile *get_last_ctx_datafile(struct rrdengine_instance *ctx, bool with_lock);
587
+struct rrdengine_datafile *
588
+get_next_datafile(struct rrdengine_datafile *this_datafile, struct rrdengine_instance *ctx, bool with_lock);
589
+
590
591
#endif /* NETDATA_RRDENGINE_H */
src/database/engine/rrdengineapi.c
+4
-6
@@ -1108,11 +1108,7 @@ void rrdeng_get_37_statistics(struct rrdengine_instance *ctx, unsigned long long
1108
1109
static void rrdeng_populate_mrg(struct rrdengine_instance *ctx)
1110
{
1111
- uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1112
- size_t datafiles = 0;
1113
- for(struct rrdengine_datafile *df = ctx->datafiles.first; df ;df = df->next)
1114
- datafiles++;
1115
- uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1111
+ size_t datafiles = datafile_count(ctx, false);
1112
1113
ssize_t cpus = (ssize_t)netdata_conf_cpus();
1114
if(cpus < 1)
@@ -1353,7 +1349,9 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) {
1349
RRDENG_SIZE_STATS stats = { 0 };
1350
1351
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1356
- for(struct rrdengine_datafile *df = ctx->datafiles.first; df ;df = df->next) {
1352
+ struct rrdengine_datafile *df = NULL;
1353
+
1354
+ while ((df = get_next_datafile(df, ctx, true))) {
1355
stats.datafiles++;
1356
populate_v2_statistics(df, &stats);
1357
}