165
return LIBUV_WORKERS_RELAXED;
166
}
167
168
+static inline void check_and_schedule_db_rotation(struct rrdengine_instance *ctx)
169
+{
170
+ internal_fatal(rrdeng_main.tid != gettid_cached(), "check_and_schedule_db_rotation() can only be run from the event loop thread");
171
+
172
+ if (ctx->datafiles.pending_rotate) {
173
+ nd_log_daemon(NDLP_DEBUG, "DBENGINE: tier %d is already pending rotation", ctx->config.tier);
174
+ return;
175
+ }
176
+
177
+ if(ctx_is_available_for_queries(ctx) && rrdeng_ctx_tier_cap_exceeded(ctx)) {
178
+ ctx->datafiles.pending_rotate = true;
179
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
180
+ }
181
+}
182
+
183
static inline void work_done(struct rrdeng_work *work_request) {
184
aral_freez(rrdeng_main.work_cmd.ar, work_request);
185
}
873
{
874
if(completion)
875
completion_mark_complete(completion);
876
+
877
+ check_and_schedule_db_rotation(ctx);
878
}
879
880
static void *extent_write_tp_worker(
926
927
extent_flush_to_open(ctx, xt_io_descr, ret < 0);
928
912
- if(ctx_is_available_for_queries(ctx) && rrdeng_ctx_tier_cap_exceeded(ctx))
913
- rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
929
done:
930
__atomic_sub_fetch(&ctx->atomic.extents_currently_being_flushed, 1, __ATOMIC_RELAXED);
931
worker_is_idle();
934
935
static void after_database_rotate(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) {
936
__atomic_store_n(&ctx->atomic.now_deleting_files, false, __ATOMIC_RELAXED);
937
+
938
if (__atomic_load_n(&ctx->atomic.needs_indexing, __ATOMIC_RELAXED))
923
- rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_INDEX, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
939
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_INDEX, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
940
+
941
+ check_and_schedule_db_rotation(ctx);
942
}
943
944
struct uuid_first_time_s {
1236
worker_is_idle();
1237
}
1238
1221
-void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, bool update_retention, bool worker) {
1239
+void datafile_delete(
1240
+ struct rrdengine_instance *ctx,
1241
+ struct rrdengine_datafile *datafile,
1242
+ bool update_retention,
1243
+ bool disk_time,
1244
+ bool worker)
1245
+{
1246
if(worker)
1247
worker_is_busy(UV_EVENT_DBENGINE_DATAFILE_DELETE_WAIT);
1248
1266
}
1267
}
1268
1269
+ netdata_log_info("DBENGINE: acquired data file \"%s/"
1270
+ DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1271
+ "\" for deletion.",
1272
+ ctx->config.dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1273
+
1274
if (update_retention)
1275
update_metrics_first_time_s(ctx, datafile, datafile->next, worker);
1276
1277
__atomic_add_fetch(&rrdeng_cache_efficiency_stats.datafile_deletion_started, 1, __ATOMIC_RELAXED);
1249
- netdata_log_info("DBENGINE: deleting data file '%s/"
1278
+ netdata_log_info("DBENGINE: deleting data file \"%s/"
1279
DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1251
- "'.",
1280
+ "\".",
1281
ctx->config.dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1282
1283
if(worker)
1297
journal_file_bytes = journalfile_current_size(journal_file);
1298
deleted_bytes = journalfile_v2_data_size_get(journal_file);
1299
1271
- netdata_log_info("DBENGINE: deleting data and journal files to maintain disk quota");
1300
+ netdata_log_info("DBENGINE: deleting data and journal files to maintain %s", disk_time ? "disk quota" : "time retention");
1301
// This will delete journalfile_v2 and journalfile_v1
1302
ret = journalfile_destroy_unsafe(journal_file, datafile);
1303
if (!ret) {
1324
freez(datafile);
1325
1326
ctx_current_disk_space_decrease(ctx, deleted_bytes);
1298
- netdata_log_info("DBENGINE: reclaimed %zu bytes of disk space.", deleted_bytes);
1327
+ char size_for_humans[128];
1328
+ size_snprintf(size_for_humans, sizeof(size_for_humans), deleted_bytes, "B", false);
1329
+ netdata_log_info("DBENGINE: reclaimed %zu bytes (%s) of disk space.", deleted_bytes, size_for_humans);
1330
}
1331
1332
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) {
1302
- datafile_delete(ctx, ctx->datafiles.first, ctx_is_available_for_queries(ctx), true);
1303
-
1304
- if (rrdeng_ctx_tier_cap_exceeded(ctx))
1305
- rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1333
+ datafile_delete(ctx, ctx->datafiles.first, ctx_is_available_for_queries(ctx), true, true);
1334
1335
rrdcontext_db_rotation();
1336
1703
pdc_to_epdl_router(ctx, pdc, epdl_populate_pages_synchronously, epdl_populate_pages_asynchronously);
1704
}
1705
1678
-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) {
1679
- unsigned count = 0;
1706
+static struct rrdengine_datafile *release_and_aquire_next_datafile_for_indexing(struct rrdengine_instance *ctx, struct rrdengine_datafile *release_datafile)
1707
+{
1708
+ struct rrdengine_datafile *datafile = NULL;
1709
+
1710
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1711
+ if (release_datafile) {
1712
+ datafile = release_datafile->next;
1713
+ datafile_release(release_datafile, DATAFILE_ACQUIRE_INDEXING);
1714
+ }
1715
+ else
1716
+ datafile = ctx->datafiles.first;
1717
1681
- struct rrdengine_datafile *datafile = ctx->datafiles.first;
1682
- worker_is_busy(UV_EVENT_DBENGINE_JOURNAL_INDEX);
1683
- count = 0;
1718
while (datafile && datafile->fileno != ctx_last_fileno_get(ctx) && datafile->fileno != ctx_last_flush_fileno_get(ctx)) {
1719
if(journalfile_v2_data_available(datafile->journalfile)) {
1686
- // journal file v2 is already there for this datafile
1720
datafile = datafile->next;
1721
continue;
1722
}
1723
+ bool locked = datafile_acquire(datafile, DATAFILE_ACQUIRE_INDEXING);
1724
+ if (locked) {
1725
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1726
+ return datafile;
1727
+ }
1728
+ nd_log_daemon(NDLP_INFO, "DBENGINE: Datafile %u CANNOT be locked for indexing; skipping", datafile->fileno);
1729
+ }
1730
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1731
+ return NULL;
1732
+}
1733
+
1734
+
1735
+static void *journal_v2_indexing_tp_worker(struct rrdengine_instance *ctx, void *data, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1736
+ unsigned count = 0;
1737
+
1738
+ worker_is_busy(UV_EVENT_DBENGINE_JOURNAL_INDEX);
1739
+ struct rrdengine_datafile *datafile = NULL;
1740
+ char path[RRDENG_PATH_MAX];
1741
+
1742
+ bool index_once = false;
1743
+ while ((datafile = release_and_aquire_next_datafile_for_indexing(ctx, datafile))) {
1744
1745
spinlock_lock(&datafile->writers.spinlock);
1746
bool available = (datafile->writers.running || datafile->writers.flushed_to_open_running) ? false : true;
1747
spinlock_unlock(&datafile->writers.spinlock);
1748
1749
+ journalfile_v1_generate_path(datafile, path, sizeof(path));
1750
+
1751
if(!available) {
1696
- nd_log(NDLS_DAEMON, NDLP_NOTICE,
1697
- "DBENGINE: journal file %u needs to be indexed, but it has writers working on it - "
1752
+ nd_log_daemon(NDLP_NOTICE,
1753
+ "DBENGINE: journal file \"%s\" needs to be indexed, but it has writers working on it - "
1754
"skipping it for now",
1699
- datafile->fileno);
1700
-
1701
- datafile = datafile->next;
1755
+ path);
1756
continue;
1757
}
1758
1705
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
1706
- "DBENGINE: journal file %u is ready to be indexed",
1707
- datafile->fileno);
1759
+ if (index_once && unlikely(rrdeng_ctx_tier_cap_exceeded(ctx))) {
1760
+ nd_log_daemon(
1761
+ NDLP_INFO, "DBENGINE: tier %d reached quota limit, stopping journal indexing", ctx->config.tier);
1762
+ __atomic_store_n(&ctx->atomic.needs_indexing, true, __ATOMIC_RELAXED);
1763
+ datafile_release(datafile, DATAFILE_ACQUIRE_INDEXING);
1764
+ break;
1765
+ }
1766
+ nd_log_daemon(NDLP_INFO, "DBENGINE: journal file \"%s\" is ready to be indexed", path);
1767
1768
pgc_open_cache_to_journal_v2(open_cache, (Word_t) ctx, (int) datafile->fileno, ctx->config.page_type,
1769
journalfile_migrate_to_v2_callback, (void *) datafile->journalfile);
1770
1712
- count++;
1771
+ index_once = true;
1772
1714
- datafile = datafile->next;
1773
+ count++;
1774
1716
- if (unlikely(!ctx_is_available_for_queries(ctx)))
1775
+ // check if we are shutting down
1776
+ if (unlikely(!ctx_is_available_for_queries(ctx))) {
1777
+ datafile_release(datafile, DATAFILE_ACQUIRE_INDEXING);
1778
break;
1779
+ }
1780
}
1781
1782
errno_clear();
1808
1809
static void after_journal_v2_indexing(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) {
1810
__atomic_store_n(&ctx->atomic.migration_to_v2_running, false, __ATOMIC_RELAXED);
1749
- rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1811
+
1812
+ check_and_schedule_db_rotation(ctx);
1813
}
1814
1815
struct rrdeng_buffer_sizes rrdeng_pulse_memory_sizes(void) {
1898
// Check if disk or retention time cap reached
1899
bool rrdeng_ctx_tier_cap_exceeded(struct rrdengine_instance *ctx)
1900
{
1838
-
1901
uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1902
if (!ctx->datafiles.first || !ctx->datafiles.first->next) {
1903
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1908
1909
uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1910
1849
- time_t retention = get_tier_retention(ctx);
1850
-
1851
- if (ctx->config.max_retention_s && retention > ctx->config.max_retention_s)
1852
- return true;
1911
+ if (ctx->config.max_retention_s) {
1912
+ time_t retention = get_tier_retention(ctx);
1913
+ if (retention > ctx->config.max_retention_s) {
1914
+ __atomic_store_n(&ctx->datafiles.disk_time, false, __ATOMIC_RELAXED);
1915
+ return true;
1916
+ }
1917
+ }
1918
1854
- if (ctx->config.max_disk_space && estimated_disk_space > ctx->config.max_disk_space)
1919
+ if (ctx->config.max_disk_space && estimated_disk_space > ctx->config.max_disk_space) {
1920
+ __atomic_store_n(&ctx->datafiles.disk_time, true, __ATOMIC_RELAXED);
1921
return true;
1922
+ }
1923
1924
return false;
1925
}
1936
STORAGE_ENGINE *eng = localhost->db[tier].eng;
1937
if (!eng || eng->seb != STORAGE_ENGINE_BACKEND_DBENGINE)
1938
continue;
1872
- bool cleanup = rrdeng_ctx_tier_cap_exceeded(multidb_ctx[tier]);
1873
- if (cleanup)
1874
- rrdeng_enq_cmd(multidb_ctx[tier], RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1939
+ check_and_schedule_db_rotation(multidb_ctx[tier]);
1940
}
1941
1942
worker_is_idle();
2099
}
2100
}
2101
2037
-#define NOT_INDEXING_OR_DELETING_FILES(ctx) \
2038
- (!__atomic_load_n(&(ctx)->atomic.migration_to_v2_running, __ATOMIC_RELAXED) && \
2039
- !__atomic_load_n(&(ctx)->atomic.now_deleting_files, __ATOMIC_RELAXED))
2102
+#define NOT_DELETING_FILES(ctx) \
2103
+ (!__atomic_load_n(&(ctx)->atomic.now_deleting_files, __ATOMIC_RELAXED))
2104
+
2105
+#define NOT_INDEXING_FILES(ctx) \
2106
+ (!__atomic_load_n(&(ctx)->atomic.migration_to_v2_running, __ATOMIC_RELAXED))
2107
2108
void *dbengine_event_loop(void* arg) {
2109
sanity_check();
2248
case RRDENG_OPCODE_JOURNAL_INDEX: {
2249
struct rrdengine_instance *ctx = cmd.ctx;
2250
struct rrdengine_datafile *datafile = cmd.data;
2184
- if (NOT_INDEXING_OR_DELETING_FILES(ctx) && ctx_is_available_for_queries(ctx)) {
2251
+ ctx->datafiles.pending_index = false;
2252
+ if (NOT_INDEXING_FILES(ctx) && ctx_is_available_for_queries(ctx)) {
2253
__atomic_store_n(&ctx->atomic.migration_to_v2_running, true, __ATOMIC_RELAXED);
2254
__atomic_store_n(&ctx->atomic.needs_indexing, false, __ATOMIC_RELAXED);
2255
work_dispatch(ctx, datafile, NULL, opcode, journal_v2_indexing_tp_worker, after_journal_v2_indexing);
2256
}
2189
- else
2190
- __atomic_store_n(&ctx->atomic.needs_indexing, true, __ATOMIC_RELAXED);
2257
break;
2258
}
2259
2260
case RRDENG_OPCODE_DATABASE_ROTATE: {
2261
struct rrdengine_instance *ctx = cmd.ctx;
2196
- if (NOT_INDEXING_OR_DELETING_FILES(ctx) && ctx->datafiles.first->next != NULL &&
2262
+ ctx->datafiles.pending_rotate = false;
2263
+ if (NOT_DELETING_FILES(ctx) && ctx->datafiles.first->next != NULL &&
2264
ctx->datafiles.first->next->next != NULL && rrdeng_ctx_tier_cap_exceeded(ctx)) {
2265
__atomic_store_n(&ctx->atomic.now_deleting_files, true, __ATOMIC_RELAXED);
2266
work_dispatch(ctx, NULL, NULL, opcode, database_rotate_tp_worker, after_database_rotate);