@cryptotaxi247 / netdata / commits / a334f9429

Refactor chart/dim/label cleanup functions (#22435)

* Refactor chart/dim/label cleanup functions * Address review comments * Address review comments * Address review comments

Stelios Fragkakis committed May 7, 2026 at 18:21 UTC a334f942984f463aaf495f1b6909817b5012269d
1 file changed +132 -180
src/database/sqlite/sqlite_metadata.c
+132 -180
@@ -1413,213 +1413,120 @@ static uint64_t get_rowid_from_statement(const char *sql)
1413 }
1414
1415
1416 -#define SQL_GET_MAX_DIM_ROW_ID "SELECT MAX(rowid) FROM dimension"
1417 -
1418 -static bool check_dimension_metadata(struct meta_config_s *wc)
1419 -{
1420 - static time_t next_execution_t = 0;
1421 - static uint64_t last_row_id = 0;
1422 - static uint64_t max_row_id = 0;
1423 -
1424 - time_t now = now_realtime_sec();
1425 -
1426 - if (!next_execution_t) {
1427 - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK;
1428 - max_row_id = get_rowid_from_statement(SQL_GET_MAX_DIM_ROW_ID);
1429 - nd_log(NDLS_DAEMON, NDLP_INFO, "Dimension metadata check has been scheduled to run (max id = %lu)", max_row_id);
1430 - }
1431 -
1432 - if (next_execution_t && next_execution_t > now)
1433 - return true;
1434 -
1435 - if (max_row_id && last_row_id >= max_row_id) {
1436 - nd_log_daemon(NDLP_INFO, "Dimension metadata check completed");
1437 - // For long running agents, check in a week
1438 - next_execution_t = now + 604800;
1439 - return true;
1440 - }
1441 -
1442 - sqlite3_stmt *res = NULL;
1443 -
1444 - if (!PREPARE_STATEMENT(db_meta, SELECT_DIMENSION_LIST, &res))
1445 - return true;
1446 -
1447 - uint32_t total_checked = 0;
1448 - uint32_t total_deleted = 0;
1449 -
1450 - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Checking dimensions starting after row %" PRIu64, last_row_id);
1451 -
1452 - worker_is_busy(UV_EVENT_DIMENSION_CLEANUP);
1453 -
1454 - (void) run_cleanup_loop(
1455 - res,
1456 - wc,
1457 - dimension_can_be_deleted,
1458 - delete_dimension_uuid,
1459 - &total_checked,
1460 - &total_deleted,
1461 - &last_row_id,
1462 - NULL,
1463 - NULL,
1464 - false,
1465 - false);
1466 -
1467 - now = now_realtime_sec();
1468 - next_execution_t = now + METADATA_MAINTENANCE_REPEAT;
1469 - nd_log_daemon(
1470 - NDLP_DEBUG,
1471 - "Dimensions checked %u, deleted %u. Checks will resume in %d seconds",
1472 - total_checked,
1473 - total_deleted,
1474 - METADATA_MAINTENANCE_REPEAT);
1475 -
1476 - SQLITE_FINALIZE(res);
1477 -
1478 - worker_is_idle();
1479 - return false;
1480 -}
1481 -
1482 -#define SQL_GET_MAX_CHART_ROW_ID "SELECT MAX(rowid) FROM chart"
1416 +// Descriptor + state for one cleanup cycle (dimension / chart / chart_label).
1417 +// Replaces the three former check_*_metadata() functions with one driver:
1418 +// first call -> arm timer; snapshot_pending = true so the snapshot
1419 +// is taken at the *next* entry past the timer (NOT
1420 +// now, which would burn a SELECT for nothing — the
1421 +// scan can't run yet)
1422 +// timer not expired -> return true (yield)
1423 +// snapshot_pending true -> snapshot max(rowid) for the upcoming pass and log
1424 +// "scheduled to run"; using an explicit boolean
1425 +// (instead of max_row_id==0) disambiguates a
1426 +// legitimately empty table (MAX returns NULL→0)
1427 +// from "snapshot deferred"
1428 +// past max(rowid) -> log completion; one-shot cycles (chart, label)
1429 +// mark completed and never re-run; cycles with
1430 +// complete_repeat_after > 0 (dim) reset last_row_id
1431 +// and set snapshot_pending so the next firing takes
1432 +// a fresh snapshot
1433 +// else -> run one cleanup_loop slice and re-arm short timer
1434 +struct cleanup_cycle {
1435 + // descriptor (immutable)
1436 + const char *select_sql; // SELECT id, rowid FROM <table> WHERE rowid > ?
1437 + const char *max_rowid_sql; // SELECT MAX(rowid) FROM <table>
1438 + bool (*check_cb)(nd_uuid_t *, sqlite3_stmt **, bool);
1439 + void (*action_cb)(nd_uuid_t *, sqlite3_stmt **, bool);
1440 + bool check_flag;
1441 + bool action_flag;
1442 + int repeat_after; // seconds to next slice after a partial pass
1443 + int complete_repeat_after; // seconds to next pass after full completion;
1444 + // 0 means one-shot (don't re-run after first done)
1445 + size_t worker_event; // worker_is_busy id
1446 + const char *label_singular; // "Dimension" / "Chart" / "Chart label" (INFO logs)
1447 + const char *label_plural; // "Dimensions" / "Charts" / "Chart labels" (DEBUG summary)
1448 + const char *label_lower; // "dimensions" / "charts" / "chart labels" (DEBUG checking)
1449 +
1450 + // mutable state (per-cycle)
1451 + time_t next_execution_t;
1452 + uint64_t last_row_id;
1453 + uint64_t max_row_id;
1454 + bool snapshot_pending; // true => take a fresh max(rowid) snapshot at the next entry past the timer
1455 + bool completed; // for one-shot cycles only
1456 +};
1457
1484 -static bool check_chart_metadata(struct meta_config_s *wc)
1458 +static bool run_cleanup_cycle(struct cleanup_cycle *c, struct meta_config_s *wc)
1459 {
1486 - static time_t next_execution_t = 0;
1487 - static uint64_t last_row_id = 0;
1488 - static uint64_t max_row_id = 0;
1489 - static bool check_completed = false;
1490 -
1491 - if (check_completed)
1460 + if (c->complete_repeat_after == 0 && c->completed)
1461 return true;
1462
1463 time_t now = now_realtime_sec();
1464
1496 - if (!next_execution_t) {
1497 - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK;
1498 - max_row_id = get_rowid_from_statement(SQL_GET_MAX_CHART_ROW_ID);
1499 - nd_log(NDLS_DAEMON, NDLP_INFO, "Chart metadata check has been scheduled to run (max id = %lu)", max_row_id);
1465 + if (!c->next_execution_t) {
1466 + c->next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK;
1467 + c->snapshot_pending = true;
1468 }
1469
1502 - if (next_execution_t && next_execution_t > now)
1470 + if (c->next_execution_t > now)
1471 return true;
1472
1505 - if (max_row_id && last_row_id >= max_row_id) {
1506 - nd_log(NDLS_DAEMON, NDLP_INFO, "Chart metadata check completed");
1507 - check_completed = true;
1473 + if (c->snapshot_pending) {
1474 + c->max_row_id = get_rowid_from_statement(c->max_rowid_sql);
1475 + c->snapshot_pending = false;
1476 + nd_log(NDLS_DAEMON, NDLP_INFO,
1477 + "%s metadata check has been scheduled to run (max id = %" PRIu64 ")",
1478 + c->label_singular, c->max_row_id);
1479 + }
1480 +
1481 + // No `c->max_row_id &&` guard: a legitimately empty table snapshots to 0,
1482 + // and `0 >= 0` correctly takes the completion branch (one-shot cycles
1483 + // mark completed; dim re-arms for the next pass).
1484 + if (c->last_row_id >= c->max_row_id) {
1485 + nd_log(NDLS_DAEMON, NDLP_INFO, "%s metadata check completed", c->label_singular);
1486 + if (c->complete_repeat_after) {
1487 + // Re-arm for another full pass; the snapshot is deferred to the
1488 + // next entry past the timer so it isn't stale by then.
1489 + c->next_execution_t = now + c->complete_repeat_after;
1490 + c->last_row_id = 0;
1491 + c->snapshot_pending = true;
1492 + }
1493 + else
1494 + c->completed = true;
1495 return true;
1496 }
1497
1498 sqlite3_stmt *res = NULL;
1512 -
1513 - if (!PREPARE_STATEMENT(db_meta, SELECT_CHART_LIST, &res))
1499 + if (!PREPARE_STATEMENT(db_meta, c->select_sql, &res))
1500 return true;
1501
1502 uint32_t total_checked = 0;
1503 uint32_t total_deleted = 0;
1504
1519 - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Checking charts starting after row %" PRIu64, last_row_id);
1520 -
1521 - worker_is_busy(UV_EVENT_CHART_CLEANUP);
1522 - sqlite3_stmt *check_res = NULL;
1523 - sqlite3_stmt *action_res = NULL;
1524 - (void)run_cleanup_loop(
1525 - res,
1526 - wc,
1527 - chart_can_be_deleted,
1528 - delete_chart_uuid,
1529 - &total_checked,
1530 - &total_deleted,
1531 - &last_row_id,
1532 - &check_res,
1533 - &action_res,
1534 - true,
1535 - false);
1536 -
1537 - SQLITE_FINALIZE(check_res);
1538 - SQLITE_FINALIZE(action_res);
1539 -
1540 - now = now_realtime_sec();
1541 - next_execution_t = now + METADATA_MAINTENANCE_REPEAT;
1542 - nd_log_daemon(
1543 - NDLP_DEBUG,
1544 - "Charts checked %u, deleted %u. Checks will resume in %d seconds",
1545 - total_checked,
1546 - total_deleted,
1547 - METADATA_MAINTENANCE_REPEAT);
1548 -
1549 - SQLITE_FINALIZE(res);
1550 - worker_is_idle();
1551 - return false;
1552 -}
1553 -
1554 -#define SQL_GET_MAX_CHART_LABEL_ROW_ID "SELECT MAX(rowid) FROM chart_label"
1555 -
1556 -static bool check_label_metadata(struct meta_config_s *wc)
1557 -{
1558 - static time_t next_execution_t = 0;
1559 - static uint64_t last_row_id = 0;
1560 - static uint64_t max_row_id = 0;
1561 - static bool check_completed = false;
1562 -
1563 - if (check_completed)
1564 - return true;
1565 -
1566 - time_t now = now_realtime_sec();
1567 -
1568 - if (!next_execution_t) {
1569 - next_execution_t = now + METADATA_MAINTENANCE_FIRST_CHECK;
1570 - max_row_id = get_rowid_from_statement(SQL_GET_MAX_CHART_LABEL_ROW_ID);
1571 - nd_log(NDLS_DAEMON, NDLP_INFO, "Chart label metadata check has been scheduled to run (max id = %lu)", max_row_id);
1572 - }
1573 -
1574 - if (next_execution_t && next_execution_t > now)
1575 - return true;
1576 -
1577 - if (max_row_id && last_row_id >= max_row_id) {
1578 - nd_log(NDLS_DAEMON, NDLP_INFO, "Chart label metadata check completed");
1579 - check_completed = true;
1580 - return true;
1581 - }
1582 -
1583 - sqlite3_stmt *res = NULL;
1505 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
1506 + "Checking %s starting after row %" PRIu64, c->label_lower, c->last_row_id);
1507
1585 - if (!PREPARE_STATEMENT(db_meta, SELECT_CHART_LABEL_LIST, &res))
1586 - return true;
1587 -
1588 - uint32_t total_checked = 0;
1589 - uint32_t total_deleted = 0;
1590 -
1591 - nd_log(NDLS_DAEMON, NDLP_DEBUG, "Checking charts labels starting after row %" PRIu64, last_row_id);
1508 + worker_is_busy(c->worker_event);
1509
1510 sqlite3_stmt *check_res = NULL;
1511 sqlite3_stmt *action_res = NULL;
1512
1596 - worker_is_busy(UV_EVENT_CHART_LABEL_CLEANUP);
1597 -
1598 - (void )run_cleanup_loop(
1599 - res,
1600 - wc,
1601 - chart_can_be_deleted,
1602 - delete_chart_uuid,
1603 - &total_checked,
1604 - &total_deleted,
1605 - &last_row_id,
1606 - &check_res,
1607 - &action_res,
1608 - false,
1609 - true);
1513 + (void) run_cleanup_loop(
1514 + res, wc,
1515 + c->check_cb, c->action_cb,
1516 + &total_checked, &total_deleted,
1517 + &c->last_row_id,
1518 + &check_res, &action_res,
1519 + c->check_flag, c->action_flag);
1520
1521 SQLITE_FINALIZE(check_res);
1522 SQLITE_FINALIZE(action_res);
1523
1524 now = now_realtime_sec();
1615 - next_execution_t = now + METADATA_LABEL_CHECK_INTERVAL;
1525 + c->next_execution_t = now + c->repeat_after;
1526
1617 - nd_log_daemon(
1618 - NDLP_DEBUG,
1619 - "Chart labels checked %u, deleted %u. Checks will resume in %d seconds",
1620 - total_checked,
1621 - total_deleted,
1622 - METADATA_LABEL_CHECK_INTERVAL);
1527 + nd_log_daemon(NDLP_DEBUG,
1528 + "%s checked %u, deleted %u. Checks will resume in %d seconds",
1529 + c->label_plural, total_checked, total_deleted, c->repeat_after);
1530
1531 SQLITE_FINALIZE(res);
1532
@@ -1627,6 +1534,51 @@ static bool check_label_metadata(struct meta_config_s *wc)
1534 return false;
1535 }
1536
1537 +static struct cleanup_cycle dim_cleanup_cycle = {
1538 + .select_sql = SELECT_DIMENSION_LIST,
1539 + .max_rowid_sql = "SELECT MAX(rowid) FROM dimension",
1540 + .check_cb = dimension_can_be_deleted,
1541 + .action_cb = delete_dimension_uuid,
1542 + .check_flag = false,
1543 + .action_flag = false,
1544 + .repeat_after = METADATA_MAINTENANCE_REPEAT,
1545 + .complete_repeat_after = 604800, // re-fire weekly: re-snapshots max(rowid) and rescans dimensions added since the previous pass
1546 + .worker_event = UV_EVENT_DIMENSION_CLEANUP,
1547 + .label_singular = "Dimension",
1548 + .label_plural = "Dimensions",
1549 + .label_lower = "dimensions",
1550 +};
1551 +
1552 +static struct cleanup_cycle chart_cleanup_cycle = {
1553 + .select_sql = SELECT_CHART_LIST,
1554 + .max_rowid_sql = "SELECT MAX(rowid) FROM chart",
1555 + .check_cb = chart_can_be_deleted,
1556 + .action_cb = delete_chart_uuid,
1557 + .check_flag = true,
1558 + .action_flag = false,
1559 + .repeat_after = METADATA_MAINTENANCE_REPEAT,
1560 + .complete_repeat_after = 0, // one-shot
1561 + .worker_event = UV_EVENT_CHART_CLEANUP,
1562 + .label_singular = "Chart",
1563 + .label_plural = "Charts",
1564 + .label_lower = "charts",
1565 +};
1566 +
1567 +static struct cleanup_cycle label_cleanup_cycle = {
1568 + .select_sql = SELECT_CHART_LABEL_LIST,
1569 + .max_rowid_sql = "SELECT MAX(rowid) FROM chart_label",
1570 + .check_cb = chart_can_be_deleted,
1571 + .action_cb = delete_chart_uuid,
1572 + .check_flag = false,
1573 + .action_flag = true,
1574 + .repeat_after = METADATA_LABEL_CHECK_INTERVAL,
1575 + .complete_repeat_after = 0, // one-shot
1576 + .worker_event = UV_EVENT_CHART_LABEL_CLEANUP,
1577 + .label_singular = "Chart label",
1578 + .label_plural = "Chart labels",
1579 + .label_lower = "chart labels",
1580 +};
1581 +
1582 static void cleanup_health_log(struct meta_config_s *config)
1583 {
1584 static time_t next_execution_t = 0;
@@ -1859,9 +1811,9 @@ void run_metadata_cleanup(struct meta_config_s *config)
1811 if (unlikely(SHUTDOWN_REQUESTED(config)))
1812 return;
1813
1862 - if (check_dimension_metadata(config))
1863 - if (check_chart_metadata(config))
1864 - check_label_metadata(config);
1814 + if (run_cleanup_cycle(&dim_cleanup_cycle, config))
1815 + if (run_cleanup_cycle(&chart_cleanup_cycle, config))
1816 + run_cleanup_cycle(&label_cleanup_cycle, config);
1817
1818 cleanup_health_log(config);
1819