Unconditionally delete very old models. (#15720)
* Unconditionally delete very old models. * Rebase origin/master * Use the training threads to prune old models. To keep performance in check, we shedule the pruning whenever the number of successfully completed transactions is a multiple of 64.
vkalintiris committed
Aug 23, 2023 at 14:53 UTC
941fff633212dc0034a08148622fd0e4023f07e2
3 files changed
+86
-2
ml/Config.cc
+5
@@ -28,7 +28,9 @@ void ml_config_load(ml_config_t *cfg) {
28
unsigned max_train_samples = config_get_number(config_section_ml, "maximum num samples to train", 6 * 3600);
29
unsigned min_train_samples = config_get_number(config_section_ml, "minimum num samples to train", 1 * 900);
30
unsigned train_every = config_get_number(config_section_ml, "train every", 3 * 3600);
31
+
32
unsigned num_models_to_use = config_get_number(config_section_ml, "number of models per dimension", 9);
33
+ unsigned delete_models_older_than = config_get_number(config_section_ml, "delete models older than", 60 * 60 * 24 * 7);
34
35
unsigned diff_n = config_get_number(config_section_ml, "num samples to diff", 1);
36
unsigned smooth_n = config_get_number(config_section_ml, "num samples to smooth", 3);
@@ -58,7 +60,9 @@ void ml_config_load(ml_config_t *cfg) {
60
max_train_samples = clamp<unsigned>(max_train_samples, 1 * 3600, 24 * 3600);
61
min_train_samples = clamp<unsigned>(min_train_samples, 1 * 900, 6 * 3600);
62
train_every = clamp<unsigned>(train_every, 1 * 3600, 6 * 3600);
63
+
64
num_models_to_use = clamp<unsigned>(num_models_to_use, 1, 7 * 24);
65
+ delete_models_older_than = clamp<unsigned>(delete_models_older_than, 60 * 60 * 24 * 1, 60 * 60 * 24 * 7);
66
67
diff_n = clamp(diff_n, 0u, 1u);
68
smooth_n = clamp(smooth_n, 0u, 5u);
@@ -100,6 +104,7 @@ void ml_config_load(ml_config_t *cfg) {
104
cfg->train_every = train_every;
105
106
cfg->num_models_to_use = num_models_to_use;
107
+ cfg->delete_models_older_than = delete_models_older_than;
108
109
cfg->diff_n = diff_n;
110
cfg->smooth_n = smooth_n;
ml/ml-private.h
+4
@@ -291,6 +291,9 @@ typedef struct {
291
RRDDIM *training_results_not_enough_collected_values_rd;
292
RRDDIM *training_results_null_acquired_dimension_rd;
293
RRDDIM *training_results_chart_under_replication_rd;
294
+
295
+ size_t num_db_transactions;
296
+ size_t num_models_to_prune;
297
} ml_training_thread_t;
298
299
typedef struct {
@@ -301,6 +304,7 @@ typedef struct {
304
unsigned train_every;
305
306
unsigned num_models_to_use;
307
+ unsigned delete_models_older_than;
308
309
unsigned db_engine_anomaly_rate_every;
310
ml/ml.cc
+77
-2
@@ -436,6 +436,10 @@ const char *db_models_delete =
436
"DELETE FROM models "
437
"WHERE dim_id = @dim_id AND before < @before;";
438
439
+const char *db_models_prune =
440
+ "DELETE FROM models "
441
+ "WHERE after < @after LIMIT @n;";
442
+
443
static int
444
ml_dimension_add_model(const uuid_t *metric_uuid, const ml_kmeans_t *km)
445
{
@@ -563,6 +567,58 @@ bind_fail:
567
return rc;
568
}
569
570
+static int
571
+ml_prune_old_models(size_t num_models_to_prune)
572
+{
573
+ static __thread sqlite3_stmt *res = NULL;
574
+ int rc = 0;
575
+ int param = 0;
576
+
577
+ if (unlikely(!db)) {
578
+ error_report("Database has not been initialized");
579
+ return 1;
580
+ }
581
+
582
+ if (unlikely(!res)) {
583
+ rc = prepare_statement(db, db_models_prune, &res);
584
+ if (unlikely(rc != SQLITE_OK)) {
585
+ error_report("Failed to prepare statement to prune models, rc = %d", rc);
586
+ return rc;
587
+ }
588
+ }
589
+
590
+ int after = (int) (now_realtime_sec() - Cfg.delete_models_older_than);
591
+
592
+ rc = sqlite3_bind_int(res, ++param, after);
593
+ if (unlikely(rc != SQLITE_OK))
594
+ goto bind_fail;
595
+
596
+ rc = sqlite3_bind_int(res, ++param, num_models_to_prune);
597
+ if (unlikely(rc != SQLITE_OK))
598
+ goto bind_fail;
599
+
600
+ rc = execute_insert(res);
601
+ if (unlikely(rc != SQLITE_DONE)) {
602
+ error_report("Failed to prune old models, rc = %d", rc);
603
+ return rc;
604
+ }
605
+
606
+ rc = sqlite3_reset(res);
607
+ if (unlikely(rc != SQLITE_OK)) {
608
+ error_report("Failed to reset statement when pruning old models, rc = %d", rc);
609
+ return rc;
610
+ }
611
+
612
+ return 0;
613
+
614
+bind_fail:
615
+ error_report("Failed to bind parameter %d to prune old models, rc = %d", param, rc);
616
+ rc = sqlite3_reset(res);
617
+ if (unlikely(rc != SQLITE_OK))
618
+ error_report("Failed to reset statement to prune old models, rc = %d", rc);
619
+ return rc;
620
+}
621
+
622
int ml_dimension_load_models(RRDDIM *rd) {
623
ml_dimension_t *dim = (ml_dimension_t *) rd->ml_dimension;
624
if (!dim)
@@ -1498,9 +1554,12 @@ bool ml_dimension_is_anomalous(RRDDIM *rd, time_t curr_time, double value, bool
1554
}
1555
1556
static void ml_flush_pending_models(ml_training_thread_t *training_thread) {
1501
- int rc = db_execute(db, "BEGIN TRANSACTION;");
1557
int op_no = 1;
1558
1559
+ // begin transaction
1560
+ int rc = db_execute(db, "BEGIN TRANSACTION;");
1561
+
1562
+ // add/delete models
1563
if (!rc) {
1564
op_no++;
1565
@@ -1513,12 +1572,22 @@ static void ml_flush_pending_models(ml_training_thread_t *training_thread) {
1572
}
1573
}
1574
1575
+ // prune old models
1576
+ if (!rc) {
1577
+ if ((training_thread->num_db_transactions % 64) == 0) {
1578
+ rc = ml_prune_old_models(training_thread->num_models_to_prune);
1579
+ if (!rc)
1580
+ training_thread->num_models_to_prune = 0;
1581
+ }
1582
+ }
1583
+
1584
+ // commit transaction
1585
if (!rc) {
1586
op_no++;
1587
rc = db_execute(db, "COMMIT TRANSACTION;");
1588
}
1589
1521
- // try to rollback transaction if we got any failures
1590
+ // rollback transaction on failure
1591
if (rc) {
1592
netdata_log_error("Trying to rollback ML transaction because it failed with rc=%d, op_no=%d", rc, op_no);
1593
op_no++;
@@ -1527,6 +1596,11 @@ static void ml_flush_pending_models(ml_training_thread_t *training_thread) {
1596
netdata_log_error("ML transaction rollback failed with rc=%d", rc);
1597
}
1598
1599
+ if (!rc) {
1600
+ training_thread->num_db_transactions++;
1601
+ training_thread->num_models_to_prune += training_thread->pending_model_info.size();
1602
+ }
1603
+
1604
training_thread->pending_model_info.clear();
1605
}
1606
@@ -1677,6 +1751,7 @@ void ml_init()
1751
db = NULL;
1752
}
1753
1754
+ // create table
1755
if (db) {
1756
char *err = NULL;
1757
int rc = sqlite3_exec(db, db_models_create_table, NULL, NULL, &err);