754
755
// ----------------------------------------------------------------------------
756
757
+#define MAX_PAGE_SIZES_TO_KEEP 3
758
+#define MIN_PAGES_PER_SIZE_TO_KEEP 100
759
+
760
+struct dbengine_page_size {
761
+ SPINLOCK spinlock;
762
+ size_t page_size; // read-only, no lock required to read it
763
+
764
+ size_t demand;
765
+ size_t supply;
766
+
767
+ size_t hit;
768
+ size_t miss;
769
+
770
+ size_t used;
771
+ size_t array_size;
772
+ void **array;
773
+};
774
+
775
+struct {
776
+ struct {
777
+ size_t hit;
778
+ size_t miss_wrong_size;
779
+ size_t miss_short_supply;
780
+ size_t cached_size;
781
+ size_t struct_size;
782
+ } atomic;
783
+
784
+ struct dbengine_page_size slots[MAX_PAGE_SIZES_TO_KEEP];
785
+} dbengine_page_alloc_globals = {
786
+ .atomic = {
787
+ .struct_size = sizeof(dbengine_page_alloc_globals),
788
+ }
789
+};
790
+
791
+__attribute__((constructor)) void initialize_sizes_to_slots(void) {
792
+ uint8_t found[RRDENG_BLOCK_SIZE + 1];
793
+ memset(found, 0, RRDENG_BLOCK_SIZE + 1);
794
+
795
+ for(int i = 0; i < MAX_PAGE_SIZES_TO_KEEP ; i++) {
796
+ struct dbengine_page_size *dps = &dbengine_page_alloc_globals.slots[i];
797
+ memset(dps, 0, sizeof(struct dbengine_page_size));
798
+ netdata_spinlock_init(&dps->spinlock);
799
+ }
800
+
801
+ for(int tier = 0; tier < MAX_PAGE_SIZES_TO_KEEP && tier < RRD_STORAGE_TIERS ; tier++) {
802
+ size_t size = tier_page_size[tier];
803
+
804
+ if(size <= RRDENG_BLOCK_SIZE && !found[size]) {
805
+ struct dbengine_page_size *dps = &dbengine_page_alloc_globals.slots[tier];
806
+ dps->page_size = size;
807
+ found[size] = 1;
808
+ }
809
+ }
810
+}
811
+
812
+static inline struct dbengine_page_size *page_size_lookup(size_t size) {
813
+ for(int i = 0; i < MAX_PAGE_SIZES_TO_KEEP ; i++) {
814
+ if(size == dbengine_page_alloc_globals.slots[i].page_size)
815
+ return &dbengine_page_alloc_globals.slots[i];
816
+ }
817
+ return NULL;
818
+}
819
+
820
+static void dbengine_page_alloc_cleanup1(void) {
821
+ for(int i = 0; i < MAX_PAGE_SIZES_TO_KEEP ; i++) {
822
+ void *page = NULL;
823
+
824
+ struct dbengine_page_size *dps = &dbengine_page_alloc_globals.slots[i];
825
+ netdata_spinlock_lock(&dps->spinlock);
826
+ if(dps->used > MIN_PAGES_PER_SIZE_TO_KEEP) {
827
+ dps->used--;
828
+ internal_fatal(!dps->array[dps->used], "DBENGINE: slot should have a page but is empty");
829
+ page = dps->array[dps->used];
830
+ dps->array[dps->used] = NULL;
831
+ __atomic_sub_fetch(&dbengine_page_alloc_globals.atomic.cached_size, dps->page_size, __ATOMIC_RELAXED);
832
+ }
833
+ netdata_spinlock_unlock(&dps->spinlock);
834
+
835
+ if(page)
836
+ freez(page);
837
+ }
838
+}
839
+
840
void *dbengine_page_alloc(size_t size) {
758
- void *page = mallocz(size);
841
+ void *page = NULL;
842
+
843
+ struct dbengine_page_size *dps = page_size_lookup(size);
844
+ if(dps) {
845
+ netdata_spinlock_lock(&dps->spinlock);
846
+ dps->demand++;
847
+
848
+ if(dps->used > 0) {
849
+ dps->hit++;
850
+ dps->used--;
851
+ internal_fatal(!dps->array[dps->used], "DBENGINE: slot should have a page but is empty");
852
+ page = dps->array[dps->used];
853
+ dps->array[dps->used] = NULL;
854
+ __atomic_add_fetch(&dbengine_page_alloc_globals.atomic.hit, 1, __ATOMIC_RELAXED);
855
+ __atomic_sub_fetch(&dbengine_page_alloc_globals.atomic.cached_size, dps->page_size, __ATOMIC_RELAXED);
856
+ }
857
+ else {
858
+ dps->miss++;
859
+ __atomic_add_fetch(&dbengine_page_alloc_globals.atomic.miss_short_supply, 1, __ATOMIC_RELAXED);
860
+ }
861
+
862
+ netdata_spinlock_unlock(&dps->spinlock);
863
+ }
864
+ else
865
+ __atomic_add_fetch(&dbengine_page_alloc_globals.atomic.miss_wrong_size, 1, __ATOMIC_RELAXED);
866
+
867
+ if(!page)
868
+ page = mallocz(size);
869
+
870
return page;
871
}
872
873
void dbengine_page_free(void *page, size_t size __maybe_unused) {
763
- freez(page);
874
+ if(unlikely(!page || page == DBENGINE_EMPTY_PAGE))
875
+ return;
876
+
877
+ struct dbengine_page_size *dps = page_size_lookup(size);
878
+ if(dps) {
879
+ netdata_spinlock_lock(&dps->spinlock);
880
+ dps->supply++;
881
+
882
+ if(dps->used == dps->array_size) {
883
+ size_t new_array_size = dps->array_size ? dps->array_size * 2 : MIN_PAGES_PER_SIZE_TO_KEEP;
884
+ dps->array = reallocz(dps->array, new_array_size * sizeof(void *));
885
+
886
+ __atomic_add_fetch(&dbengine_page_alloc_globals.atomic.struct_size,
887
+ (new_array_size - dps->array_size) * sizeof(void *), __ATOMIC_RELAXED);
888
+
889
+ dps->array_size = new_array_size;
890
+ }
891
+
892
+ if(dps->used < dps->array_size) {
893
+ dps->array[dps->used] = page;
894
+ dps->used++;
895
+ page = NULL;
896
+ __atomic_add_fetch(&dbengine_page_alloc_globals.atomic.cached_size, dps->page_size, __ATOMIC_RELAXED);
897
+ }
898
+
899
+ netdata_spinlock_unlock(&dps->spinlock);
900
+ }
901
+
902
+ if(page)
903
+ freez(page);
904
}
905
906
+// ----------------------------------------------------------------------------
907
+
908
void *dbengine_extent_alloc(size_t size) {
909
void *extent = mallocz(size);
910
return extent;
960
}
961
962
static void *extent_flushed_to_open_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) {
821
- worker_is_busy(UV_EVENT_FLUSHED_TO_OPEN);
963
+ worker_is_busy(UV_EVENT_DBENGINE_FLUSHED_TO_OPEN);
964
965
uv_fs_t *uv_fs_request = data;
966
struct extent_io_descriptor *xt_io_descr = uv_fs_request->data;
1241
}
1242
1243
static void *extent_write_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) {
1102
- worker_is_busy(UV_EVENT_FLUSH_PAGES);
1244
+ worker_is_busy(UV_EVENT_DBENGINE_EXTENT_WRITE);
1245
struct page_descr_with_data *base = data;
1246
struct extent_io_descriptor *xt_io_descr = datafile_extent_build(ctx, base, completion);
1247
return xt_io_descr;
1354
__atomic_add_fetch(&rrdeng_cache_efficiency_stats.metrics_retention_started, 1, __ATOMIC_RELAXED);
1355
1356
if(worker)
1215
- worker_is_busy(UV_EVENT_ANALYZE_V2);
1357
+ worker_is_busy(UV_EVENT_DBENGINE_FIND_ROTATED_METRICS);
1358
1359
struct rrdengine_journalfile *journalfile = datafile_to_delete->journalfile;
1360
struct journal_v2_header *j2_header = journalfile_v2_data_acquire(journalfile, NULL, 0, 0);
1375
if (!*PValue) {
1376
uuid_first_t_entry = mallocz(sizeof(*uuid_first_t_entry));
1377
uuid_first_t_entry->metric = metric;
1236
- uuid_first_t_entry->first_time_s = mrg_metric_get_first_time_s(main_mrg, metric);
1237
- uuid_first_t_entry->last_time_s = mrg_metric_get_latest_time_s(main_mrg, metric);
1378
+ uuid_first_t_entry->first_time_s = LONG_MAX;
1379
+ uuid_first_t_entry->last_time_s = 0;
1380
uuid_first_t_entry->uuid = mrg_metric_uuid(main_mrg, metric);
1381
*PValue = uuid_first_t_entry;
1382
count++;
1384
}
1385
journalfile_v2_data_release(journalfile);
1386
1245
- info("DBENGINE: recalculating retention for %u metrics", count);
1387
+ info("DBENGINE: recalculating retention for %u metrics starting with datafile %u", count, first_datafile_remaining->fileno);
1388
1389
// Update the first time / last time for all metrics we plan to delete
1390
1391
if(worker)
1250
- worker_is_busy(UV_EVENT_RETENTION_V2);
1392
+ worker_is_busy(UV_EVENT_DBENGINE_FIND_REMAINING_RETENTION);
1393
1394
find_uuid_first_time(ctx, first_datafile_remaining, metric_first_time_JudyL);
1395
1396
if(worker)
1255
- worker_is_busy(UV_EVENT_RETENTION_UPDATE);
1397
+ worker_is_busy(UV_EVENT_DBENGINE_POPULATE_MRG);
1398
1399
info("DBENGINE: updating metric registry retention for %u metrics", count);
1400
1402
bool first_then_next = true;
1403
while ((PValue = JudyLFirstThenNext(metric_first_time_JudyL, &index, &first_then_next))) {
1404
uuid_first_t_entry = *PValue;
1263
- mrg_metric_set_first_time_s(main_mrg, uuid_first_t_entry->metric, uuid_first_t_entry->first_time_s);
1405
+
1406
+ if (likely(uuid_first_t_entry->first_time_s != LONG_MAX && uuid_first_t_entry->last_time_s))
1407
+ mrg_metric_set_first_time_s_if_bigger(main_mrg, uuid_first_t_entry->metric, uuid_first_t_entry->first_time_s);
1408
+ else
1409
+ mrg_metric_set_first_time_s(main_mrg, uuid_first_t_entry->metric, 0);
1410
+
1411
mrg_metric_release(main_mrg, uuid_first_t_entry->metric);
1412
freez(uuid_first_t_entry);
1413
}
1418
worker_is_idle();
1419
}
1420
1274
-static void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, bool worker) {
1421
+void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, bool update_retention, bool worker) {
1422
if(worker)
1276
- worker_is_busy(UV_EVENT_DATAFILE_ACQUIRE);
1423
+ worker_is_busy(UV_EVENT_DBENGINE_DATAFILE_DELETE_WAIT);
1424
1425
bool datafile_got_for_deletion = datafile_acquire_for_deletion(datafile);
1426
1280
- if (ctx_is_available_for_queries(ctx))
1427
+ if (update_retention)
1428
update_metrics_first_time_s(ctx, datafile, datafile->next, worker);
1429
1430
while (!datafile_got_for_deletion) {
1431
if(worker)
1285
- worker_is_busy(UV_EVENT_DATAFILE_ACQUIRE);
1432
+ worker_is_busy(UV_EVENT_DBENGINE_DATAFILE_DELETE_WAIT);
1433
1434
datafile_got_for_deletion = datafile_acquire_for_deletion(datafile);
1435
1452
ctx->config.dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1453
1454
if(worker)
1308
- worker_is_busy(UV_EVENT_DATAFILE_DELETE);
1455
+ worker_is_busy(UV_EVENT_DBENGINE_DATAFILE_DELETE);
1456
1457
struct rrdengine_journalfile *journal_file;
1458
unsigned deleted_bytes, journal_file_bytes, datafile_bytes;
1488
1489
ctx_current_disk_space_decrease(ctx, deleted_bytes);
1490
info("DBENGINE: reclaimed %u bytes of disk space.", deleted_bytes);
1491
+}
1492
+
1493
+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) {
1494
+ datafile_delete(ctx, ctx->datafiles.first, ctx_is_available_for_queries(ctx), true);
1495
1496
if (rrdeng_ctx_exceeded_disk_quota(ctx))
1497
rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1498
1499
rrdcontext_db_rotation();
1349
-}
1500
1351
-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) {
1352
- datafile_delete(ctx, ctx->datafiles.first, true);
1501
return data;
1502
}
1503
1506
}
1507
1508
static void *flush_all_hot_and_dirty_pages_of_section_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) {
1361
- worker_is_busy(UV_EVENT_QUIESCE);
1509
+ worker_is_busy(UV_EVENT_DBENGINE_QUIESCE);
1510
pgc_flush_all_hot_and_dirty_pages(main_cache, (Word_t)ctx);
1511
completion_mark_complete(&ctx->quiesce.completion);
1512
return data;
1517
}
1518
1519
static void *populate_mrg_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) {
1372
- worker_is_busy(UV_EVENT_POPULATE_MRG);
1520
+ worker_is_busy(UV_EVENT_DBENGINE_POPULATE_MRG);
1521
1522
do {
1523
struct rrdengine_datafile *datafile = NULL;
1557
}
1558
1559
static void *ctx_shutdown_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) {
1412
- worker_is_busy(UV_EVENT_SHUTDOWN);
1560
+ worker_is_busy(UV_EVENT_DBENGINE_SHUTDOWN);
1561
1562
completion_wait_for(&ctx->quiesce.completion);
1563
completion_destroy(&ctx->quiesce.completion);
1575
if (!main_cache)
1576
return data;
1577
1430
- worker_is_busy(UV_EVENT_FLUSH_MAIN);
1578
+ worker_is_busy(UV_EVENT_DBENGINE_FLUSH_MAIN_CACHE);
1579
pgc_flush_pages(main_cache, 0);
1580
1581
return data;
1585
if (!main_cache)
1586
return data;
1587
1440
- worker_is_busy(UV_EVENT_EVICT_MAIN);
1588
+ worker_is_busy(UV_EVENT_DBENGINE_EVICT_MAIN_CACHE);
1589
pgc_evict_pages(main_cache, 0, 0);
1590
1591
return data;
1596
}
1597
1598
static void *query_prep_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1451
- worker_is_busy(UV_EVENT_PREP_QUERY);
1599
+ worker_is_busy(UV_EVENT_DBENGINE_QUERY);
1600
PDC *pdc = data;
1601
rrdeng_prep_query(pdc);
1602
return data;
1664
#define MAX_RETRIES_TO_START_INDEX (100)
1665
static void *journal_v2_indexing_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) {
1666
unsigned count = 0;
1519
- worker_is_busy(UV_EVENT_JOURNAL_INDEX_WAIT);
1667
+ worker_is_busy(UV_EVENT_DBENGINE_JOURNAL_INDEX_WAIT);
1668
1669
while (__atomic_load_n(&ctx->atomic.now_deleting_files, __ATOMIC_RELAXED) && count++ < MAX_RETRIES_TO_START_INDEX)
1670
sleep_usec(100 * USEC_PER_MS);
1675
}
1676
1677
struct rrdengine_datafile *datafile = ctx->datafiles.first;
1530
- worker_is_busy(UV_EVENT_JOURNAL_INDEX);
1678
+ worker_is_busy(UV_EVENT_DBENGINE_JOURNAL_INDEX);
1679
count = 0;
1680
while (datafile && datafile->fileno != ctx_last_fileno_get(ctx) && datafile->fileno != ctx_last_flush_fileno_get(ctx)) {
1681
1737
.epdl = epdl_cache_size(),
1738
.deol = deol_cache_size(),
1739
.pd = pd_cache_size(),
1740
+ .pages = __atomic_load_n(&dbengine_page_alloc_globals.atomic.cached_size, __ATOMIC_RELAXED) +
1741
+ __atomic_load_n(&dbengine_page_alloc_globals.atomic.struct_size, __ATOMIC_RELAXED),
1742
+
1743
#ifdef PDC_USE_JULYL
1744
.julyl = julyl_cache_size(),
1745
#endif
1751
}
1752
1753
static void *cleanup_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) {
1603
- worker_is_busy(UV_EVENT_BUFFERS_CLEANUP);
1754
+ worker_is_busy(UV_EVENT_DBENGINE_BUFFERS_CLEANUP);
1755
1756
rrdeng_cmd_cleanup1();
1757
work_request_cleanup1();
1764
extent_buffer_cleanup1();
1765
epdl_cleanup1();
1766
deol_cleanup1();
1767
+ dbengine_page_alloc_cleanup1();
1768
1769
{
1770
static time_t last_run_s = 0;