@cryptotaxi247 / netdata-1 / commits / 7553d19a1

Verify and cleanup deleted contexts (#19389)

* Verify and cleanup deleted contexts * Remove context from cleanup list immediately when processed * Throttle cleanup if wal size is getting big * Add context check creation time

Stelios Fragkakis committed Jan 14, 2025 at 17:21 UTC 7553d19a18eab4f1c75e6edb02b9c51de55947ee
4 files changed +253 -9
src/database/sqlite/sqlite_context.c
+77 -1
@@ -13,6 +13,13 @@ const char *database_context_config[] = {
13 "last_time_t INT NOT NULL, deleted INT NOT NULL, "
14 "family TEXT, PRIMARY KEY (host_id, id))",
15
16 + "CREATE TABLE IF NOT EXISTS context_metadata_cleanup (id INTEGER PRIMARY KEY, host_id BLOB, context TEXT NOT NULL, date_created INT, "
17 + "UNIQUE (host_id, context))",
18 +
19 + "CREATE TRIGGER IF NOT EXISTS del_context1 AFTER DELETE ON context "
20 + "BEGIN INSERT INTO context_metadata_cleanup (host_id, context, date_created) "
21 + "VALUES (old.host_id, old.id, UNIXEPOCH()) ON CONFLICT DO UPDATE SET date_created = excluded.date_created; END",
22 +
23 NULL
24 };
25
@@ -261,8 +268,77 @@ done:
268 return (rc_stored != SQLITE_DONE);
269 }
270
264 -// Delete a context
271 +#define CTX_DELETE_CONTEXT_META_CLEANUP_ITEM "DELETE FROM context_metadata_cleanup WHERE host_id = @host_id AND context = @context"
272 +
273 +void ctx_delete_metadata_cleanup_context(sqlite3_stmt **res, nd_uuid_t(*host_uuid), const char *context)
274 +{
275 + if (!*res) {
276 + if (!PREPARE_STATEMENT(db_context_meta, CTX_DELETE_CONTEXT_META_CLEANUP_ITEM, res))
277 + return;
278 + }
279 +
280 + int param = 0;
281 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(*res, ++param, host_uuid, sizeof(*host_uuid), SQLITE_STATIC));
282 + SQLITE_BIND_FAIL(done, sqlite3_bind_text(*res, ++param, context, -1, SQLITE_STATIC));
283 +
284 + param = 0;
285 + int rc = sqlite3_step_monitored(*res);
286 + if (rc != SQLITE_DONE)
287 + error_report("Failed to delete context check entry, rc = %d", rc);
288 +
289 +done:
290 + REPORT_BIND_FAIL(*res, param);
291 + SQLITE_RESET(*res);
292 +}
293 +
294 +// Schedule context cleanup for host
295 +#define CTX_GET_CONTEXT_META_CLEANUP_LIST "SELECT context FROM context_metadata_cleanup WHERE host_id = @host_id"
296 +
297 +void ctx_get_context_list_to_cleanup(nd_uuid_t *host_uuid, void (*cleanup_cb)(Pvoid_t JudyL, void *data), void *data)
298 +{
299 + if (unlikely(!host_uuid))
300 + return;
301 +
302 + sqlite3_stmt *res = NULL;
303 +
304 + if (!PREPARE_STATEMENT(db_context_meta, CTX_GET_CONTEXT_META_CLEANUP_LIST, &res))
305 + return;
306 +
307 + int param = 0;
308 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, host_uuid, sizeof(*host_uuid), SQLITE_STATIC));
309 + param = 0;
310 +
311 + const char *context;
312 + Pvoid_t CTX_JudyL = NULL;
313 + Pvoid_t *Pvalue;
314 + while (sqlite3_step_monitored(res) == SQLITE_ROW) {
315 + context = (char *) sqlite3_column_text(res, 0);
316 + STRING *ctx = string_strdupz(context);
317 + Pvalue = JudyLIns(&CTX_JudyL, (Word_t) ctx, PJE0);
318 + if (*Pvalue)
319 + string_freez(ctx);
320 + else
321 + *(int *)Pvalue = 1;
322 + }
323
324 + if (CTX_JudyL) {
325 + cleanup_cb(CTX_JudyL, data);
326 +
327 + bool first = true;
328 + Word_t Index = 0;
329 + while ((Pvalue = JudyLFirstThenNext(CTX_JudyL, &Index, &first))) {
330 + STRING *ctx = (STRING *) Index;
331 + string_freez(ctx);
332 + }
333 + }
334 + (void)JudyLFreeArray(&CTX_JudyL, PJE0);
335 +
336 +done:
337 + REPORT_BIND_FAIL(res, param);
338 + SQLITE_FINALIZE(res);
339 +}
340 +
341 +// Delete a context
342 #define CTX_DELETE_CONTEXT "DELETE FROM context WHERE host_id = @host_id AND id = @context"
343 int ctx_delete_context(nd_uuid_t *host_uuid, VERSIONED_CONTEXT_DATA *context_data)
344 {
src/database/sqlite/sqlite_context.h
+3
@@ -63,6 +63,9 @@ void ctx_get_dimension_list(nd_uuid_t *host_uuid, void (*dict_cb)(SQL_DIMENSION_
63
64 int ctx_store_context(nd_uuid_t *host_uuid, VERSIONED_CONTEXT_DATA *context_data);
65
66 +void ctx_get_context_list_to_cleanup(nd_uuid_t *host_uuid, void (*cleanup_cb)(Pvoid_t context, void *data), void *data);
67 +void ctx_delete_metadata_cleanup_context(sqlite3_stmt **context_res, nd_uuid_t(*host_uuid), const char *context);
68 +
69 #define ctx_update_context(host_uuid, context_data) ctx_store_context(host_uuid, context_data)
70
71 int ctx_delete_context(nd_uuid_t *host_id, VERSIONED_CONTEXT_DATA *context_data);
src/database/sqlite/sqlite_functions.c
+6 -3
@@ -5,6 +5,8 @@
5 #define MAX_PREPARED_STATEMENTS (32)
6 pthread_key_t key_pool[MAX_PREPARED_STATEMENTS];
7
8 +long long def_journal_size_limit = 16777216;
9 +
10 SQLITE_API int sqlite3_exec_monitored(
11 sqlite3 *db, /* An open database */
12 const char *sql, /* SQL to be evaluated */
@@ -84,7 +86,6 @@ int configure_sqlite_database(sqlite3 *database, int target_version, const char
86 const char *def_synchronous = "NORMAL";
87 const char *def_journal_mode = "WAL";
88 const char *def_temp_store = "MEMORY";
87 - long long def_journal_size_limit = 16777216;
89 long long def_cache_size = -2000;
90
91 // https://www.sqlite.org/pragma.html#pragma_auto_vacuum
@@ -122,8 +123,10 @@ int configure_sqlite_database(sqlite3 *database, int target_version, const char
123 // https://www.sqlite.org/pragma.html#pragma_journal_size_limit
124 // PRAGMA schema.journal_size_limit = N ;
125 snprintfz(buf, sizeof(buf) - 1, "PRAGMA journal_size_limit=%lld", def_journal_size_limit);
125 - if (config_exists(CONFIG_SECTION_SQLITE, "journal size limit"))
126 - snprintfz(buf, sizeof(buf) - 1, "PRAGMA journal_size_limit=%lld", config_get_number(CONFIG_SECTION_SQLITE, "journal size limit", def_journal_size_limit));
126 + if (config_exists(CONFIG_SECTION_SQLITE, "journal size limit")) {
127 + def_journal_size_limit = config_get_number(CONFIG_SECTION_SQLITE, "journal size limit", def_journal_size_limit);
128 + snprintfz(buf, sizeof(buf) - 1, "PRAGMA journal_size_limit=%lld", def_journal_size_limit);
129 + }
130 if (init_database_batch(database, list, description))
131 return 1;
132
src/database/sqlite/sqlite_metadata.c
+167 -5
@@ -8,6 +8,8 @@
8
9 #define DB_METADATA_VERSION 18
10
11 +extern long long def_journal_size_limit;
12 +
13 const char *database_config[] = {
14 "CREATE TABLE IF NOT EXISTS host(host_id BLOB PRIMARY KEY, hostname TEXT NOT NULL, "
15 "registry_hostname TEXT NOT NULL default 'unknown', update_every INT NOT NULL default 1, "
@@ -184,7 +186,6 @@ sqlite3 *db_meta = NULL;
186
187 enum metadata_opcode {
188 METADATA_DATABASE_NOOP = 0,
187 - METADATA_DATABASE_TIMER,
189 METADATA_DEL_DIMENSION,
190 METADATA_STORE_CLAIM_ID,
191 METADATA_ADD_HOST_INFO,
@@ -668,6 +669,31 @@ void sqlite_uuid_random(sqlite3_context *context, int argc, sqlite3_value **argv
669 sqlite3_result_blob(context, &uuid, sizeof(nd_uuid_t), SQLITE_TRANSIENT);
670 }
671
672 +static int64_t sql_get_wal_size(const char *database_file)
673 +{
674 + char filename[FILENAME_MAX + 1];
675 + snprintfz(filename, sizeof(filename) - 1, "%s/%s-wal", netdata_configured_cache_dir, database_file);
676 +
677 + uv_fs_t req;
678 + int result = uv_fs_stat(NULL, &req, filename, NULL);
679 + int64_t file_size = result >= 0 ? (int64_t) req.statbuf.st_size : -1;
680 +
681 + uv_fs_req_cleanup(&req);
682 + return file_size;
683 +}
684 +
685 +#define SQLITE_METADATA_WAL_LIMIT_X (10)
686 +
687 +bool sql_metadata_wal_size_acceptable()
688 +{
689 + int64_t wal_size = sql_get_wal_size("netdata-meta.db");
690 +
691 + if (wal_size > SQLITE_METADATA_WAL_LIMIT_X * def_journal_size_limit)
692 + return false;
693 +
694 + return true;
695 +}
696 +
697 // Init
698 /*
699 * Initialize the SQLite database
@@ -898,6 +924,29 @@ done:
924 return rc != SQLITE_DONE;
925 }
926
927 +#define SQL_DELETE_DIMENSION_BY_ID "DELETE FROM dimension WHERE rowid = @dimension_row AND dim_id = @uuid"
928 +
929 +static void delete_dimension_by_rowid(sqlite3_stmt **res, int64_t dimension_id, nd_uuid_t *dim_uuid)
930 +{
931 + if (!*res) {
932 + if (!PREPARE_STATEMENT(db_meta, SQL_DELETE_DIMENSION_BY_ID, res))
933 + return;
934 + }
935 +
936 + int param = 0;
937 + SQLITE_BIND_FAIL(done, sqlite3_bind_int64(*res, ++param, dimension_id));
938 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(*res, ++param, dim_uuid, sizeof(*dim_uuid), SQLITE_STATIC));
939 +
940 + param = 0;
941 + int rc = sqlite3_step_monitored(*res);
942 + if (unlikely(rc != SQLITE_DONE))
943 + error_report("Failed to delete dimension id, rc = %d", rc);
944 +
945 +done:
946 + REPORT_BIND_FAIL(*res, param);
947 + SQLITE_RESET(*res);
948 +}
949 +
950 static void delete_dimension_uuid(nd_uuid_t *dimension_uuid, sqlite3_stmt **action_res __maybe_unused, bool flag __maybe_unused)
951 {
952 static __thread sqlite3_stmt *res = NULL;
@@ -1542,11 +1591,127 @@ void vacuum_database(sqlite3 *database, const char *db_alias, int threshold, int
1591 }
1592 }
1593
1594 +
1595 +#define SQL_SELECT_HOST_CTX_CHART_DIM_LIST \
1596 + "SELECT d.dim_id, d.rowid FROM chart c, dimension d WHERE c.chart_id = d.chart_id AND c.rowid = @rowid"
1597 +
1598 +static bool clean_host_chart_dimensions(sqlite3_stmt **res, int64_t chart_row_id, size_t *checked, size_t *deleted)
1599 +{
1600 + struct metadata_wc *wc = &metasync_worker;
1601 +
1602 + if (!*res) {
1603 + if (!PREPARE_STATEMENT(db_meta, SQL_SELECT_HOST_CTX_CHART_DIM_LIST, res))
1604 + return false;
1605 + }
1606 + int param = 0;
1607 + SQLITE_BIND_FAIL(done, sqlite3_bind_int64(*res, ++param, chart_row_id));
1608 + param = 0;
1609 +
1610 + sqlite3_stmt *dim_del_stmt = NULL;
1611 +
1612 + bool can_continue = true;
1613 +
1614 + while (can_continue && sqlite3_step_monitored(*res) == SQLITE_ROW) {
1615 + if (sqlite3_column_bytes(*res, 0) != sizeof(nd_uuid_t))
1616 + continue;
1617 +
1618 + nd_uuid_t *dim_uuid = (nd_uuid_t *)sqlite3_column_blob(*res, 0);
1619 + int64_t dimension_id = sqlite3_column_int64(*res, 1);
1620 +
1621 + if (dimension_can_be_deleted(dim_uuid, NULL, false)) {
1622 + delete_dimension_by_rowid(&dim_del_stmt, dimension_id, dim_uuid);
1623 + (*deleted)++;
1624 + }
1625 + (*checked)++;
1626 + can_continue = (!metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN)) && sql_metadata_wal_size_acceptable();
1627 + }
1628 + SQLITE_FINALIZE(dim_del_stmt);
1629 +
1630 +done:
1631 + REPORT_BIND_FAIL(*res, param);
1632 + SQLITE_RESET(*res);
1633 + return can_continue;
1634 +}
1635 +
1636 +#define SQL_SELECT_HOST_CTX_CHART_LIST "SELECT rowid, context FROM chart WHERE host_id = @host"
1637 +
1638 +static void cleanup_host_context_metadata(Pvoid_t CTX_JudyL, void *data)
1639 +{
1640 + if (!CTX_JudyL || !data)
1641 + return;
1642 +
1643 + struct metadata_wc *wc = &metasync_worker;
1644 +
1645 + RRDHOST *host = data;
1646 +
1647 + sqlite3_stmt *res = NULL;
1648 + sqlite3_stmt *dimension_res = NULL;
1649 + sqlite3_stmt *context_res = NULL;
1650 +
1651 + if (!PREPARE_STATEMENT(db_meta, SQL_SELECT_HOST_CTX_CHART_LIST, &res))
1652 + return;
1653 +
1654 + Word_t num_of_contexts = JudyLCount(CTX_JudyL, 0, -1, PJE0);
1655 +
1656 + nd_log_daemon(NDLP_DEBUG, "Verifying the retention of %zu contexts for host %s", num_of_contexts, rrdhost_hostname(host));
1657 +
1658 + int param = 0;
1659 + SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
1660 +
1661 + param = 0;
1662 + Pvoid_t *Pvalue;
1663 + int64_t chart_row_id;
1664 +
1665 + size_t deleted = 0;
1666 + size_t checked = 0;
1667 +
1668 + bool can_continue = true;
1669 + while (can_continue && sqlite3_step_monitored(res) == SQLITE_ROW) {
1670 + chart_row_id = sqlite3_column_int64(res, 0);
1671 + const char *context = (char *)sqlite3_column_text(res, 1);
1672 + STRING *ctx = string_strdupz(context);
1673 + Pvalue = JudyLGet(CTX_JudyL, (Word_t)ctx, PJE0);
1674 + if (Pvalue) {
1675 + can_continue = clean_host_chart_dimensions(&dimension_res, chart_row_id, &checked, &deleted);
1676 + ctx_delete_metadata_cleanup_context(&context_res, &host->host_id.uuid, context);
1677 + }
1678 + string_freez(ctx);
1679 + can_continue =
1680 + can_continue && (!metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN)) && sql_metadata_wal_size_acceptable();
1681 + }
1682 + SQLITE_FINALIZE(dimension_res);
1683 + SQLITE_FINALIZE(context_res);
1684 +
1685 + nd_log_daemon(
1686 + NDLP_DEBUG,
1687 + "Verified the contexts of host %s (Checked %zu metrics and removed %zu)",
1688 + rrdhost_hostname(host),
1689 + checked,
1690 + deleted);
1691 +
1692 +done:
1693 + REPORT_BIND_FAIL(res, param);
1694 + SQLITE_FINALIZE(res);
1695 +}
1696 +
1697 void run_metadata_cleanup(struct metadata_wc *wc)
1698 {
1699 if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN)))
1700 return;
1701
1702 + if (sql_metadata_wal_size_acceptable()) {
1703 + RRDHOST *host;
1704 + dfe_start_reentrant(rrdhost_root_index, host) {
1705 + ctx_get_context_list_to_cleanup(&host->host_id.uuid, cleanup_host_context_metadata, host);
1706 + if (metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN) || false == sql_metadata_wal_size_acceptable())
1707 + break;
1708 + }
1709 + dfe_done(host);
1710 + }
1711 +
1712 + if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN)))
1713 + return;
1714 +
1715 check_dimension_metadata(wc);
1716 check_chart_metadata(wc);
1717 check_label_metadata(wc);
@@ -1956,7 +2121,7 @@ static void do_chart_label_cleanup(struct judy_list_t *cl_cleanup_data)
2121 }
2122
2123 // Worker thread to scan hosts for pending metadata to store
1959 -static void start_metadata_hosts(uv_work_t *req __maybe_unused)
2124 +static void start_metadata_hosts(uv_work_t *req)
2125 {
2126 register_libuv_worker_jobs();
2127
@@ -2072,7 +2237,6 @@ static void metadata_event_loop(void *arg)
2237 {
2238 worker_register("METASYNC");
2239 worker_register_job_name(METADATA_DATABASE_NOOP, "noop");
2075 - worker_register_job_name(METADATA_DATABASE_TIMER, "timer");
2240 worker_register_job_name(METADATA_DEL_DIMENSION, "delete dimension");
2241 worker_register_job_name(METADATA_STORE_CLAIM_ID, "add claim id");
2242 worker_register_job_name(METADATA_ADD_HOST_INFO, "add host info");
@@ -2130,7 +2294,6 @@ static void metadata_event_loop(void *arg)
2294 nd_uuid_t *uuid;
2295 RRDHOST *host = NULL;
2296 ALARM_ENTRY *ae = NULL;
2133 -// struct aclk_sync_cfg_t *host_aclk_sync;
2297
2298 worker_is_idle();
2299 uv_run(loop, UV_RUN_DEFAULT);
@@ -2156,7 +2319,6 @@ static void metadata_event_loop(void *arg)
2319
2320 switch (opcode) {
2321 case METADATA_DATABASE_NOOP:
2159 - case METADATA_DATABASE_TIMER:
2322 break;
2323 case METADATA_DEL_DIMENSION:
2324 uuid = (nd_uuid_t *) cmd.param[0];