@cryptotaxi247 / netdata-1 / commits / a706491f7

Improve agent to cloud synchronization performance (#12348)

* Switch to prepare statement when storing active charts / dimensions * Switch to prepare statement when storing chart labels * Switch to prepare statement when doing a node id lookup * Switch to prepare statement when loading the node id for a host * Improve performance by avoiding db query * Use prepare statement when counting pending chart messages to send to the cloud * Delay locking while preparing commands * No need to use buffer, avoid memory allocation overhead * Switch to prepare statement when loading pending chart updates to send to the cloud

Stelios Fragkakis committed Mar 9, 2022 at 19:54 UTC a706491f775fbd959fc97e38cf902f6748855e18
3 files changed +93 -95
aclk/aclk_query.c
+7
@@ -58,6 +58,13 @@ static RRDHOST *node_id_2_rrdhost(const char *node_id)
58 {
59 int res;
60 uuid_t node_id_bin, host_id_bin;
61 +
62 + rrd_rdlock();
63 + RRDHOST *host = find_host_by_node_id((char *) node_id);
64 + rrd_unlock();
65 + if (host)
66 + return host;
67 +
68 char host_id[UUID_STR_LEN];
69 if (uuid_parse(node_id, node_id_bin)) {
70 error("Couldn't parse UUID %s", node_id);
database/sqlite/sqlite_aclk_chart.c
+53 -68
@@ -29,12 +29,11 @@ static int payload_sent(char *uuid_str, uuid_t *uuid, void *payload, size_t payl
29 int send_status = 0;
30
31 if (unlikely(!res)) {
32 - BUFFER *sql = buffer_create(1024);
33 - buffer_sprintf(sql,"SELECT 1 FROM aclk_chart_latest_%s acl, aclk_chart_payload_%s acp "
32 + char sql[ACLK_SYNC_QUERY_SIZE];
33 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "SELECT 1 FROM aclk_chart_latest_%s acl, aclk_chart_payload_%s acp "
34 "WHERE acl.unique_id = acp.unique_id AND acl.uuid = @uuid AND acp.payload = @payload;",
35 uuid_str, uuid_str);
36 - rc = prepare_statement(db_meta, (char *) buffer_tostring(sql), &res);
37 - buffer_free(sql);
36 + rc = prepare_statement(db_meta, sql, &res);
37 if (rc != SQLITE_OK) {
38 error_report("Failed to prepare statement to check payload data");
39 return 0;
@@ -70,14 +69,11 @@ static int aclk_add_chart_payload(struct aclk_database_worker_config *wc, uuid_t
69 return 0;
70
71 if (unlikely(!res_chart)) {
73 - BUFFER *sql = buffer_create(1024);
74 -
75 - buffer_sprintf(sql,"INSERT INTO aclk_chart_payload_%s (unique_id, uuid, claim_id, date_created, type, payload) " \
76 - "VALUES (@unique_id, @uuid, @claim_id, strftime('%%s','now'), @type, @payload);", wc->uuid_str);
77 -
78 - rc = prepare_statement(db_meta, (char *) buffer_tostring(sql), &res_chart);
79 - buffer_free(sql);
80 -
72 + char sql[ACLK_SYNC_QUERY_SIZE];
73 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1,
74 + "INSERT INTO aclk_chart_payload_%s (unique_id, uuid, claim_id, date_created, type, payload) " \
75 + "VALUES (@unique_id, @uuid, @claim_id, strftime('%%s','now'), @type, @payload);", wc->uuid_str);
76 + rc = prepare_statement(db_meta, sql, &res_chart);
77 if (rc != SQLITE_OK) {
78 error_report("Failed to prepare statement to store chart payload data");
79 return 1;
@@ -320,21 +316,20 @@ void aclk_send_chart_event(struct aclk_database_worker_config *wc, struct aclk_d
316 uint64_t last_sequence;
317 time_t last_timestamp = 0;
318
323 - BUFFER *sql = buffer_create(1024);
324 -
325 - sqlite3_stmt *res = NULL;
326 -
327 - buffer_sprintf(sql, "SELECT ac.sequence_id, acp.payload, ac.date_created, ac.type, ac.uuid " \
328 - "FROM aclk_chart_%s ac, aclk_chart_payload_%s acp " \
329 - "WHERE ac.date_submitted IS NULL AND ac.unique_id = acp.unique_id AND ac.update_count > 0 " \
330 - "AND acp.claim_id = @claim_id ORDER BY ac.sequence_id ASC LIMIT %d;", wc->uuid_str, wc->uuid_str, limit);
319 + char sql[ACLK_SYNC_QUERY_SIZE];
320 + static __thread sqlite3_stmt *res = NULL;
321
332 - rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res, 0);
333 - if (rc != SQLITE_OK) {
334 - error_report("Failed to prepare statement when trying to send a chart update via ACLK");
335 - buffer_free(sql);
336 - freez(claim_id);
337 - return;
322 + if (unlikely(!res)) {
323 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1,"SELECT ac.sequence_id, acp.payload, ac.date_created, ac.type, ac.uuid " \
324 + "FROM aclk_chart_%s ac, aclk_chart_payload_%s acp " \
325 + "WHERE ac.date_submitted IS NULL AND ac.unique_id = acp.unique_id AND ac.update_count > 0 " \
326 + "AND acp.claim_id = @claim_id ORDER BY ac.sequence_id ASC LIMIT %d;", wc->uuid_str, wc->uuid_str, limit);
327 + rc = prepare_statement(db_meta, sql, &res);
328 + if (rc != SQLITE_OK) {
329 + error_report("Failed to prepare statement when trying to send a chart update via ACLK");
330 + freez(claim_id);
331 + return;
332 + }
333 }
334
335 rc = sqlite3_bind_blob(res, 1, claim_uuid , sizeof(claim_uuid), SQLITE_STATIC);
@@ -388,21 +383,18 @@ void aclk_send_chart_event(struct aclk_database_worker_config *wc, struct aclk_d
383 error_report("Failed to reset statement when pushing chart events, rc = %d", rc);
384
385 if (likely(first_sequence)) {
391 - buffer_flush(sql);
386
387 db_lock();
394 - buffer_sprintf(sql, "UPDATE aclk_chart_%s SET status = NULL, date_submitted=strftime('%%s','now') "
388 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "UPDATE aclk_chart_%s SET status = NULL, date_submitted=strftime('%%s','now') "
389 "WHERE date_submitted IS NULL AND sequence_id BETWEEN %" PRIu64 " AND %" PRIu64 ";",
390 wc->uuid_str, first_sequence, last_sequence);
397 - db_execute(buffer_tostring(sql));
398 -
399 - buffer_flush(sql);
400 - buffer_sprintf(sql, "INSERT OR REPLACE INTO aclk_chart_latest_%s (uuid, unique_id, date_submitted) "
391 + db_execute(sql);
392 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "INSERT OR REPLACE INTO aclk_chart_latest_%s (uuid, unique_id, date_submitted) "
393 " SELECT uuid, unique_id, date_submitted FROM aclk_chart_%s s "
394 " WHERE date_submitted IS NOT NULL AND sequence_id BETWEEN %" PRIu64 " AND %" PRIu64
395 " ;",
396 wc->uuid_str, wc->uuid_str, first_sequence, last_sequence);
405 - db_execute(buffer_tostring(sql));
397 + db_execute(sql);
398 db_unlock();
399
400 aclk_chart_inst_and_dim_update(payload_list, payload_list_size, is_dim, position_list, wc->batch_id);
@@ -436,11 +428,10 @@ void aclk_send_chart_event(struct aclk_database_worker_config *wc, struct aclk_d
428 freez(is_dim);
429
430 bind_fail:
439 - rc = sqlite3_finalize(res);
431 + rc = sqlite3_reset(res);
432 if (unlikely(rc != SQLITE_OK))
441 - error_report("Failed to finalize statement when pushing chart events, rc = %d", rc);
433 + error_report("Failed to reset statement when pushing chart events, rc = %d", rc);
434
443 - buffer_free(sql);
435 freez(claim_id);
436 return;
437 }
@@ -521,15 +512,15 @@ void aclk_receive_chart_ack(struct aclk_database_worker_config *wc, struct aclk_
512
513 log_access("IN [%s (%s)]: Received ack chart sequence id %"PRIu64, wc->node_id, wc->host ? wc->host->hostname : "N/A", cmd.param1);
514
524 - BUFFER *sql = buffer_create(1024);
515 + char sql[ACLK_SYNC_QUERY_SIZE];
516
526 - buffer_sprintf(sql, "UPDATE aclk_chart_%s SET date_updated=strftime('%%s','now') WHERE sequence_id <= @sequence_id "
527 - "AND date_submitted IS NOT NULL AND date_updated IS NULL;", wc->uuid_str);
517 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1,"UPDATE aclk_chart_%s SET date_updated=strftime('%%s','now') WHERE sequence_id <= @sequence_id "
518 + "AND date_submitted IS NOT NULL AND date_updated IS NULL;", wc->uuid_str);
519
529 - rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res, 0);
520 + rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0);
521 if (rc != SQLITE_OK) {
531 - error_report("Failed to prepare statement count sequence ids in the database");
532 - goto prepare_fail;
522 + error_report("Failed to prepare statement to ack chart sequence ids");
523 + return;
524 }
525
526 rc = sqlite3_bind_int64(res, 1, (uint64_t) cmd.param1);
@@ -540,13 +531,10 @@ void aclk_receive_chart_ack(struct aclk_database_worker_config *wc, struct aclk_
531 if (rc != SQLITE_DONE)
532 error_report("Failed to ACK sequence id, rc = %d", rc);
533
543 - bind_fail:
544 - if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
545 - error_report("Failed to finalize statement to ACK older sequence ids, rc = %d", rc);
546 -
547 - prepare_fail:
548 - buffer_free(sql);
549 - return;
534 +bind_fail:
535 + if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
536 + error_report("Failed to finalize statement to ACK older sequence ids, rc = %d", rc);
537 + return;
538 }
539
540 void aclk_receive_chart_reset(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd)
@@ -556,12 +544,11 @@ void aclk_receive_chart_reset(struct aclk_database_worker_config *wc, struct acl
544 wc->uuid_str, cmd.param1);
545 db_execute(buffer_tostring(sql));
546 if (cmd.param1 == 1) {
559 - db_lock();
547 buffer_flush(sql);
548 log_access("IN [%s (%s)]: Received chart full resync.", wc->node_id, wc->host ? wc->host->hostname : "N/A");
549 buffer_sprintf(sql, "DELETE FROM aclk_chart_payload_%s; DELETE FROM aclk_chart_%s; " \
550 "DELETE FROM aclk_chart_latest_%s;", wc->uuid_str, wc->uuid_str, wc->uuid_str);
564 -
551 + db_lock();
552 db_execute("BEGIN TRANSACTION;");
553 db_execute(buffer_tostring(sql));
554 db_execute("COMMIT TRANSACTION;");
@@ -909,43 +896,43 @@ failed:
896
897 uint32_t sql_get_pending_count(struct aclk_database_worker_config *wc)
898 {
912 - BUFFER *sql = buffer_create(1024);
913 - sqlite3_stmt *res = NULL;
899 + char sql[ACLK_SYNC_QUERY_SIZE];
900 + static __thread sqlite3_stmt *res = NULL;
901
915 - buffer_sprintf(sql,"SELECT count(1) FROM aclk_chart_%s ac WHERE ac.date_submitted IS NULL;", wc->uuid_str);
902 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "SELECT count(1) FROM aclk_chart_%s ac WHERE ac.date_submitted IS NULL;", wc->uuid_str);
903
904 int rc;
905 uint32_t chart_payload_count = 0;
919 - rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res, 0);
920 - if (rc != SQLITE_OK) {
921 - error_report("Failed to prepare statement to count pending messages");
922 - goto fail;
906 + if (unlikely(!res)) {
907 + rc = prepare_statement(db_meta, sql, &res);
908 + if (rc != SQLITE_OK) {
909 + error_report("Failed to prepare statement to count pending messages");
910 + return 0;
911 + }
912 }
913 while (sqlite3_step(res) == SQLITE_ROW)
914 chart_payload_count = (uint32_t) sqlite3_column_int(res, 0);
915
927 - rc = sqlite3_finalize(res);
916 + rc = sqlite3_reset(res);
917 if (unlikely(rc != SQLITE_OK))
918 error_report("Failed to reset statement when fetching pending messages, rc = %d", rc);
919
931 -fail:
932 - buffer_free(sql);
920 return chart_payload_count;
921 }
922
923 void sql_get_last_chart_sequence(struct aclk_database_worker_config *wc)
924 {
938 - BUFFER *sql = buffer_create(1024);
925 + char sql[ACLK_SYNC_QUERY_SIZE];
926
940 - buffer_sprintf(sql,"SELECT ac.sequence_id, ac.date_created FROM aclk_chart_%s ac " \
941 - "WHERE ac.date_submitted IS NOT NULL ORDER BY ac.sequence_id DESC LIMIT 1;", wc->uuid_str);
927 + snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "SELECT ac.sequence_id, ac.date_created FROM aclk_chart_%s ac " \
928 + "WHERE ac.date_submitted IS NOT NULL ORDER BY ac.sequence_id DESC LIMIT 1;", wc->uuid_str);
929
930 int rc;
931 sqlite3_stmt *res = NULL;
945 - rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res, 0);
932 + rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0);
933 if (rc != SQLITE_OK) {
934 error_report("Failed to prepare statement to find last chart sequence id");
948 - goto fail;
935 + return;
936 }
937
938 wc->chart_sequence_id = 0;
@@ -961,8 +948,6 @@ void sql_get_last_chart_sequence(struct aclk_database_worker_config *wc)
948 if (unlikely(rc != SQLITE_OK))
949 error_report("Failed to reset statement when fetching chart sequence info, rc = %d", rc);
950
964 -fail:
965 - buffer_free(sql);
951 return;
952 }
953
database/sqlite/sqlite_functions.c
+33 -27
@@ -121,7 +121,7 @@ static int store_active_uuid_object(sqlite3_stmt **res, char *statement, uuid_t
121
122 // Check if we should need to prepare the statement
123 if (!*res) {
124 - rc = sqlite3_prepare_v2(db_meta, statement, -1, res, 0);
124 + rc = prepare_statement(db_meta, statement, res);
125 if (unlikely(rc != SQLITE_OK)) {
126 error_report("Failed to prepare statement to store active object, rc = %d", rc);
127 return rc;
@@ -142,7 +142,7 @@ static int store_active_uuid_object(sqlite3_stmt **res, char *statement, uuid_t
142 */
143 void store_active_chart(uuid_t *chart_uuid)
144 {
145 - sqlite3_stmt *res = NULL;
145 + static __thread sqlite3_stmt *res = NULL;
146 int rc;
147
148 if (unlikely(!db_meta)) {
@@ -158,7 +158,7 @@ void store_active_chart(uuid_t *chart_uuid)
158 if (rc != SQLITE_DONE)
159 error_report("Failed to store active chart, rc = %d", rc);
160
161 - rc = sqlite3_finalize(res);
161 + rc = sqlite3_reset(res);
162 if (unlikely(rc != SQLITE_OK))
163 error_report("Failed to finalize statement in store active chart, rc = %d", rc);
164 return;
@@ -170,7 +170,7 @@ void store_active_chart(uuid_t *chart_uuid)
170 */
171 void store_active_dimension(uuid_t *dimension_uuid)
172 {
173 - sqlite3_stmt *res = NULL;
173 + static __thread sqlite3_stmt *res = NULL;
174 int rc;
175
176 if (unlikely(!db_meta)) {
@@ -186,7 +186,7 @@ void store_active_dimension(uuid_t *dimension_uuid)
186 if (rc != SQLITE_DONE)
187 error_report("Failed to store active dimension, rc = %d", rc);
188
189 - rc = sqlite3_finalize(res);
189 + rc = sqlite3_reset(res);
190 if (unlikely(rc != SQLITE_OK))
191 error_report("Failed to finalize statement in store active dimension, rc = %d", rc);
192 return;
@@ -1335,7 +1335,7 @@ void add_migrated_file(char *path, uint64_t file_size)
1335
1336 void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, char *value)
1337 {
1338 - sqlite3_stmt *res = NULL;
1338 + static __thread sqlite3_stmt *res = NULL;
1339 int rc;
1340
1341 if (unlikely(!db_meta)) {
@@ -1344,10 +1344,12 @@ void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, cha
1344 return;
1345 }
1346
1347 - rc = sqlite3_prepare_v2(db_meta, SQL_INS_CHART_LABEL, -1, &res, 0);
1348 - if (unlikely(rc != SQLITE_OK)) {
1349 - error_report("Failed to prepare statement store chart labels");
1350 - return;
1347 + if (unlikely(!res)) {
1348 + rc = prepare_statement(db_meta, SQL_INS_CHART_LABEL, &res);
1349 + if (unlikely(rc != SQLITE_OK)) {
1350 + error_report("Failed to prepare statement store chart labels");
1351 + return;
1352 + }
1353 }
1354
1355 rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC);
@@ -1379,8 +1381,8 @@ void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, cha
1381 error_report("Failed to store chart label entry, rc = %d", rc);
1382
1383 failed:
1382 - if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
1383 - error_report("Failed to finalize the prepared statement when storing chart label information");
1384 + if (unlikely(sqlite3_reset(res) != SQLITE_OK))
1385 + error_report("Failed to reset the prepared statement when storing chart label information");
1386
1387 return;
1388 }
@@ -1881,7 +1883,7 @@ failed:
1883
1884 int get_host_id(uuid_t *node_id, uuid_t *host_id)
1885 {
1884 - sqlite3_stmt *res = NULL;
1886 + static __thread sqlite3_stmt *res = NULL;
1887 int rc;
1888
1889 if (unlikely(!db_meta)) {
@@ -1890,10 +1892,12 @@ int get_host_id(uuid_t *node_id, uuid_t *host_id)
1892 return 1;
1893 }
1894
1893 - rc = sqlite3_prepare_v2(db_meta, SQL_SELECT_HOST_BY_NODE_ID, -1, &res, 0);
1894 - if (unlikely(rc != SQLITE_OK)) {
1895 - error_report("Failed to prepare statement to select node instance information for a node");
1896 - return 1;
1895 + if (unlikely(!res)) {
1896 + rc = prepare_statement(db_meta, SQL_SELECT_HOST_BY_NODE_ID, &res);
1897 + if (unlikely(rc != SQLITE_OK)) {
1898 + error_report("Failed to prepare statement to select node instance information for a node");
1899 + return 1;
1900 + }
1901 }
1902
1903 rc = sqlite3_bind_blob(res, 1, node_id, sizeof(*node_id), SQLITE_STATIC);
@@ -1907,8 +1911,8 @@ int get_host_id(uuid_t *node_id, uuid_t *host_id)
1911 uuid_copy(*host_id, *((uuid_t *) sqlite3_column_blob(res, 0)));
1912
1913 failed:
1910 - if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
1911 - error_report("Failed to finalize the prepared statement when selecting node instance information");
1914 + if (unlikely(sqlite3_reset(res) != SQLITE_OK))
1915 + error_report("Failed to reset the prepared statement when selecting node instance information");
1916
1917 return (rc == SQLITE_ROW) ? 0 : -1;
1918 }
@@ -2060,7 +2064,7 @@ failed:
2064
2065 void sql_load_node_id(RRDHOST *host)
2066 {
2063 - sqlite3_stmt *res = NULL;
2067 + static __thread sqlite3_stmt *res = NULL;
2068 int rc;
2069
2070 if (unlikely(!db_meta)) {
@@ -2069,11 +2073,13 @@ void sql_load_node_id(RRDHOST *host)
2073 return;
2074 }
2075
2072 - rc = sqlite3_prepare_v2(db_meta, SQL_GET_HOST_NODE_ID, -1, &res, 0);
2073 - if (unlikely(rc != SQLITE_OK)) {
2074 - error_report("Failed to prepare statement to fetch node id");
2075 - return;
2076 - };
2076 + if (unlikely(!res)) {
2077 + rc = prepare_statement(db_meta, SQL_GET_HOST_NODE_ID, &res);
2078 + if (unlikely(rc != SQLITE_OK)) {
2079 + error_report("Failed to prepare statement to fetch node id");
2080 + return;
2081 + };
2082 + }
2083
2084 rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC);
2085 if (unlikely(rc != SQLITE_OK)) {
@@ -2090,8 +2096,8 @@ void sql_load_node_id(RRDHOST *host)
2096 }
2097
2098 failed:
2093 - if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
2094 - error_report("Failed to finalize the prepared statement when loading node instance information");
2099 + if (unlikely(sqlite3_reset(res) != SQLITE_OK))
2100 + error_report("Failed to reset the prepared statement when loading node instance information");
2101
2102 return;
2103 };