@cryptotaxi247 / netdata-1 / commits / dd0f7ae99

DBENGINE v2 - improvements part 7 (#14307)

* run cleanup in workers * when there is a discrepancy between update every, fix it * fix the other occurences of metric update every mismatch * allow resetting the same timestamp * validate flushed pages before committing them to disk * initialize collection with the latest time in mrg * these should be static functions * acquire metrics for writing to detect multiple data collections of the same metric * print the uuid of the metric that is collected twice * log the discrepancies of completed pages * 1 second tolerance * unify validation of pages and related logging across dbengine * make do_flush_pages() thread safe * flush pages runs on libuv workers * added uv events to tp workers * dont cross datafile spinlock and rwlock * should be unlock * prevent the creation of multiple datafiles * break an infinite replication loop * do not log the epxansion of the replication window due to start streaming * log all invalid pages with internal checks * do not shutdown event loop threads * add information about collected page events, to find the root cause of invalid collected pages * rewrite of the gap filling to fix the invalid collected pages problem * handle multiple collections of the same metric gracefully * added log about main cache page conflicts; fix gap filling once again... * keep track of the first metric writer * it should be an internal fatal - it does not harm users * do not check of future timestamps on collected pages, since we inherit the clock of the children; do not check collected pages validity without internal checks * prevent negative replication completion percentage * internal error for the discrepancy of mrg * better logging of dbengine new metrics collection * without internal checks it is unused * prevent pluginsd crash on exit due to calling pthread_cancel() on an exited thread * renames and atomics everywhere * if a datafile cannot be acquired for deletion during shutdown, continue - this can happen when there are hot pages in open cache referencing it * Debug for context load * rrdcontext uuid debug * rrddim uuid debug * rrdeng uuid debug * Revert "rrdeng uuid debug" This reverts commit 393da190826a582e7e6cc90771bf91b175826d8b. * Revert "rrddim uuid debug" This reverts commit 72150b30408294f141b19afcfb35abd7c34777d8. * Revert "rrdcontext uuid debug" This reverts commit 2c3b940dc23f460226e9b2a6861c214e840044d0. * Revert "Debug for context load" This reverts commit 0d880fc1589f128524e0b47abd9ff0714283ce3b. * do not use legacy uuids on multihost dbs * thread safety for journafile size * handle other cases of inconsistent collected pages * make health thread check if it should be running in key loops * do not log uuids Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>

Costa Tsaousis committed Jan 23, 2023 at 22:18 UTC dd0f7ae992a8de282c77dc7745c5090e5d65cc28
27 files changed +1039 -618
collectors/plugins.d/plugins_d.c
+78 -45
@@ -21,23 +21,54 @@ inline size_t pluginsd_initialize_plugin_directories()
21 return quoted_strings_splitter(plugins_dir_list, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace, NULL, NULL, 0);
22 }
23
24 +static inline void plugin_set_disabled(struct plugind *cd) {
25 + netdata_spinlock_lock(&cd->unsafe.spinlock);
26 + cd->unsafe.enabled = false;
27 + netdata_spinlock_unlock(&cd->unsafe.spinlock);
28 +}
29 +
30 +bool plugin_is_enabled(struct plugind *cd) {
31 + netdata_spinlock_lock(&cd->unsafe.spinlock);
32 + bool ret = cd->unsafe.enabled;
33 + netdata_spinlock_unlock(&cd->unsafe.spinlock);
34 + return ret;
35 +}
36 +
37 +static inline void plugin_set_running(struct plugind *cd) {
38 + netdata_spinlock_lock(&cd->unsafe.spinlock);
39 + cd->unsafe.running = true;
40 + netdata_spinlock_unlock(&cd->unsafe.spinlock);
41 +}
42 +
43 +static inline bool plugin_is_running(struct plugind *cd) {
44 + netdata_spinlock_lock(&cd->unsafe.spinlock);
45 + bool ret = cd->unsafe.running;
46 + netdata_spinlock_unlock(&cd->unsafe.spinlock);
47 + return ret;
48 +}
49 +
50 static void pluginsd_worker_thread_cleanup(void *arg)
51 {
52 struct plugind *cd = (struct plugind *)arg;
53
28 - if (cd->enabled && !cd->obsolete) {
29 - cd->obsolete = 1;
54 + netdata_spinlock_lock(&cd->unsafe.spinlock);
55 +
56 + cd->unsafe.running = false;
57 + cd->unsafe.thread = 0;
58
59 + pid_t pid = cd->unsafe.pid;
60 + cd->unsafe.pid = 0;
61 +
62 + netdata_spinlock_unlock(&cd->unsafe.spinlock);
63 +
64 + if (pid) {
65 info("data collection thread exiting");
66
33 - if (cd->pid) {
34 - siginfo_t info;
35 - info("killing child process pid %d", cd->pid);
36 - if (killpid(cd->pid) != -1) {
37 - info("waiting for child process pid %d to exit...", cd->pid);
38 - waitid(P_PID, (id_t)cd->pid, &info, WEXITED);
39 - }
40 - cd->pid = 0;
67 + siginfo_t info;
68 + info("killing child process pid %d", pid);
69 + if (killpid(pid) != -1) {
70 + info("waiting for child process pid %d to exit...", pid);
71 + waitid(P_PID, (id_t)pid, &info, WEXITED);
72 }
73 }
74 }
@@ -53,8 +84,8 @@ static void pluginsd_worker_thread_handle_success(struct plugind *cd)
84 if (likely(cd->serial_failures <= SERIAL_FAILURES_THRESHOLD)) {
85 info(
86 "'%s' (pid %d) does not generate useful output but it reports success (exits with 0). %s.",
56 - cd->fullfilename, cd->pid,
57 - cd->enabled ? "Waiting a bit before starting it again." : "Will not start it again - it is now disabled.");
87 + cd->fullfilename, cd->unsafe.pid,
88 + plugin_is_enabled(cd) ? "Waiting a bit before starting it again." : "Will not start it again - it is now disabled.");
89 sleep((unsigned int)(cd->update_every * 10));
90 return;
91 }
@@ -63,35 +94,33 @@ static void pluginsd_worker_thread_handle_success(struct plugind *cd)
94 error(
95 "'%s' (pid %d) does not generate useful output, although it reports success (exits with 0)."
96 "We have tried to collect something %zu times - unsuccessfully. Disabling it.",
66 - cd->fullfilename, cd->pid, cd->serial_failures);
67 - cd->enabled = 0;
97 + cd->fullfilename, cd->unsafe.pid, cd->serial_failures);
98 + plugin_set_disabled(cd);
99 return;
100 }
70 -
71 - return;
101 }
102
103 static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_ret_code)
104 {
105 if (worker_ret_code == -1) {
77 - info("'%s' (pid %d) was killed with SIGTERM. Disabling it.", cd->fullfilename, cd->pid);
78 - cd->enabled = 0;
106 + info("'%s' (pid %d) was killed with SIGTERM. Disabling it.", cd->fullfilename, cd->unsafe.pid);
107 + plugin_set_disabled(cd);
108 return;
109 }
110
111 if (!cd->successful_collections) {
112 error(
113 "'%s' (pid %d) exited with error code %d and haven't collected any data. Disabling it.", cd->fullfilename,
85 - cd->pid, worker_ret_code);
86 - cd->enabled = 0;
114 + cd->unsafe.pid, worker_ret_code);
115 + plugin_set_disabled(cd);
116 return;
117 }
118
119 if (cd->serial_failures <= SERIAL_FAILURES_THRESHOLD) {
120 error(
121 "'%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times). %s",
93 - cd->fullfilename, cd->pid, worker_ret_code, cd->successful_collections,
94 - cd->enabled ? "Waiting a bit before starting it again." : "Will not start it again - it is disabled.");
122 + cd->fullfilename, cd->unsafe.pid, worker_ret_code, cd->successful_collections,
123 + plugin_is_enabled(cd) ? "Waiting a bit before starting it again." : "Will not start it again - it is disabled.");
124 sleep((unsigned int)(cd->update_every * 10));
125 return;
126 }
@@ -100,48 +129,47 @@ static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_r
129 error(
130 "'%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times)."
131 "We tried to restart it %zu times, but it failed to generate data. Disabling it.",
103 - cd->fullfilename, cd->pid, worker_ret_code, cd->successful_collections, cd->serial_failures);
104 - cd->enabled = 0;
132 + cd->fullfilename, cd->unsafe.pid, worker_ret_code, cd->successful_collections, cd->serial_failures);
133 + plugin_set_disabled(cd);
134 return;
135 }
107 -
108 - return;
136 }
137 +
138 #undef SERIAL_FAILURES_THRESHOLD
139
112 -void *pluginsd_worker_thread(void *arg)
140 +static void *pluginsd_worker_thread(void *arg)
141 {
142 worker_register("PLUGINSD");
143
144 netdata_thread_cleanup_push(pluginsd_worker_thread_cleanup, arg);
145
146 struct plugind *cd = (struct plugind *)arg;
147 + plugin_set_running(cd);
148
120 - cd->obsolete = 0;
149 size_t count = 0;
150
151 while (service_running(SERVICE_COLLECTORS)) {
152 FILE *fp_child_input = NULL;
125 - FILE *fp_child_output = netdata_popen(cd->cmd, &cd->pid, &fp_child_input);
153 + FILE *fp_child_output = netdata_popen(cd->cmd, &cd->unsafe.pid, &fp_child_input);
154 if (unlikely(!fp_child_input || !fp_child_output)) {
155 error("Cannot popen(\"%s\", \"r\").", cd->cmd);
156 break;
157 }
158
131 - info("connected to '%s' running on pid %d", cd->fullfilename, cd->pid);
159 + info("connected to '%s' running on pid %d", cd->fullfilename, cd->unsafe.pid);
160 count = pluginsd_process(localhost, cd, fp_child_input, fp_child_output, 0);
133 - error("'%s' (pid %d) disconnected after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count);
134 - killpid(cd->pid);
161 + error("'%s' (pid %d) disconnected after %zu successful data collections (ENDs).", cd->fullfilename, cd->unsafe.pid, count);
162 + killpid(cd->unsafe.pid);
163
136 - int worker_ret_code = netdata_pclose(fp_child_input, fp_child_output, cd->pid);
164 + int worker_ret_code = netdata_pclose(fp_child_input, fp_child_output, cd->unsafe.pid);
165
166 if (likely(worker_ret_code == 0))
167 pluginsd_worker_thread_handle_success(cd);
168 else
169 pluginsd_worker_thread_handle_error(cd, worker_ret_code);
170
143 - cd->pid = 0;
144 - if (unlikely(!cd->enabled))
171 + cd->unsafe.pid = 0;
172 + if (unlikely(!plugin_is_enabled(cd)))
173 break;
174 }
175 worker_unregister();
@@ -158,10 +186,12 @@ static void pluginsd_main_cleanup(void *data)
186
187 struct plugind *cd;
188 for (cd = pluginsd_root; cd; cd = cd->next) {
161 - if (cd->enabled && !cd->obsolete) {
189 + netdata_spinlock_lock(&cd->unsafe.spinlock);
190 + if (cd->unsafe.enabled && cd->unsafe.running && cd->unsafe.thread != 0) {
191 info("stopping plugin thread: %s", cd->id);
163 - netdata_thread_cancel(cd->thread);
192 + netdata_thread_cancel(cd->unsafe.thread);
193 }
194 + netdata_spinlock_unlock(&cd->unsafe.spinlock);
195 }
196
197 info("cleanup completed.");
@@ -237,7 +267,7 @@ void *pluginsd_main(void *ptr)
267 if (unlikely(strcmp(cd->filename, file->d_name) == 0))
268 break;
269
240 - if (likely(cd && !cd->obsolete)) {
270 + if (likely(cd && plugin_is_running(cd))) {
271 debug(D_PLUGINSD, "plugin '%s' is already running", cd->filename);
272 continue;
273 }
@@ -252,7 +282,9 @@ void *pluginsd_main(void *ptr)
282 strncpyz(cd->filename, file->d_name, FILENAME_MAX);
283 snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", directory_name, cd->filename);
284
255 - cd->enabled = enabled;
285 + cd->unsafe.enabled = enabled;
286 + cd->unsafe.running = false;
287 +
288 cd->update_every = (int)config_get_number(cd->id, "update every", localhost->rrd_update_every);
289 cd->started_t = now_realtime_sec();
290
@@ -266,15 +298,16 @@ void *pluginsd_main(void *ptr)
298 cd->next = pluginsd_root;
299 pluginsd_root = cd;
300
269 - // it is not currently running
270 - cd->obsolete = 1;
271 -
272 - if (cd->enabled) {
301 + if (plugin_is_enabled(cd)) {
302 char tag[NETDATA_THREAD_TAG_MAX + 1];
303 snprintfz(tag, NETDATA_THREAD_TAG_MAX, "PD[%s]", pluginname);
304 +
305 // spawn a new thread for it
276 - netdata_thread_create(
277 - &cd->thread, tag, NETDATA_THREAD_OPTION_DEFAULT, pluginsd_worker_thread, cd);
306 + netdata_thread_create(&cd->unsafe.thread,
307 + tag,
308 + NETDATA_THREAD_OPTION_DEFAULT,
309 + pluginsd_worker_thread,
310 + cd);
311 }
312 }
313 }
collectors/plugins.d/plugins_d.h
+8 -5
@@ -50,9 +50,6 @@ struct plugind {
50 char fullfilename[FILENAME_MAX+1]; // with path
51 char cmd[PLUGINSD_CMD_MAX+1]; // the command that it executes
52
53 - volatile pid_t pid;
54 - netdata_thread_t thread;
55 -
53 size_t successful_collections; // the number of times we have seen
54 // values collected from this plugin
55
@@ -60,8 +57,14 @@ struct plugind {
57 // without collecting values
58
59 int update_every; // the plugin default data collection frequency
63 - volatile sig_atomic_t obsolete; // do not touch this structure after setting this to 1
64 - volatile sig_atomic_t enabled; // if this is enabled or not
60 +
61 + struct {
62 + SPINLOCK spinlock;
63 + bool running; // do not touch this structure after setting this to 1
64 + bool enabled; // if this is enabled or not
65 + netdata_thread_t thread;
66 + pid_t pid;
67 + } unsafe;
68
69 time_t started_t;
70 uint32_t capabilities; // follows the same principles as streaming capabilities
collectors/plugins.d/pluginsd_parser.c
+6 -5
@@ -1239,7 +1239,8 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user)
1239 time_t started = st->rrdhost->receiver->replication_first_time_t;
1240 time_t current = ((PARSER_USER_OBJECT *) user)->replay.end_time;
1241
1242 - worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION,
1242 + if(started && current > started)
1243 + worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION,
1244 (NETDATA_DOUBLE)(current - started) * 100.0 / (NETDATA_DOUBLE)(now - started));
1245 }
1246
@@ -1300,10 +1301,10 @@ static void pluginsd_process_thread_cleanup(void *ptr) {
1301
1302 inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugin_input, FILE *fp_plugin_output, int trust_durations)
1303 {
1303 - int enabled = cd->enabled;
1304 + int enabled = cd->unsafe.enabled;
1305
1306 if (!fp_plugin_input || !fp_plugin_output || !enabled) {
1306 - cd->enabled = 0;
1307 + cd->unsafe.enabled = 0;
1308 return 0;
1309 }
1310
@@ -1323,7 +1324,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugi
1324 clearerr(fp_plugin_output);
1325
1326 PARSER_USER_OBJECT user = {
1326 - .enabled = cd->enabled,
1327 + .enabled = cd->unsafe.enabled,
1328 .host = host,
1329 .cd = cd,
1330 .trust_durations = trust_durations
@@ -1348,7 +1349,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugi
1349 // free parser with the pop function
1350 netdata_thread_cleanup_pop(1);
1351
1351 - cd->enabled = user.enabled;
1352 + cd->unsafe.enabled = user.enabled;
1353 size_t count = user.count;
1354
1355 if (likely(count)) {
daemon/event_loop.c
+5 -10
@@ -13,15 +13,9 @@ void register_libuv_worker_jobs() {
13 registered = true;
14
15 worker_register("LIBUV");
16 - worker_register_job_name(UV_EVENT_READ_PAGE_CB, "read page cb");
17 - worker_register_job_name(UV_EVENT_READ_EXTENT_CB, "read extent cb");
18 - worker_register_job_name(UV_EVENT_COMMIT_PAGE_CB, "commit cb");
19 - worker_register_job_name(UV_EVENT_FLUSH_PAGES_CB, "flush cb");
16 worker_register_job_name(UV_EVENT_PAGE_LOOKUP, "page lookup");
17 worker_register_job_name(UV_EVENT_PAGE_POPULATION, "populate page");
18 worker_register_job_name(UV_EVENT_EXT_DECOMPRESSION, "extent decompression");
23 - worker_register_job_name(UV_EVENT_READ_MMAP_EXTENT, "read extent (mmap)");
24 - worker_register_job_name(UV_EVENT_EXTENT_PROCESSING, "extent processing");
19 worker_register_job_name(UV_EVENT_METADATA_STORE, "store host metadata");
20 worker_register_job_name(UV_EVENT_JOURNAL_INDEX_WAIT, "journal v2 wait");
21 worker_register_job_name(UV_EVENT_JOURNAL_INDEX, "journal v2 indexing");
@@ -29,12 +23,8 @@ void register_libuv_worker_jobs() {
23 worker_register_job_name(UV_EVENT_METADATA_CLEANUP, "metadata cleanup");
24 worker_register_job_name(UV_EVENT_EXTENT_CACHE, "extent cache");
25 worker_register_job_name(UV_EVENT_EXTENT_MMAP, "extent mmap");
32 - worker_register_job_name(UV_EVENT_PAGE_DISPATCH, "dispatch page list");
33 - worker_register_job_name(UV_EVENT_FLUSH_CALLBACK, "flush callback");
26 worker_register_job_name(UV_EVENT_FLUSH_MAIN, "flush main");
35 - worker_register_job_name(UV_EVENT_FLUSH_OPEN, "flush open");
27 worker_register_job_name(UV_EVENT_EVICT_MAIN, "evict main");
37 - worker_register_job_name(UV_EVENT_DELETING_FILE, "delete datafiles");
28 worker_register_job_name(UV_EVENT_ANALYZE_V2, "analyze journalfile");
29 worker_register_job_name(UV_EVENT_RETENTION_V2, "calculate retention");
30 worker_register_job_name(UV_EVENT_RETENTION_UPDATE, "update retention");
@@ -43,6 +33,11 @@ void register_libuv_worker_jobs() {
33 worker_register_job_name(UV_EVENT_FLUSHED_TO_OPEN, "flushed to open");
34 worker_register_job_name(UV_EVENT_PREP_QUERY, "prep query");
35 worker_register_job_name(UV_EVENT_WORKER_INIT, "worker init");
36 + worker_register_job_name(UV_EVENT_FLUSH_PAGES, "flush pages");
37 + worker_register_job_name(UV_EVENT_BUFFERS_CLEANUP, "buffers cleanup");
38 + worker_register_job_name(UV_EVENT_QUIESCE, "quiesce");
39 + worker_register_job_name(UV_EVENT_POPULATE_MRG, "populate mrg");
40 + worker_register_job_name(UV_EVENT_SHUTDOWN, "shutdown");
41
42 uv_thread_set_name_np(pthread_self(), "LIBUV_WORKER");
43 }
daemon/event_loop.h
+5 -11
@@ -5,15 +5,9 @@
5
6 enum event_loop_job {
7 UV_EVENT_JOB_NONE = 0,
8 - UV_EVENT_READ_PAGE_CB,
9 - UV_EVENT_READ_EXTENT_CB,
10 - UV_EVENT_COMMIT_PAGE_CB,
11 - UV_EVENT_FLUSH_PAGES_CB,
8 UV_EVENT_EXT_DECOMPRESSION,
9 UV_EVENT_PAGE_LOOKUP,
10 UV_EVENT_PAGE_POPULATION,
15 - UV_EVENT_READ_MMAP_EXTENT,
16 - UV_EVENT_EXTENT_PROCESSING,
11 UV_EVENT_METADATA_STORE,
12 UV_EVENT_JOURNAL_INDEX_WAIT,
13 UV_EVENT_JOURNAL_INDEX,
@@ -21,13 +15,8 @@ enum event_loop_job {
15 UV_EVENT_METADATA_CLEANUP,
16 UV_EVENT_EXTENT_CACHE,
17 UV_EVENT_EXTENT_MMAP,
24 - UV_EVENT_FLUSH_CALLBACK,
25 - UV_EVENT_EXTEXT_DISPATCH,
18 UV_EVENT_FLUSH_MAIN,
27 - UV_EVENT_FLUSH_OPEN,
19 UV_EVENT_EVICT_MAIN,
29 - UV_EVENT_PAGE_DISPATCH,
30 - UV_EVENT_DELETING_FILE,
20 UV_EVENT_ANALYZE_V2,
21 UV_EVENT_RETENTION_V2,
22 UV_EVENT_RETENTION_UPDATE,
@@ -36,6 +25,11 @@ enum event_loop_job {
25 UV_EVENT_FLUSHED_TO_OPEN,
26 UV_EVENT_PREP_QUERY,
27 UV_EVENT_WORKER_INIT,
28 + UV_EVENT_FLUSH_PAGES,
29 + UV_EVENT_BUFFERS_CLEANUP,
30 + UV_EVENT_QUIESCE,
31 + UV_EVENT_POPULATE_MRG,
32 + UV_EVENT_SHUTDOWN,
33 };
34
35 void register_libuv_worker_jobs();
daemon/main.c
+4
@@ -72,10 +72,12 @@ SERVICE_THREAD *service_register(SERVICE_THREAD_TYPE thread_type, request_quit_t
72 *PValue = sth;
73
74 switch(thread_type) {
75 + default:
76 case SERVICE_THREAD_TYPE_NETDATA:
77 sth->netdata_thread = netdata_thread_self();
78 break;
79
80 + case SERVICE_THREAD_TYPE_EVENT_LOOP:
81 case SERVICE_THREAD_TYPE_LIBUV:
82 sth->uv_thread = uv_thread_self();
83 break;
@@ -197,10 +199,12 @@ static bool service_wait_exit(SERVICE_TYPE service, usec_t timeout_ut) {
199 sth->cancelled = true;
200
201 switch(sth->type) {
202 + default:
203 case SERVICE_THREAD_TYPE_NETDATA:
204 netdata_thread_cancel(sth->netdata_thread);
205 break;
206
207 + case SERVICE_THREAD_TYPE_EVENT_LOOP:
208 case SERVICE_THREAD_TYPE_LIBUV:
209 break;
210 }
daemon/main.h
+1
@@ -48,6 +48,7 @@ typedef enum {
48 typedef enum {
49 SERVICE_THREAD_TYPE_NETDATA,
50 SERVICE_THREAD_TYPE_LIBUV,
51 + SERVICE_THREAD_TYPE_EVENT_LOOP,
52 } SERVICE_THREAD_TYPE;
53
54 typedef void (*force_quit_t)(void *data);
database/engine/cache.c
+1 -1
@@ -1975,7 +1975,7 @@ void pgc_page_hot_set_end_time_s(PGC *cache __maybe_unused, PGC_PAGE *page, time
1975 internal_fatal(!is_page_hot(page),
1976 "DBENGINE CACHE: end_time_s update on non-hot page");
1977
1978 - internal_fatal(end_time_s <= __atomic_load_n(&page->end_time_s, __ATOMIC_RELAXED),
1978 + internal_fatal(end_time_s < __atomic_load_n(&page->end_time_s, __ATOMIC_RELAXED),
1979 "DBENGINE CACHE: end_time_s is not bigger than existing");
1980
1981 __atomic_store_n(&page->end_time_s, end_time_s, __ATOMIC_RELAXED);
database/engine/datafile.c
+20 -30
@@ -183,8 +183,7 @@ int close_data_file(struct rrdengine_datafile *datafile)
183 ret = uv_fs_close(NULL, &req, datafile->file, NULL);
184 if (ret < 0) {
185 error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
186 - ++ctx->stats.fs_errors;
187 - rrd_stat_atomic_add(&global_fs_errors, 1);
186 + ctx_fs_error(ctx);
187 }
188 uv_fs_req_cleanup(&req);
189
@@ -203,12 +202,11 @@ int unlink_data_file(struct rrdengine_datafile *datafile)
202 ret = uv_fs_unlink(NULL, &req, path, NULL);
203 if (ret < 0) {
204 error("DBENGINE: uv_fs_fsunlink(%s): %s", path, uv_strerror(ret));
206 - ++ctx->stats.fs_errors;
207 - rrd_stat_atomic_add(&global_fs_errors, 1);
205 + ctx_fs_error(ctx);
206 }
207 uv_fs_req_cleanup(&req);
208
211 - ++ctx->stats.datafile_deletions;
209 + __atomic_add_fetch(&ctx->stats.datafile_deletions, 1, __ATOMIC_RELAXED);
210
211 return ret;
212 }
@@ -225,28 +223,25 @@ int destroy_data_file_unsafe(struct rrdengine_datafile *datafile)
223 ret = uv_fs_ftruncate(NULL, &req, datafile->file, 0, NULL);
224 if (ret < 0) {
225 error("DBENGINE: uv_fs_ftruncate(%s): %s", path, uv_strerror(ret));
228 - ++ctx->stats.fs_errors;
229 - rrd_stat_atomic_add(&global_fs_errors, 1);
226 + ctx_fs_error(ctx);
227 }
228 uv_fs_req_cleanup(&req);
229
230 ret = uv_fs_close(NULL, &req, datafile->file, NULL);
231 if (ret < 0) {
232 error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
236 - ++ctx->stats.fs_errors;
237 - rrd_stat_atomic_add(&global_fs_errors, 1);
233 + ctx_fs_error(ctx);
234 }
235 uv_fs_req_cleanup(&req);
236
237 ret = uv_fs_unlink(NULL, &req, path, NULL);
238 if (ret < 0) {
239 error("DBENGINE: uv_fs_fsunlink(%s): %s", path, uv_strerror(ret));
244 - ++ctx->stats.fs_errors;
245 - rrd_stat_atomic_add(&global_fs_errors, 1);
240 + ctx_fs_error(ctx);
241 }
242 uv_fs_req_cleanup(&req);
243
249 - ++ctx->stats.datafile_deletions;
244 + __atomic_add_fetch(&ctx->stats.datafile_deletions, 1, __ATOMIC_RELAXED);
245
246 return ret;
247 }
@@ -264,12 +259,11 @@ int create_data_file(struct rrdengine_datafile *datafile)
259 generate_datafilepath(datafile, path, sizeof(path));
260 fd = open_file_direct_io(path, O_CREAT | O_RDWR | O_TRUNC, &file);
261 if (fd < 0) {
267 - ++ctx->stats.fs_errors;
268 - rrd_stat_atomic_add(&global_fs_errors, 1);
262 + ctx_fs_error(ctx);
263 return fd;
264 }
265 datafile->file = file;
272 - ++ctx->stats.datafile_creations;
266 + __atomic_add_fetch(&ctx->stats.datafile_creations, 1, __ATOMIC_RELAXED);
267
268 ret = posix_memalign((void *)&superblock, RRDFILE_ALIGNMENT, sizeof(*superblock));
269 if (unlikely(ret)) {
@@ -286,8 +280,7 @@ int create_data_file(struct rrdengine_datafile *datafile)
280 if (ret < 0) {
281 fatal_assert(req.result < 0);
282 error("DBENGINE: uv_fs_write: %s", uv_strerror(ret));
289 - ++ctx->stats.io_errors;
290 - rrd_stat_atomic_add(&global_io_errors, 1);
283 + ctx_io_error(ctx);
284 }
285 uv_fs_req_cleanup(&req);
286 posix_memfree(superblock);
@@ -297,8 +290,7 @@ int create_data_file(struct rrdengine_datafile *datafile)
290 }
291
292 datafile->pos = sizeof(*superblock);
300 - ctx->stats.io_write_bytes += sizeof(*superblock);
301 - ++ctx->stats.io_write_requests;
293 + ctx_io_write_op_bytes(ctx, sizeof(*superblock));
294
295 return 0;
296 }
@@ -350,8 +342,7 @@ static int load_data_file(struct rrdengine_datafile *datafile)
342 generate_datafilepath(datafile, path, sizeof(path));
343 fd = open_file_direct_io(path, O_RDWR, &file);
344 if (fd < 0) {
353 - ++ctx->stats.fs_errors;
354 - rrd_stat_atomic_add(&global_fs_errors, 1);
345 + ctx_fs_error(ctx);
346 return fd;
347 }
348 info("DBENGINE: initializing data file \"%s\".", path);
@@ -364,8 +355,8 @@ static int load_data_file(struct rrdengine_datafile *datafile)
355 ret = check_data_file_superblock(file);
356 if (ret)
357 goto error;
367 - ctx->stats.io_read_bytes += sizeof(struct rrdeng_df_sb);
368 - ++ctx->stats.io_read_requests;
358 +
359 + ctx_io_read_op_bytes(ctx, sizeof(struct rrdeng_df_sb));
360
361 datafile->file = file;
362 datafile->pos = file_size;
@@ -378,8 +369,7 @@ static int load_data_file(struct rrdengine_datafile *datafile)
369 ret = uv_fs_close(NULL, &req, file, NULL);
370 if (ret < 0) {
371 error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
381 - ++ctx->stats.fs_errors;
382 - rrd_stat_atomic_add(&global_fs_errors, 1);
372 + ctx_fs_error(ctx);
373 }
374 uv_fs_req_cleanup(&req);
375 return error;
@@ -412,8 +402,7 @@ static int scan_data_files(struct rrdengine_instance *ctx)
402 fatal_assert(req.result < 0);
403 uv_fs_req_cleanup(&req);
404 error("DBENGINE: uv_fs_scandir(%s): %s", ctx->config.dbfiles_path, uv_strerror(ret));
415 - ++ctx->stats.fs_errors;
416 - rrd_stat_atomic_add(&global_fs_errors, 1);
405 + ctx_fs_error(ctx);
406 return ret;
407 }
408 info("DBENGINE: found %d files in path %s", ret, ctx->config.dbfiles_path);
@@ -474,8 +463,8 @@ static int scan_data_files(struct rrdengine_instance *ctx)
463 continue;
464 }
465
466 + ctx_current_disk_space_increase(ctx, datafile->pos + journalfile->unsafe.pos);
467 datafile_list_insert(ctx, datafile);
478 - ctx_current_disk_space_increase(ctx, datafile->pos + journalfile->pos);
468 }
469 matched_files -= failed_to_load;
470 freez(datafiles);
@@ -511,8 +500,8 @@ int create_new_datafile_pair(struct rrdengine_instance *ctx)
500 journalfile_v1_generate_path(datafile, path, sizeof(path));
501 info("DBENGINE: created journal file \"%s\".", path);
502
503 + ctx_current_disk_space_increase(ctx, datafile->pos + journalfile->unsafe.pos);
504 datafile_list_insert(ctx, datafile);
515 - ctx_current_disk_space_increase(ctx, datafile->pos + journalfile->pos);
505 ctx_last_fileno_increment(ctx);
506
507 return 0;
@@ -576,7 +565,8 @@ void finalize_data_files(struct rrdengine_instance *ctx)
565 }
566
567 logged = false;
579 - while(!datafile_acquire_for_deletion(datafile) && datafile != ctx->datafiles.first->prev) {
568 + size_t iterations = 100;
569 + while(!datafile_acquire_for_deletion(datafile) && datafile != ctx->datafiles.first->prev && --iterations > 0) {
570 if(!logged) {
571 info("Waiting to acquire data file %u of tier %d to close it...", datafile->fileno, ctx->config.tier);
572 logged = true;
database/engine/journalfile.c
+48 -60
@@ -52,7 +52,7 @@ static void update_metric_retention_and_granularity_by_uuid(
52 mrg_metric_release(main_mrg, metric);
53 }
54
55 -static void wal_flush_transaction_buffer_cb(uv_fs_t* req)
55 +static void after_extent_write_journalfile_v1_io(uv_fs_t* req)
56 {
57 worker_is_busy(RRDENG_FLUSH_TRANSACTION_BUFFER_CB);
58
@@ -62,8 +62,7 @@ static void wal_flush_transaction_buffer_cb(uv_fs_t* req)
62
63 debug(D_RRDENGINE, "%s: Journal block was written to disk.", __func__);
64 if (req->result < 0) {
65 - ++ctx->stats.io_errors;
66 - rrd_stat_atomic_add(&global_io_errors, 1);
65 + ctx_io_error(ctx);
66 error("DBENGINE: %s: uv_fs_write: %s", __func__, uv_strerror((int)req->result));
67 } else {
68 debug(D_RRDENGINE, "%s: Journal block was written to disk.", __func__);
@@ -78,7 +77,7 @@ static void wal_flush_transaction_buffer_cb(uv_fs_t* req)
77 }
78
79 /* Careful to always call this before creating a new journal file */
81 -void wal_flush_transaction_buffer(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, WAL *wal, uv_loop_t *loop)
80 +void journalfile_v1_extent_write(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, WAL *wal, uv_loop_t *loop)
81 {
82 int ret;
83 struct generic_io_descriptor *io_descr;
@@ -92,19 +91,23 @@ void wal_flush_transaction_buffer(struct rrdengine_instance *ctx, struct rrdengi
91 }
92 io_descr->buf = wal->buf;
93 io_descr->bytes = wal->buf_size;
95 - io_descr->pos = journalfile->pos;
94 +
95 + netdata_spinlock_lock(&journalfile->unsafe.spinlock);
96 + io_descr->pos = journalfile->unsafe.pos;
97 + journalfile->unsafe.pos += wal->buf_size;
98 + netdata_spinlock_unlock(&journalfile->unsafe.spinlock);
99 +
100 io_descr->req.data = wal;
101 io_descr->data = journalfile;
102 io_descr->completion = NULL;
103
104 io_descr->iov = uv_buf_init((void *)io_descr->buf, wal->buf_size);
105 ret = uv_fs_write(loop, &io_descr->req, journalfile->file, &io_descr->iov, 1,
102 - journalfile->pos, wal_flush_transaction_buffer_cb);
106 + (int64_t)io_descr->pos, after_extent_write_journalfile_v1_io);
107 fatal_assert(-1 != ret);
104 - journalfile->pos += wal->buf_size;
108 +
109 ctx_current_disk_space_increase(ctx, wal->buf_size);
106 - ctx->stats.io_write_bytes += wal->buf_size;
107 - ++ctx->stats.io_write_requests;
110 + ctx_io_write_op_bytes(ctx, wal->buf_size);
111 }
112
113 void journalfile_v2_generate_path(struct rrdengine_datafile *datafile, char *str, size_t maxlen)
@@ -137,8 +140,7 @@ static struct journal_v2_header *journalfile_v2_mounted_data_get(struct rrdengin
140 journalfile->v2.flags &= ~(JOURNALFILE_FLAG_IS_AVAILABLE | JOURNALFILE_FLAG_IS_MOUNTED);
141 netdata_spinlock_unlock(&journalfile->v2.spinlock);
142
140 - ++journalfile->datafile->ctx->stats.fs_errors;
141 - rrd_stat_atomic_add(&global_fs_errors, 1);
143 + ctx_fs_error(journalfile->datafile->ctx);
144 }
145 else {
146 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.journal_v2_mapped, 1, __ATOMIC_RELAXED);
@@ -194,8 +196,7 @@ static bool journalfile_v2_mounted_data_unmount(struct rrdengine_journalfile *jo
196 journalfile_v2_generate_path(journalfile->datafile, path, sizeof(path));
197 error("DBENGINE: failed to unmap index file '%s'", path);
198 internal_fatal(true, "DBENGINE: failed to unmap file '%s'", path);
197 - ++journalfile->datafile->ctx->stats.fs_errors;
198 - rrd_stat_atomic_add(&global_fs_errors, 1);
199 + ctx_fs_error(journalfile->datafile->ctx);
200 }
201 else {
202 __atomic_add_fetch(&rrdeng_cache_efficiency_stats.journal_v2_unmapped, 1, __ATOMIC_RELAXED);
@@ -385,6 +386,7 @@ struct rrdengine_journalfile *journalfile_alloc_and_init(struct rrdengine_datafi
386 journalfile->datafile = datafile;
387 netdata_spinlock_init(&journalfile->mmap.spinlock);
388 netdata_spinlock_init(&journalfile->v2.spinlock);
389 + netdata_spinlock_init(&journalfile->unsafe.spinlock);
390 journalfile->mmap.fd = -1;
391 datafile->journalfile = journalfile;
392 return journalfile;
@@ -400,8 +402,7 @@ static int close_uv_file(struct rrdengine_datafile *datafile, uv_file file)
402 if (ret < 0) {
403 journalfile_v1_generate_path(datafile, path, sizeof(path));
404 error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
403 - ++datafile->ctx->stats.fs_errors;
404 - rrd_stat_atomic_add(&global_fs_errors, 1);
405 + ctx_fs_error(datafile->ctx);
406 }
407 uv_fs_req_cleanup(&req);
408 return ret;
@@ -430,12 +431,11 @@ int journalfile_unlink(struct rrdengine_journalfile *journalfile)
431 ret = uv_fs_unlink(NULL, &req, path, NULL);
432 if (ret < 0) {
433 error("DBENGINE: uv_fs_fsunlink(%s): %s", path, uv_strerror(ret));
433 - ++ctx->stats.fs_errors;
434 - rrd_stat_atomic_add(&global_fs_errors, 1);
434 + ctx_fs_error(ctx);
435 }
436 uv_fs_req_cleanup(&req);
437
438 - ++ctx->stats.journalfile_deletions;
438 + __atomic_add_fetch(&ctx->stats.journalfile_deletions, 1, __ATOMIC_RELAXED);
439
440 return ret;
441 }
@@ -455,8 +455,7 @@ int journalfile_destroy_unsafe(struct rrdengine_journalfile *journalfile, struct
455 ret = uv_fs_ftruncate(NULL, &req, journalfile->file, 0, NULL);
456 if (ret < 0) {
457 error("DBENGINE: uv_fs_ftruncate(%s): %s", path, uv_strerror(ret));
458 - ++ctx->stats.fs_errors;
459 - rrd_stat_atomic_add(&global_fs_errors, 1);
458 + ctx_fs_error(ctx);
459 }
460 uv_fs_req_cleanup(&req);
461 (void) close_uv_file(datafile, journalfile->file);
@@ -466,21 +465,18 @@ int journalfile_destroy_unsafe(struct rrdengine_journalfile *journalfile, struct
465 ret = uv_fs_unlink(NULL, &req, path_v2, NULL);
466 if (ret < 0) {
467 error("DBENGINE: uv_fs_fsunlink(%s): %s", path, uv_strerror(ret));
469 - ++ctx->stats.fs_errors;
470 - rrd_stat_atomic_add(&global_fs_errors, 1);
468 + ctx_fs_error(ctx);
469 }
470 uv_fs_req_cleanup(&req);
471
472 ret = uv_fs_unlink(NULL, &req, path, NULL);
473 if (ret < 0) {
474 error("DBENGINE: uv_fs_fsunlink(%s): %s", path, uv_strerror(ret));
477 - ++ctx->stats.fs_errors;
478 - rrd_stat_atomic_add(&global_fs_errors, 1);
475 + ctx_fs_error(ctx);
476 }
477 uv_fs_req_cleanup(&req);
478
482 - ++ctx->stats.journalfile_deletions;
483 - ++ctx->stats.journalfile_deletions;
479 + __atomic_add_fetch(&ctx->stats.journalfile_deletions, 2, __ATOMIC_RELAXED);
480
481 if(journalfile_v2_data_available(journalfile))
482 journalfile_v2_data_unmap_permanently(journalfile);
@@ -501,12 +497,11 @@ int journalfile_create(struct rrdengine_journalfile *journalfile, struct rrdengi
497 journalfile_v1_generate_path(datafile, path, sizeof(path));
498 fd = open_file_direct_io(path, O_CREAT | O_RDWR | O_TRUNC, &file);
499 if (fd < 0) {
504 - ++ctx->stats.fs_errors;
505 - rrd_stat_atomic_add(&global_fs_errors, 1);
500 + ctx_fs_error(ctx);
501 return fd;
502 }
503 journalfile->file = file;
509 - ++ctx->stats.journalfile_creations;
504 + __atomic_add_fetch(&ctx->stats.journalfile_creations, 1, __ATOMIC_RELAXED);
505
506 ret = posix_memalign((void *)&superblock, RRDFILE_ALIGNMENT, sizeof(*superblock));
507 if (unlikely(ret)) {
@@ -522,8 +517,7 @@ int journalfile_create(struct rrdengine_journalfile *journalfile, struct rrdengi
517 if (ret < 0) {
518 fatal_assert(req.result < 0);
519 error("DBENGINE: uv_fs_write: %s", uv_strerror(ret));
525 - ++ctx->stats.io_errors;
526 - rrd_stat_atomic_add(&global_io_errors, 1);
520 + ctx_io_error(ctx);
521 }
522 uv_fs_req_cleanup(&req);
523 posix_memfree(superblock);
@@ -532,9 +526,9 @@ int journalfile_create(struct rrdengine_journalfile *journalfile, struct rrdengi
526 return ret;
527 }
528
535 - journalfile->pos = sizeof(*superblock);
536 - ctx->stats.io_write_bytes += sizeof(*superblock);
537 - ++ctx->stats.io_write_requests;
529 + journalfile->unsafe.pos = sizeof(*superblock);
530 +
531 + ctx_io_write_op_bytes(ctx, sizeof(*superblock));
532
533 return 0;
534 }
@@ -588,7 +582,7 @@ static void journalfile_restore_extent_metadata(struct rrdengine_instance *ctx,
582 return;
583 }
584
591 - time_t now_s = now_realtime_sec();
585 + time_t now_s = max_acceptable_collected_time();
586 for (i = 0; i < count ; ++i) {
587 uuid_t *temp_id;
588 uint8_t page_type = jf_metric_data->descr[i].type;
@@ -610,7 +604,7 @@ static void journalfile_restore_extent_metadata(struct rrdengine_instance *ctx,
604 (metric) ? mrg_metric_get_update_every_s(main_mrg, metric) : 0,
605 false);
606
613 - if(!vd.data_on_disk_valid) {
607 + if(!vd.is_valid) {
608 mrg_metric_release(main_mrg, metric);
609 continue;
610 }
@@ -717,7 +711,7 @@ static uint64_t journalfile_iterate_transactions(struct rrdengine_instance *ctx,
711 uv_fs_t req;
712
713 file = journalfile->file;
720 - file_size = journalfile->pos;
714 + file_size = journalfile->unsafe.pos;
715 //data_file_size = journalfile->datafile->pos; TODO: utilize this?
716
717 max_id = 1;
@@ -741,8 +735,7 @@ static uint64_t journalfile_iterate_transactions(struct rrdengine_instance *ctx,
735 }
736 fatal_assert(req.result >= 0);
737 uv_fs_req_cleanup(&req);
744 - ++ctx->stats.io_read_requests;
745 - ctx->stats.io_read_bytes += size_bytes;
738 + ctx_io_read_op_bytes(ctx, size_bytes);
739 }
740
741 for (pos_i = 0 ; pos_i < size_bytes ; ) {
@@ -922,7 +915,7 @@ void journalfile_v2_populate_retention_to_mrg(struct rrdengine_instance *ctx, st
915
916 struct journal_metric_list *metric = (struct journal_metric_list *) (data_start + j2_header->metric_offset);
917 time_t header_start_time_s = (time_t) (j2_header->start_time_ut / USEC_PER_SEC);
925 - time_t now_s = now_realtime_sec();
918 + time_t now_s = max_acceptable_collected_time();
919 for (size_t i=0; i < entries; i++) {
920 time_t start_time_s = header_start_time_s + metric->delta_start_s;
921 time_t end_time_s = header_start_time_s + metric->delta_end_s;
@@ -968,8 +961,7 @@ int journalfile_v2_load(struct rrdengine_instance *ctx, struct rrdengine_journal
961 if (fd < 0) {
962 if (errno == ENOENT)
963 return 1;
971 - ++ctx->stats.fs_errors;
972 - rrd_stat_atomic_add(&global_fs_errors, 1);
964 + ctx_fs_error(ctx);
965 error("DBENGINE: failed to open '%s'", path_v2);
966 return 1;
967 }
@@ -1200,7 +1192,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1192 number_of_pages);
1193
1194 #ifdef NETDATA_INTERNAL_CHECKS
1203 - usec_t start_loading = now_realtime_usec();
1195 + usec_t start_loading = now_monotonic_usec();
1196 #endif
1197
1198 size_t total_file_size = 0;
@@ -1251,13 +1243,13 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1243 j2_header.extent_trailer_offset = extent_offset_trailer;
1244 j2_header.metric_trailer_offset = metric_offset_trailer;
1245 j2_header.journal_v2_file_size = total_file_size;
1254 - j2_header.journal_v1_file_size = (uint32_t) journalfile->pos;
1246 + j2_header.journal_v1_file_size = (uint32_t)journalfile_current_size(journalfile);
1247 j2_header.data = data_start; // Used during migration
1248
1249 struct journal_v2_block_trailer *journal_v2_trailer;
1250
1251 data = journalfile_v2_write_extent_list(JudyL_extents_pos, data_start + extent_offset);
1260 - internal_error(true, "DBENGINE: write extent list so far %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1252 + internal_error(true, "DBENGINE: write extent list so far %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1253
1254 fatal_assert(data == data_start + extent_offset_trailer);
1255
@@ -1268,7 +1260,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1260 crc = crc32(crc, (uint8_t *) data_start + extent_offset, number_of_extents * sizeof(struct journal_extent_list));
1261 crc32set(journal_v2_trailer->checksum, crc);
1262
1271 - internal_error(true, "DBENGINE: CALCULATE CRC FOR EXTENT %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1263 + internal_error(true, "DBENGINE: CALCULATE CRC FOR EXTENT %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1264 // Skip the trailer, point to the metrics off
1265 data += sizeof(struct journal_v2_block_trailer);
1266
@@ -1295,7 +1287,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1287 j2_header.end_time_ut = max_time_s * USEC_PER_SEC;
1288
1289 qsort(&uuid_list[0], number_of_metrics, sizeof(struct journal_metric_list_to_sort), journalfile_metric_compare);
1298 - internal_error(true, "DBENGINE: traverse and qsort UUID %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1290 + internal_error(true, "DBENGINE: traverse and qsort UUID %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1291
1292 uint32_t resize_file_to = total_file_size;
1293
@@ -1341,7 +1333,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1333 }
1334
1335 if (data == data_start + metric_offset_trailer) {
1344 - internal_error(true, "DBENGINE: WRITE METRICS AND PAGES %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1336 + internal_error(true, "DBENGINE: WRITE METRICS AND PAGES %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1337
1338 // Calculate CRC for metrics
1339 journal_v2_trailer = (struct journal_v2_block_trailer *)(data_start + metric_offset_trailer);
@@ -1349,7 +1341,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1341 crc =
1342 crc32(crc, (uint8_t *)data_start + metrics_offset, number_of_metrics * sizeof(struct journal_metric_list));
1343 crc32set(journal_v2_trailer->checksum, crc);
1352 - internal_error(true, "DBENGINE: CALCULATE CRC FOR UUIDs %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1344 + internal_error(true, "DBENGINE: CALCULATE CRC FOR UUIDs %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1345
1346 // Prepare to write checksum for the file
1347 j2_header.data = NULL;
@@ -1361,14 +1353,14 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1353 // Write header to the file
1354 memcpy(data_start, &j2_header, sizeof(j2_header));
1355
1364 - internal_error(true, "DBENGINE: FILE COMPLETED --------> %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1356 + internal_error(true, "DBENGINE: FILE COMPLETED --------> %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1357
1358 info("DBENGINE: migrated journal file '%s', file size %zu", path, total_file_size);
1359
1360 // msync(data_start, total_file_size, MS_SYNC);
1361 journalfile_v2_data_set(journalfile, fd_v2, data_start, total_file_size);
1362
1371 - internal_error(true, "DBENGINE: ACTIVATING NEW INDEX JNL %llu", (now_realtime_usec() - start_loading) / USEC_PER_MS);
1363 + internal_error(true, "DBENGINE: ACTIVATING NEW INDEX JNL %llu", (now_monotonic_usec() - start_loading) / USEC_PER_MS);
1364 ctx_current_disk_space_increase(ctx, total_file_size);
1365 freez(uuid_list);
1366 return;
@@ -1390,8 +1382,7 @@ void journalfile_migrate_to_v2_callback(Word_t section, unsigned datafile_fileno
1382 int ret = truncate(path, (long) resize_file_to);
1383 if (ret < 0) {
1384 ctx_current_disk_space_increase(ctx, total_file_size);
1393 - ++ctx->stats.fs_errors;
1394 - rrd_stat_atomic_add(&global_fs_errors, 1);
1385 + ctx_fs_error(ctx);
1386 error("DBENGINE: failed to resize file '%s'", path);
1387 }
1388 else
@@ -1420,8 +1411,7 @@ int journalfile_load(struct rrdengine_instance *ctx, struct rrdengine_journalfil
1411 // If it is not the last file, open read only
1412 fd = open_file_direct_io(path, O_RDWR, &file);
1413 if (fd < 0) {
1423 - ++ctx->stats.fs_errors;
1424 - rrd_stat_atomic_add(&global_fs_errors, 1);
1414 + ctx_fs_error(ctx);
1415 return fd;
1416 }
1417
@@ -1435,11 +1425,10 @@ int journalfile_load(struct rrdengine_instance *ctx, struct rrdengine_journalfil
1425 info("DBENGINE: invalid journal file '%s' ; superblock check failed.", path);
1426 goto error;
1427 }
1438 - ctx->stats.io_read_bytes += sizeof(struct rrdeng_jf_sb);
1439 - ++ctx->stats.io_read_requests;
1428 + ctx_io_read_op_bytes(ctx, sizeof(struct rrdeng_jf_sb));
1429
1430 journalfile->file = file;
1442 - journalfile->pos = file_size;
1431 + journalfile->unsafe.pos = file_size;
1432
1433 journalfile->data = netdata_mmap(path, file_size, MAP_SHARED, 0, !(datafile->fileno == ctx_last_fileno_get(ctx)), NULL);
1434 info("DBENGINE: loading journal file '%s' using %s.", path, journalfile->data?"MMAP":"uv_fs_read");
@@ -1471,8 +1460,7 @@ error:
1460 ret = uv_fs_close(NULL, &req, file, NULL);
1461 if (ret < 0) {
1462 error("DBENGINE: uv_fs_close(%s): %s", path, uv_strerror(ret));
1474 - ++ctx->stats.fs_errors;
1475 - rrd_stat_atomic_add(&global_fs_errors, 1);
1463 + ctx_fs_error(ctx);
1464 }
1465 uv_fs_req_cleanup(&req);
1466 return error;
database/engine/journalfile.h
+12 -2
@@ -41,12 +41,22 @@ struct rrdengine_journalfile {
41 time_t not_needed_since_s;
42 } v2;
43
44 + struct {
45 + SPINLOCK spinlock;
46 + uint64_t pos;
47 + } unsafe;
48 +
49 uv_file file;
45 - uint64_t pos;
50 void *data;
51 struct rrdengine_datafile *datafile;
52 };
53
54 +static inline uint64_t journalfile_current_size(struct rrdengine_journalfile *journalfile) {
55 + netdata_spinlock_lock(&journalfile->unsafe.spinlock);
56 + uint64_t size = journalfile->unsafe.pos;
57 + netdata_spinlock_unlock(&journalfile->unsafe.spinlock);
58 + return size;
59 +}
60
61 // Journal v2 structures
62
@@ -126,7 +136,7 @@ struct wal;
136 void journalfile_v1_generate_path(struct rrdengine_datafile *datafile, char *str, size_t maxlen);
137 void journalfile_v2_generate_path(struct rrdengine_datafile *datafile, char *str, size_t maxlen);
138 struct rrdengine_journalfile *journalfile_alloc_and_init(struct rrdengine_datafile *datafile);
129 -void wal_flush_transaction_buffer(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, struct wal *wal, uv_loop_t *loop);
139 +void journalfile_v1_extent_write(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, struct wal *wal, uv_loop_t *loop);
140 int journalfile_close(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile);
141 int journalfile_unlink(struct rrdengine_journalfile *journalfile);
142 int journalfile_destroy_unsafe(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile);
database/engine/metric.c
+56 -29
@@ -6,11 +6,13 @@ typedef int32_t REFCOUNT;
6 struct metric {
7 uuid_t uuid; // never changes
8 Word_t section; // never changes
9 +
10 time_t first_time_s; //
11 time_t latest_time_s_clean; // archived pages latest time
12 time_t latest_time_s_hot; // latest time of the currently collected page
13 uint32_t latest_update_every_s; //
13 - SPINLOCK timestamps_lock; // protects the 3 timestamps
14 + pid_t writer;
15 + SPINLOCK spinlock; // protects all variable members
16
17 // THIS IS allocated with malloc()
18 // YOU HAVE TO INITIALIZE IT YOURSELF !
@@ -133,7 +135,8 @@ static METRIC *metric_add(MRG *mrg, MRG_ENTRY *entry, bool *ret) {
135 metric->latest_time_s_clean = entry->last_time_s;
136 metric->latest_time_s_hot = 0;
137 metric->latest_update_every_s = entry->latest_update_every_s;
136 - netdata_spinlock_init(&metric->timestamps_lock);
138 + metric->writer = 0;
139 + netdata_spinlock_init(&metric->spinlock);
140 *PValue = metric;
141
142 mrg_index_write_unlock(mrg, partition);
@@ -241,7 +244,7 @@ void mrg_destroy(MRG *mrg __maybe_unused) {
244 METRIC *mrg_metric_add_and_acquire(MRG *mrg, MRG_ENTRY entry, bool *ret) {
245 // FIXME - support refcount
246
244 -// internal_fatal(entry.latest_time_s > now_realtime_sec(),
247 +// internal_fatal(entry.latest_time_s > max_acceptable_collected_time(),
248 // "DBENGINE METRIC: metric latest time is in the future");
249
250 return metric_add(mrg, &entry, ret);
@@ -280,21 +283,21 @@ Word_t mrg_metric_section(MRG *mrg __maybe_unused, METRIC *metric) {
283 }
284
285 bool mrg_metric_set_first_time_s(MRG *mrg __maybe_unused, METRIC *metric, time_t first_time_s) {
283 - netdata_spinlock_lock(&metric->timestamps_lock);
286 + netdata_spinlock_lock(&metric->spinlock);
287 metric->first_time_s = first_time_s;
285 - netdata_spinlock_unlock(&metric->timestamps_lock);
288 + netdata_spinlock_unlock(&metric->spinlock);
289
290 return true;
291 }
292
293 void mrg_metric_expand_retention(MRG *mrg __maybe_unused, METRIC *metric, time_t first_time_s, time_t last_time_s, time_t update_every_s) {
294
292 - internal_fatal(first_time_s > now_realtime_sec() + 1,
295 + internal_fatal(first_time_s > max_acceptable_collected_time(),
296 "DBENGINE METRIC: metric first time is in the future");
294 - internal_fatal(last_time_s > now_realtime_sec() + 1,
297 + internal_fatal(last_time_s > max_acceptable_collected_time(),
298 "DBENGINE METRIC: metric last time is in the future");
299
297 - netdata_spinlock_lock(&metric->timestamps_lock);
300 + netdata_spinlock_lock(&metric->spinlock);
301
302 if(unlikely(first_time_s && (!metric->first_time_s || first_time_s < metric->first_time_s)))
303 metric->first_time_s = first_time_s;
@@ -308,13 +311,13 @@ void mrg_metric_expand_retention(MRG *mrg __maybe_unused, METRIC *metric, time_t
311 else if(unlikely(!metric->latest_update_every_s && update_every_s))
312 metric->latest_update_every_s = update_every_s;
313
311 - netdata_spinlock_unlock(&metric->timestamps_lock);
314 + netdata_spinlock_unlock(&metric->spinlock);
315 }
316
317 bool mrg_metric_set_first_time_s_if_zero(MRG *mrg __maybe_unused, METRIC *metric, time_t first_time_s) {
318 bool ret = false;
319
317 - netdata_spinlock_lock(&metric->timestamps_lock);
320 + netdata_spinlock_lock(&metric->spinlock);
321 if(!metric->first_time_s) {
322 metric->first_time_s = first_time_s;
323
@@ -326,14 +329,14 @@ bool mrg_metric_set_first_time_s_if_zero(MRG *mrg __maybe_unused, METRIC *metric
329
330 ret = true;
331 }
329 - netdata_spinlock_unlock(&metric->timestamps_lock);
332 + netdata_spinlock_unlock(&metric->spinlock);
333
334 return ret;
335 }
336
337 time_t mrg_metric_get_first_time_s(MRG *mrg __maybe_unused, METRIC *metric) {
338 time_t first_time_s;
336 - netdata_spinlock_lock(&metric->timestamps_lock);
339 + netdata_spinlock_lock(&metric->spinlock);
340 first_time_s = metric->first_time_s;
341 if(!first_time_s) {
342 if(metric->latest_time_s_clean)
@@ -342,15 +345,15 @@ time_t mrg_metric_get_first_time_s(MRG *mrg __maybe_unused, METRIC *metric) {
345 if(!first_time_s || metric->latest_time_s_hot < metric->latest_time_s_clean)
346 first_time_s = metric->latest_time_s_hot;
347 }
345 - netdata_spinlock_unlock(&metric->timestamps_lock);
348 + netdata_spinlock_unlock(&metric->spinlock);
349
350 return first_time_s;
351 }
352
353 bool mrg_metric_set_clean_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric, time_t latest_time_s) {
351 - netdata_spinlock_lock(&metric->timestamps_lock);
354 + netdata_spinlock_lock(&metric->spinlock);
355
353 -// internal_fatal(latest_time_s > now_realtime_sec() + 1,
356 +// internal_fatal(latest_time_s > max_acceptable_collected_time(),
357 // "DBENGINE METRIC: metric latest time is in the future");
358
359 // internal_fatal(metric->latest_time_s_clean > latest_time_s,
@@ -364,15 +367,15 @@ bool mrg_metric_set_clean_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric,
367 // if(unlikely(metric->first_time_s > latest_time_s))
368 // metric->first_time_s = latest_time_s;
369
367 - netdata_spinlock_unlock(&metric->timestamps_lock);
370 + netdata_spinlock_unlock(&metric->spinlock);
371 return true;
372 }
373
374 bool mrg_metric_set_hot_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric, time_t latest_time_s) {
372 -// internal_fatal(latest_time_s > now_realtime_sec(),
375 +// internal_fatal(latest_time_s > max_acceptable_collected_time(),
376 // "DBENGINE METRIC: metric latest time is in the future");
377
375 - netdata_spinlock_lock(&metric->timestamps_lock);
378 + netdata_spinlock_lock(&metric->spinlock);
379 metric->latest_time_s_hot = latest_time_s;
380
381 if(unlikely(!metric->first_time_s))
@@ -381,15 +384,15 @@ bool mrg_metric_set_hot_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric, t
384 // if(unlikely(metric->first_time_s > latest_time_s))
385 // metric->first_time_s = latest_time_s;
386
384 - netdata_spinlock_unlock(&metric->timestamps_lock);
387 + netdata_spinlock_unlock(&metric->spinlock);
388 return true;
389 }
390
391 time_t mrg_metric_get_latest_time_s(MRG *mrg __maybe_unused, METRIC *metric) {
392 time_t max;
390 - netdata_spinlock_lock(&metric->timestamps_lock);
393 + netdata_spinlock_lock(&metric->spinlock);
394 max = MAX(metric->latest_time_s_clean, metric->latest_time_s_hot);
392 - netdata_spinlock_unlock(&metric->timestamps_lock);
395 + netdata_spinlock_unlock(&metric->spinlock);
396 return max;
397 }
398
@@ -397,9 +400,9 @@ bool mrg_metric_set_update_every(MRG *mrg __maybe_unused, METRIC *metric, time_t
400 if(!update_every_s)
401 return false;
402
400 - netdata_spinlock_lock(&metric->timestamps_lock);
403 + netdata_spinlock_lock(&metric->spinlock);
404 metric->latest_update_every_s = update_every_s;
402 - netdata_spinlock_unlock(&metric->timestamps_lock);
405 + netdata_spinlock_unlock(&metric->spinlock);
406
407 return true;
408 }
@@ -408,10 +411,10 @@ bool mrg_metric_set_update_every_s_if_zero(MRG *mrg __maybe_unused, METRIC *metr
411 if(!update_every_s)
412 return false;
413
411 - netdata_spinlock_lock(&metric->timestamps_lock);
414 + netdata_spinlock_lock(&metric->spinlock);
415 if(!metric->latest_update_every_s)
416 metric->latest_update_every_s = update_every_s;
414 - netdata_spinlock_unlock(&metric->timestamps_lock);
417 + netdata_spinlock_unlock(&metric->spinlock);
418
419 return true;
420 }
@@ -419,13 +422,37 @@ bool mrg_metric_set_update_every_s_if_zero(MRG *mrg __maybe_unused, METRIC *metr
422 time_t mrg_metric_get_update_every_s(MRG *mrg __maybe_unused, METRIC *metric) {
423 time_t update_every_s;
424
422 - netdata_spinlock_lock(&metric->timestamps_lock);
425 + netdata_spinlock_lock(&metric->spinlock);
426 update_every_s = metric->latest_update_every_s;
424 - netdata_spinlock_unlock(&metric->timestamps_lock);
427 + netdata_spinlock_unlock(&metric->spinlock);
428
429 return update_every_s;
430 }
431
432 +bool mrg_metric_writer_acquire(MRG *mrg, METRIC *metric) {
433 + bool done = false;
434 + netdata_spinlock_lock(&metric->spinlock);
435 + if(!metric->writer) {
436 + metric->writer = gettid();
437 + __atomic_add_fetch(&mrg->stats.writers, 1, __ATOMIC_RELAXED);
438 + done = true;
439 + }
440 + netdata_spinlock_unlock(&metric->spinlock);
441 + return done;
442 +}
443 +
444 +bool mrg_metric_writer_release(MRG *mrg, METRIC *metric) {
445 + bool done = false;
446 + netdata_spinlock_lock(&metric->spinlock);
447 + if(metric->writer) {
448 + metric->writer = 0;
449 + __atomic_sub_fetch(&mrg->stats.writers, 1, __ATOMIC_RELAXED);
450 + done = true;
451 + }
452 + netdata_spinlock_unlock(&metric->spinlock);
453 + return done;
454 +}
455 +
456 struct mrg_statistics mrg_get_statistics(MRG *mrg) {
457 // FIXME - use atomics
458 return mrg->stats;
@@ -613,7 +640,7 @@ int mrg_unittest(void) {
640 fatal("DBENGINE METRIC: invalid entries counter");
641
642 #ifdef MRG_STRESS_TEST
616 - usec_t started_ut = now_realtime_usec();
643 + usec_t started_ut = now_monotonic_usec();
644 pthread_t thread1;
645 netdata_thread_create(&thread1, "TH1",
646 NETDATA_THREAD_OPTION_JOINABLE | NETDATA_THREAD_OPTION_DONT_LOG,
@@ -639,7 +666,7 @@ int mrg_unittest(void) {
666 netdata_thread_join(thread1, NULL);
667 netdata_thread_join(thread2, NULL);
668 netdata_thread_join(thread3, NULL);
642 - usec_t ended_ut = now_realtime_usec();
669 + usec_t ended_ut = now_monotonic_usec();
670
671 info("DBENGINE METRIC: did %zu additions, %zu duplicate additions, "
672 "%zu deletions, %zu wrong deletions, "
database/engine/metric.h
+4
@@ -27,6 +27,7 @@ struct mrg_statistics {
27 size_t search_misses;
28 size_t pointer_validation_hits;
29 size_t pointer_validation_misses;
30 + size_t writers;
31 };
32
33 MRG *mrg_create(void);
@@ -57,6 +58,9 @@ time_t mrg_metric_get_update_every_s(MRG *mrg, METRIC *metric);
58
59 bool mrg_metric_set_update_every_s_if_zero(MRG *mrg, METRIC *metric, time_t update_every_s);
60
61 +bool mrg_metric_writer_acquire(MRG *mrg, METRIC *metric);
62 +bool mrg_metric_writer_release(MRG *mrg, METRIC *metric);
63 +
64 struct mrg_statistics mrg_get_statistics(MRG *mrg);
65
66 #endif // DBENGINE_METRIC_H
database/engine/pagecache.c
+2 -2
@@ -58,7 +58,7 @@ static void main_cache_flush_dirty_page_callback(PGC *cache __maybe_unused, PGC_
58
59 struct completion completion;
60 completion_init(&completion);
61 - rrdeng_enq_cmd(ctx, RRDENG_OPCODE_FLUSH_PAGES, base, &completion, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
61 + rrdeng_enq_cmd(ctx, RRDENG_OPCODE_EXTENT_WRITE, base, &completion, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
62 completion_wait_for(&completion);
63 completion_destroy(&completion);
64 }
@@ -776,7 +776,7 @@ void pg_cache_preload(struct rrdeng_query_handle *handle) {
776
777 if(ctx_is_available_for_queries(handle->ctx)) {
778 handle->pdc->refcount++; // we get 1 for the query thread and 1 for the prep thread
779 - rrdeng_enq_cmd(handle->ctx, RRDENG_OPCODE_PREP_QUERY, handle->pdc, NULL, handle->priority, NULL, NULL);
779 + rrdeng_enq_cmd(handle->ctx, RRDENG_OPCODE_QUERY, handle->pdc, NULL, handle->priority, NULL, NULL);
780 }
781 else {
782 completion_mark_complete(&handle->pdc->prep_completion);
database/engine/pdc.c
+165 -30
@@ -827,40 +827,115 @@ static void fill_page_with_nulls(void *page, uint32_t page_length, uint8_t type)
827 }
828 }
829
830 +void collect_page_flags_to_buffer(BUFFER *wb, RRDENG_COLLECT_PAGE_FLAGS flags) {
831 + if(flags & RRDENG_PAGE_PAST_COLLECTION)
832 + buffer_strcat(wb, "PAST_COLLECTION ");
833 + if(flags & RRDENG_PAGE_REPEATED_COLLECTION)
834 + buffer_strcat(wb, "REPEATED_COLLECTION ");
835 + if(flags & RRDENG_PAGE_BIG_GAP)
836 + buffer_strcat(wb, "BIG_GAP ");
837 + if(flags & RRDENG_PAGE_GAP)
838 + buffer_strcat(wb, "GAP ");
839 + if(flags & RRDENG_PAGE_FUTURE_POINT)
840 + buffer_strcat(wb, "FUTURE_POINT ");
841 + if(flags & RRDENG_PAGE_CREATED_IN_FUTURE)
842 + buffer_strcat(wb, "CREATED_IN_FUTURE ");
843 + if(flags & RRDENG_PAGE_COMPLETED_IN_FUTURE)
844 + buffer_strcat(wb, "COMPLETED_IN_FUTURE ");
845 + if(flags & RRDENG_PAGE_UNALIGNED)
846 + buffer_strcat(wb, "UNALIGNED ");
847 + if(flags & RRDENG_PAGE_CONFLICT)
848 + buffer_strcat(wb, "CONFLICT ");
849 + if(flags & RRDENG_PAGE_FULL)
850 + buffer_strcat(wb, "PAGE_FULL");
851 + if(flags & RRDENG_PAGE_COLLECT_FINALIZE)
852 + buffer_strcat(wb, "COLLECT_FINALIZE");
853 + if(flags & RRDENG_PAGE_UPDATE_EVERY_CHANGE)
854 + buffer_strcat(wb, "UPDATE_EVERY_CHANGE");
855 + if(flags & RRDENG_PAGE_STEP_TOO_SMALL)
856 + buffer_strcat(wb, "STEP_TOO_SMALL");
857 + if(flags & RRDENG_PAGE_STEP_UNALIGNED)
858 + buffer_strcat(wb, "STEP_UNALIGNED");
859 +}
860 +
861 inline VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_extent_page_descr *descr, time_t now_s, time_t overwrite_zero_update_every_s, bool have_read_error) {
862 + return validate_page(
863 + (uuid_t *)descr->uuid,
864 + (time_t) (descr->start_time_ut / USEC_PER_SEC),
865 + (time_t) (descr->end_time_ut / USEC_PER_SEC),
866 + 0,
867 + descr->page_length,
868 + descr->type,
869 + 0,
870 + now_s,
871 + overwrite_zero_update_every_s,
872 + have_read_error,
873 + true,
874 + "loaded", 0);
875 +}
876 +
877 +VALIDATED_PAGE_DESCRIPTOR validate_page(
878 + uuid_t *uuid,
879 + time_t start_time_s,
880 + time_t end_time_s,
881 + time_t update_every_s, // can be zero, if unknown
882 + size_t page_length,
883 + uint8_t page_type,
884 + size_t entries, // can be zero, if unknown
885 + time_t now_s, // can be zero, to disable future timestamp check
886 + time_t overwrite_zero_update_every_s, // can be zero, if unknown
887 + bool have_read_error,
888 + bool minimize_invalid_size,
889 + const char *msg,
890 + RRDENG_COLLECT_PAGE_FLAGS flags) {
891 +
892 VALIDATED_PAGE_DESCRIPTOR vd = {
832 - .start_time_s = (time_t) (descr->start_time_ut / USEC_PER_SEC),
833 - .end_time_s = (time_t) (descr->end_time_ut / USEC_PER_SEC),
834 - .page_length = descr->page_length,
835 - .type = descr->type,
893 + .start_time_s = start_time_s,
894 + .end_time_s = end_time_s,
895 + .update_every_s = update_every_s,
896 + .page_length = page_length,
897 + .type = page_type,
898 + .is_valid = true,
899 };
900 +
901 + // always calculate entries by size
902 vd.point_size = page_type_size[vd.type];
903 vd.entries = page_entries_by_size(vd.page_length, vd.point_size);
839 - vd.update_every_s = (vd.entries > 1) ? ((vd.end_time_s - vd.start_time_s) / (time_t)(vd.entries - 1)) : overwrite_zero_update_every_s;
904
841 - bool is_valid = true;
905 + // allow to be called without entries (when loading pages from disk)
906 + if(!entries)
907 + entries = vd.entries;
908 +
909 + // allow to be called without update every (when loading pages from disk)
910 + if(!update_every_s) {
911 + vd.update_every_s = (vd.entries > 1) ? ((vd.end_time_s - vd.start_time_s) / (time_t) (vd.entries - 1))
912 + : overwrite_zero_update_every_s;
913 +
914 + update_every_s = vd.update_every_s;
915 + }
916
917 // another such set of checks exists in
918 // update_metric_retention_and_granularity_by_uuid()
919
920 + bool updated = false;
921 +
922 if( have_read_error ||
923 vd.page_length == 0 ||
924 vd.page_length > RRDENG_BLOCK_SIZE ||
925 vd.start_time_s > vd.end_time_s ||
850 - vd.end_time_s > now_s ||
926 + (now_s && vd.end_time_s > now_s) ||
927 vd.start_time_s == 0 ||
928 vd.end_time_s == 0 ||
929 (vd.start_time_s == vd.end_time_s && vd.entries > 1) ||
930 (vd.update_every_s == 0 && vd.entries > 1)
855 - ) {
856 - is_valid = false;
931 + )
932 + vd.is_valid = false;
933
858 - error_limit_static_global_var(erl, 1, 0);
859 - error_limit(&erl, "DBENGINE: ignoring invalid page of type %u from %ld to %ld (now %ld), update every %ld, page length %zu, point size %zu, entries %zu.",
860 - vd.type, vd.start_time_s, vd.end_time_s, now_s, vd.update_every_s, vd.page_length, vd.point_size, vd.entries);
861 - }
934 else {
863 - if (vd.update_every_s) {
935 + if(unlikely(vd.entries != entries || vd.update_every_s != update_every_s))
936 + updated = true;
937 +
938 + if (likely(vd.update_every_s)) {
939 size_t entries_by_time = page_entries_by_time(vd.start_time_s, vd.end_time_s, vd.update_every_s);
940
941 if (vd.entries != entries_by_time) {
@@ -878,25 +953,86 @@ inline VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_
953 vd.update_every_s = overwrite_zero_update_every_s;
954 vd.end_time_s = (time_t)(vd.start_time_s + (vd.entries - 1) * vd.update_every_s);
955 }
956 +
957 + updated = true;
958 }
959 }
883 - else
960 + else if(overwrite_zero_update_every_s) {
961 vd.update_every_s = overwrite_zero_update_every_s;
962 + updated = true;
963 + }
964 }
965
887 - if(!is_valid) {
888 - if(vd.start_time_s == vd.end_time_s) {
889 - vd.page_length = vd.point_size;
890 - vd.entries = 1;
966 + if(unlikely(!vd.is_valid || updated)) {
967 +#ifndef NETDATA_INTERNAL_CHECKS
968 + error_limit_static_global_var(erl, 1, 0);
969 +#endif
970 + char uuid_str[UUID_STR_LEN + 1];
971 + uuid_unparse(*uuid, uuid_str);
972 +
973 + BUFFER *wb = NULL;
974 +
975 + if(flags) {
976 + wb = buffer_create(0, NULL);
977 + collect_page_flags_to_buffer(wb, flags);
978 + }
979 +
980 + if(!vd.is_valid) {
981 +#ifdef NETDATA_INTERNAL_CHECKS
982 + internal_error(true,
983 +#else
984 + error_limit(&erl,
985 +#endif
986 + "DBENGINE: metric '%s' %s invalid page of type %u "
987 + "from %ld to %ld (now %ld), update every %ld, page length %zu, entries %zu (flags: %s)",
988 + uuid_str, msg, vd.type,
989 + vd.start_time_s, vd.end_time_s, now_s, vd.update_every_s, vd.page_length, vd.entries, wb?buffer_tostring(wb):""
990 + );
991 +
992 + if(minimize_invalid_size) {
993 + // since the page is going to be loaded
994 + // let's minimize the memory the invalid page will occupy
995 +
996 + if(vd.start_time_s == vd.end_time_s) {
997 + vd.page_length = vd.point_size;
998 + vd.entries = 1;
999 + }
1000 + else {
1001 + vd.page_length = vd.point_size * 2;
1002 + vd.update_every_s = vd.end_time_s - vd.start_time_s;
1003 + vd.entries = 2;
1004 + }
1005 + }
1006 }
1007 else {
893 - vd.page_length = vd.point_size * 2;
894 - vd.update_every_s = vd.end_time_s - vd.start_time_s;
895 - vd.entries = 2;
1008 + const char *err_valid = (vd.is_valid) ? "" : "found invalid, ";
1009 + const char *err_start = (vd.start_time_s == start_time_s) ? "" : "start time updated, ";
1010 + const char *err_end = (vd.end_time_s == end_time_s) ? "" : "end time updated, ";
1011 + const char *err_update = (vd.update_every_s == update_every_s) ? "" : "update every updated, ";
1012 + const char *err_length = (vd.page_length == page_length) ? "" : "page length updated, ";
1013 + const char *err_entries = (vd.entries == entries) ? "" : "entries updated, ";
1014 + const char *err_future = (now_s && vd.end_time_s <= now_s) ? "" : "future end time, ";
1015 +
1016 +#ifdef NETDATA_INTERNAL_CHECKS
1017 + internal_error(true,
1018 +#else
1019 + error_limit(&erl,
1020 +#endif
1021 + "DBENGINE: metric '%s' %s page of type %u "
1022 + "from %ld to %ld (now %ld), update every %ld, page length %zu, entries %zu (flags: %s), "
1023 + "found inconsistent - the right is "
1024 + "from %ld to %ld, update every %ld, page length %zu, entries %zu: "
1025 + "%s%s%s%s%s%s%s",
1026 + uuid_str, msg, vd.type,
1027 + start_time_s, end_time_s, now_s, update_every_s, page_length, entries, wb?buffer_tostring(wb):"",
1028 + vd.start_time_s, vd.end_time_s, vd.update_every_s, vd.page_length, vd.entries,
1029 + err_valid, err_start, err_end, err_update, err_length, err_entries, err_future
1030 + );
1031 }
1032 +
1033 + buffer_free(wb);
1034 }
1035
899 - vd.data_on_disk_valid = is_valid;
1036 return vd;
1037 }
1038
@@ -993,8 +1129,7 @@ static bool epdl_populate_pages_from_extent_data(
1129 crc = crc32(crc, data, epdl->extent_size - sizeof(*trailer));
1130 ret = crc32cmp(trailer->checksum, crc);
1131 if (unlikely(ret)) {
996 - ++ctx->stats.io_errors;
997 - rrd_stat_atomic_add(&global_io_errors, 1);
1132 + ctx_io_error(ctx);
1133 have_read_error = true;
1134
1135 error_limit_static_global_var(erl, 1, 0);
@@ -1027,9 +1162,9 @@ static bool epdl_populate_pages_from_extent_data(
1162
1163 ret = LZ4_decompress_safe(data + payload_offset, uncompressed_buf,
1164 (int) payload_length, (int) uncompressed_payload_length);
1030 - ctx->stats.before_decompress_bytes += payload_length;
1031 - ctx->stats.after_decompress_bytes += ret;
1032 - debug(D_RRDENGINE, "LZ4 decompressed %u bytes to %d bytes.", payload_length, ret);
1165 +
1166 + __atomic_add_fetch(&ctx->stats.before_decompress_bytes, payload_length, __ATOMIC_RELAXED);
1167 + __atomic_add_fetch(&ctx->stats.after_decompress_bytes, ret, __ATOMIC_RELAXED);
1168 }
1169 }
1170
@@ -1044,7 +1179,7 @@ static bool epdl_populate_pages_from_extent_data(
1179 size_t stats_cache_hit_while_inserting = 0;
1180
1181 uint32_t page_offset = 0, page_length;
1047 - time_t now_s = now_realtime_sec();
1182 + time_t now_s = max_acceptable_collected_time();
1183 for (i = 0; i < count; i++, page_offset += page_length) {
1184 page_length = header->descr[i].page_length;
1185 time_t start_time_s = (time_t) (header->descr[i].start_time_ut / USEC_PER_SEC);
@@ -1080,7 +1215,7 @@ static bool epdl_populate_pages_from_extent_data(
1215
1216 void *page_data = dbengine_page_alloc(vd.page_length);
1217
1083 - if (unlikely(!vd.data_on_disk_valid)) {
1218 + if (unlikely(!vd.is_valid)) {
1219 fill_page_with_nulls(page_data, vd.page_length, vd.type);
1220 stats_load_invalid_page++;
1221 }
database/engine/rrdengine.c
+208 -120
@@ -25,6 +25,7 @@ struct rrdeng_main {
25
26 size_t flushes_running;
27 size_t evictions_running;
28 + size_t cleanup_running;
29 } rrdeng_main = {
30 .thread = 0,
31 .loop = {},
@@ -32,6 +33,7 @@ struct rrdeng_main {
33 .timer = {},
34 .flushes_running = 0,
35 .evictions_running = 0,
36 + .cleanup_running = 0,
37 };
38
39 static void sanity_check(void)
@@ -64,7 +66,7 @@ static void sanity_check(void)
66 // ----------------------------------------------------------------------------
67 // work request cache
68
67 -typedef void (*work_cb)(struct rrdengine_instance *ctx, void *data, struct completion *completion, uv_work_t* req);
69 +typedef void *(*work_cb)(struct rrdengine_instance *ctx, void *data, struct completion *completion, uv_work_t* req);
70 typedef void (*after_work_cb)(struct rrdengine_instance *ctx, void *data, struct completion *completion, uv_work_t* req, int status);
71
72 struct rrdeng_work {
@@ -140,14 +142,14 @@ static inline void work_done(struct rrdeng_work *work_request) {
142 netdata_spinlock_unlock(&work_request_globals.protected.spinlock);
143 }
144
143 -void work_standard_worker(uv_work_t *req) {
145 +static void work_standard_worker(uv_work_t *req) {
146 __atomic_add_fetch(&work_request_globals.atomics.executing, 1, __ATOMIC_RELAXED);
147
148 register_libuv_worker_jobs();
149 worker_is_busy(UV_EVENT_WORKER_INIT);
150
151 struct rrdeng_work *work_request = req->data;
150 - work_request->work_cb(work_request->ctx, work_request->data, work_request->completion, req);
152 + work_request->data = work_request->work_cb(work_request->ctx, work_request->data, work_request->completion, req);
153 worker_is_idle();
154
155 __atomic_sub_fetch(&work_request_globals.atomics.dispatched, 1, __ATOMIC_RELAXED);
@@ -158,7 +160,7 @@ void work_standard_worker(uv_work_t *req) {
160 fatal_assert(0 == uv_async_send(&rrdeng_main.async));
161 }
162
161 -void after_work_standard_callback(uv_work_t* req, int status) {
163 +static void after_work_standard_callback(uv_work_t* req, int status) {
164 struct rrdeng_work *work_request = req->data;
165
166 worker_is_busy(RRDENG_OPCODE_MAX + work_request->opcode);
@@ -610,7 +612,7 @@ static inline STORAGE_PRIORITY rrdeng_enq_cmd_map_opcode_to_priority(enum rrdeng
612 priority = STORAGE_PRIORITY_BEST_EFFORT;
613
614 switch(opcode) {
613 - case RRDENG_OPCODE_PREP_QUERY:
615 + case RRDENG_OPCODE_QUERY:
616 priority = STORAGE_PRIORITY_INTERNAL_QUERY_PREP;
617 break;
618
@@ -770,7 +772,7 @@ void dbengine_extent_free(void *extent, size_t size __maybe_unused) {
772 freez(extent);
773 }
774
773 -static void commit_data_extent(struct rrdengine_instance *ctx, struct extent_io_descriptor *xt_io_descr) {
775 +static void journalfile_extent_build(struct rrdengine_instance *ctx, struct extent_io_descriptor *xt_io_descr) {
776 unsigned count, payload_length, descr_size, size_bytes;
777 void *buf;
778 /* persistent structures */
@@ -815,7 +817,7 @@ static void after_extent_flushed_to_open(struct rrdengine_instance *ctx __maybe_
817 rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
818 }
819
818 -static void extent_flushed_to_open_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) {
820 +static void *extent_flushed_to_open_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) {
821 worker_is_busy(UV_EVENT_FLUSHED_TO_OPEN);
822
823 uv_fs_t *uv_fs_request = data;
@@ -825,8 +827,7 @@ static void extent_flushed_to_open_tp_worker(struct rrdengine_instance *ctx __ma
827 unsigned i;
828
829 if (uv_fs_request->result < 0) {
828 - __atomic_add_fetch(&ctx->stats.io_errors, 1, __ATOMIC_RELAXED);
829 - rrd_stat_atomic_add(&global_io_errors, 1);
830 + ctx_io_error(ctx);
831 error("DBENGINE: %s: uv_fs_write: %s", __func__, uv_strerror((int)uv_fs_request->result));
832 }
833 datafile = xt_io_descr->datafile;
@@ -858,34 +859,102 @@ static void extent_flushed_to_open_tp_worker(struct rrdengine_instance *ctx __ma
859
860 if(datafile->fileno != ctx_last_fileno_get(ctx) && still_running)
861 // we just finished a flushing on a datafile that is not the active one
861 - rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_FILE_INDEX, datafile, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
862 + rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_INDEX, datafile, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
863 +
864 + return data;
865 }
866
867 // Main event loop callback
865 -static void extent_flush_io_callback(uv_fs_t *uv_fs_request) {
866 - worker_is_busy(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSH_PAGES);
868 +static void after_extent_write_datafile_io(uv_fs_t *uv_fs_request) {
869 + worker_is_busy(RRDENG_OPCODE_MAX + RRDENG_OPCODE_EXTENT_WRITE);
870 +
871 struct extent_io_descriptor *xt_io_descr = uv_fs_request->data;
872 struct rrdengine_datafile *datafile = xt_io_descr->datafile;
873 struct rrdengine_instance *ctx = datafile->ctx;
874
871 - wal_flush_transaction_buffer(ctx, xt_io_descr->datafile, xt_io_descr->wal, &rrdeng_main.loop);
875 + journalfile_v1_extent_write(ctx, xt_io_descr->datafile, xt_io_descr->wal, &rrdeng_main.loop);
876
877 netdata_spinlock_lock(&datafile->writers.spinlock);
878 datafile->writers.running--;
875 -
879 datafile->writers.flushed_to_open_running++;
877 - rrdeng_enq_cmd(xt_io_descr->ctx, RRDENG_OPCODE_FLUSHED_TO_OPEN, uv_fs_request, xt_io_descr->completion,
878 - STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
879 -
880 netdata_spinlock_unlock(&datafile->writers.spinlock);
881
882 + rrdeng_enq_cmd(xt_io_descr->ctx,
883 + RRDENG_OPCODE_FLUSHED_TO_OPEN,
884 + uv_fs_request,
885 + xt_io_descr->completion,
886 + STORAGE_PRIORITY_INTERNAL_DBENGINE,
887 + NULL,
888 + NULL);
889 +
890 worker_is_idle();
891 }
892
893 +static bool datafile_is_full(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile) {
894 + bool ret = false;
895 + netdata_spinlock_lock(&datafile->writers.spinlock);
896 +
897 + if(ctx_is_available_for_queries(ctx) && datafile->pos > rrdeng_target_data_file_size(ctx))
898 + ret = true;
899 +
900 + netdata_spinlock_unlock(&datafile->writers.spinlock);
901 +
902 + return ret;
903 +}
904 +
905 +static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_instance *ctx) {
906 + struct rrdengine_datafile *datafile;
907 +
908 + // get the latest datafile
909 + uv_rwlock_rdlock(&ctx->datafiles.rwlock);
910 + datafile = ctx->datafiles.first->prev;
911 + // become a writer on this datafile, to prevent it from vanishing
912 + netdata_spinlock_lock(&datafile->writers.spinlock);
913 + datafile->writers.running++;
914 + netdata_spinlock_unlock(&datafile->writers.spinlock);
915 + uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
916 +
917 + if(datafile_is_full(ctx, datafile)) {
918 + // remember the datafile we have become writers to
919 + struct rrdengine_datafile *old_datafile = datafile;
920 +
921 + // only 1 datafile creation at a time
922 + static netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER;
923 + netdata_mutex_lock(&mutex);
924 +
925 + // take the latest datafile again - without this, multiple threads may create multiple files
926 + uv_rwlock_rdlock(&ctx->datafiles.rwlock);
927 + datafile = ctx->datafiles.first->prev;
928 + uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
929 +
930 + if(datafile_is_full(ctx, datafile) && create_new_datafile_pair(ctx) == 0)
931 + rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_INDEX, datafile, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL,
932 + NULL);
933 +
934 + netdata_mutex_unlock(&mutex);
935 +
936 + // get the new latest datafile again, like above
937 + uv_rwlock_rdlock(&ctx->datafiles.rwlock);
938 + datafile = ctx->datafiles.first->prev;
939 + // become a writer on this datafile, to prevent it from vanishing
940 + netdata_spinlock_lock(&datafile->writers.spinlock);
941 + datafile->writers.running++;
942 + netdata_spinlock_unlock(&datafile->writers.spinlock);
943 + uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
944 +
945 + // release the writers on the old datafile
946 + netdata_spinlock_lock(&old_datafile->writers.spinlock);
947 + old_datafile->writers.running--;
948 + netdata_spinlock_unlock(&old_datafile->writers.spinlock);
949 + }
950 +
951 + return datafile;
952 +}
953 +
954 /*
955 * Take a page list in a judy array and write them
956 */
888 -static unsigned do_flush_extent(struct rrdengine_instance *ctx, struct page_descr_with_data *base, struct completion *completion) {
957 +static struct extent_io_descriptor *datafile_extent_build(struct rrdengine_instance *ctx, struct page_descr_with_data *base, struct completion *completion) {
958 int ret;
959 int compressed_size, max_compressed_size = 0;
960 unsigned i, count, size_bytes, pos, real_io_size;
@@ -916,7 +985,7 @@ static unsigned do_flush_extent(struct rrdengine_instance *ctx, struct page_desc
985 completion_mark_complete(completion);
986
987 __atomic_sub_fetch(&ctx->atomic.extents_currently_being_flushed, 1, __ATOMIC_RELAXED);
919 - return 0;
988 + return NULL;
989 }
990
991 xt_io_descr = extent_io_descriptor_get();
@@ -966,52 +1035,35 @@ static unsigned do_flush_extent(struct rrdengine_instance *ctx, struct page_desc
1035 pos += descr->page_length;
1036 }
1037
969 - switch (compression_algorithm) {
970 - case RRD_NO_COMPRESSION:
971 - header->payload_length = uncompressed_payload_length;
972 - break;
973 - default: /* Compress */
974 - compressed_size = LZ4_compress_default(xt_io_descr->buf + payload_offset, compressed_buf,
975 - uncompressed_payload_length, max_compressed_size);
976 - ctx->stats.before_compress_bytes += uncompressed_payload_length;
977 - ctx->stats.after_compress_bytes += compressed_size;
978 - debug(D_RRDENGINE, "LZ4 compressed %"PRIu32" bytes to %d bytes.", uncompressed_payload_length, compressed_size);
979 - (void) memcpy(xt_io_descr->buf + payload_offset, compressed_buf, compressed_size);
980 - extent_buffer_release(eb);
981 - size_bytes = payload_offset + compressed_size + sizeof(*trailer);
982 - header->payload_length = compressed_size;
983 - break;
984 - }
1038 + if(likely(compression_algorithm == RRD_LZ4)) {
1039 + compressed_size = LZ4_compress_default(
1040 + xt_io_descr->buf + payload_offset,
1041 + compressed_buf,
1042 + (int)uncompressed_payload_length,
1043 + max_compressed_size);
1044
986 - // get the latest datafile
987 - uv_rwlock_rdlock(&ctx->datafiles.rwlock);
988 - datafile = ctx->datafiles.first->prev;
989 - netdata_spinlock_lock(&datafile->writers.spinlock);
990 - uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
991 -
992 - if(ctx_is_available_for_queries(ctx) && datafile->pos > rrdeng_target_data_file_size(ctx)) {
993 - static SPINLOCK sp = NETDATA_SPINLOCK_INITIALIZER;
994 - netdata_spinlock_lock(&sp);
995 - if(create_new_datafile_pair(ctx) == 0)
996 - rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_FILE_INDEX, datafile, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL,
997 - NULL);
998 - netdata_spinlock_unlock(&sp);
1045 + __atomic_add_fetch(&ctx->stats.before_compress_bytes, uncompressed_payload_length, __ATOMIC_RELAXED);
1046 + __atomic_add_fetch(&ctx->stats.after_compress_bytes, compressed_size, __ATOMIC_RELAXED);
1047
1000 - // unlock the old datafile
1001 - netdata_spinlock_unlock(&datafile->writers.spinlock);
1002 -
1003 - // get the new datafile
1004 - uv_rwlock_rdlock(&ctx->datafiles.rwlock);
1005 - datafile = ctx->datafiles.first->prev;
1006 - netdata_spinlock_lock(&datafile->writers.spinlock);
1007 - uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1048 + (void) memcpy(xt_io_descr->buf + payload_offset, compressed_buf, compressed_size);
1049 + extent_buffer_release(eb);
1050 + size_bytes = payload_offset + compressed_size + sizeof(*trailer);
1051 + header->payload_length = compressed_size;
1052 + }
1053 + else { // RRD_NO_COMPRESSION
1054 + header->payload_length = uncompressed_payload_length;
1055 }
1056
1010 - datafile->writers.running++;
1057 + real_io_size = ALIGN_BYTES_CEILING(size_bytes);
1058
1059 + datafile = get_datafile_to_write_extent(ctx);
1060 + netdata_spinlock_lock(&datafile->writers.spinlock);
1061 xt_io_descr->datafile = datafile;
1013 - xt_io_descr->bytes = size_bytes;
1062 xt_io_descr->pos = datafile->pos;
1063 + datafile->pos += real_io_size;
1064 + netdata_spinlock_unlock(&datafile->writers.spinlock);
1065 +
1066 + xt_io_descr->bytes = size_bytes;
1067 xt_io_descr->uv_fs_request.data = xt_io_descr;
1068 xt_io_descr->completion = completion;
1069
@@ -1020,26 +1072,37 @@ static unsigned do_flush_extent(struct rrdengine_instance *ctx, struct page_desc
1072 crc = crc32(crc, xt_io_descr->buf, size_bytes - sizeof(*trailer));
1073 crc32set(trailer->checksum, crc);
1074
1023 - real_io_size = ALIGN_BYTES_CEILING(size_bytes);
1075 xt_io_descr->iov = uv_buf_init((void *)xt_io_descr->buf, real_io_size);
1076 + journalfile_extent_build(ctx, xt_io_descr);
1077
1026 - ctx->stats.io_write_bytes += real_io_size;
1027 - ++ctx->stats.io_write_requests;
1028 - ctx->stats.io_write_extent_bytes += real_io_size;
1029 - ++ctx->stats.io_write_extents;
1030 - commit_data_extent(ctx, xt_io_descr);
1031 - datafile->pos += real_io_size;
1032 - ctx_current_disk_space_increase(ctx, real_io_size);
1078 ctx_last_flush_fileno_set(ctx, datafile->fileno);
1079 + ctx_current_disk_space_increase(ctx, real_io_size);
1080 + ctx_io_write_op_bytes(ctx, real_io_size);
1081
1035 - ret = uv_fs_write(&rrdeng_main.loop, &xt_io_descr->uv_fs_request, datafile->file, &xt_io_descr->iov,
1036 - 1, xt_io_descr->pos, extent_flush_io_callback);
1082 + return xt_io_descr;
1083 +}
1084
1038 - fatal_assert(-1 != ret);
1085 +static void after_extent_write(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* uv_work_req __maybe_unused, int status __maybe_unused) {
1086 + struct extent_io_descriptor *xt_io_descr = data;
1087
1040 - netdata_spinlock_unlock(&datafile->writers.spinlock);
1088 + if(xt_io_descr) {
1089 + int ret = uv_fs_write(&rrdeng_main.loop,
1090 + &xt_io_descr->uv_fs_request,
1091 + xt_io_descr->datafile->file,
1092 + &xt_io_descr->iov,
1093 + 1,
1094 + (int64_t) xt_io_descr->pos,
1095 + after_extent_write_datafile_io);
1096
1042 - return real_io_size;
1097 + fatal_assert(-1 != ret);
1098 + }
1099 +}
1100 +
1101 +static void *extent_write_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) {
1102 + worker_is_busy(UV_EVENT_FLUSH_PAGES);
1103 + struct page_descr_with_data *base = data;
1104 + struct extent_io_descriptor *xt_io_descr = datafile_extent_build(ctx, base, completion);
1105 + return xt_io_descr;
1106 }
1107
1108 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) {
@@ -1255,7 +1318,7 @@ static void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_dat
1318
1319 journal_file = datafile->journalfile;
1320 datafile_bytes = datafile->pos;
1258 - journal_file_bytes = journal_file->pos;
1321 + journal_file_bytes = journalfile_current_size(journal_file);
1322 deleted_bytes = journalfile_v2_data_size_get(journal_file);
1323
1324 info("DBENGINE: deleting data and journal files to maintain disk quota");
@@ -1285,24 +1348,29 @@ static void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_dat
1348 rrdcontext_db_rotation();
1349 }
1350
1288 -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) {
1351 +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) {
1352 datafile_delete(ctx, ctx->datafiles.first, true);
1353 + return data;
1354 }
1355
1356 static void after_flush_all_hot_and_dirty_pages_of_section(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) {
1357 ;
1358 }
1359
1296 -static void flush_all_hot_and_dirty_pages_of_section_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) {
1360 +static void *flush_all_hot_and_dirty_pages_of_section_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) {
1361 + worker_is_busy(UV_EVENT_QUIESCE);
1362 pgc_flush_all_hot_and_dirty_pages(main_cache, (Word_t)ctx);
1363 completion_mark_complete(&ctx->quiesce.completion);
1364 + return data;
1365 }
1366
1367 static void after_populate_mrg(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) {
1368 ;
1369 }
1370
1305 -static void populate_mrg_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) {
1371 +static void *populate_mrg_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) {
1372 + worker_is_busy(UV_EVENT_POPULATE_MRG);
1373 +
1374 do {
1375 struct rrdengine_datafile *datafile = NULL;
1376
@@ -1332,13 +1400,17 @@ static void populate_mrg_tp_worker(struct rrdengine_instance *ctx __maybe_unused
1400 } while(1);
1401
1402 completion_mark_complete(completion);
1403 +
1404 + return data;
1405 }
1406
1407 static void after_ctx_shutdown(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) {
1408 ;
1409 }
1410
1341 -static void ctx_shutdown_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) {
1411 +static void *ctx_shutdown_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) {
1412 + worker_is_busy(UV_EVENT_SHUTDOWN);
1413 +
1414 completion_wait_for(&ctx->quiesce.completion);
1415 completion_destroy(&ctx->quiesce.completion);
1416
@@ -1347,32 +1419,39 @@ static void ctx_shutdown_tp_worker(struct rrdengine_instance *ctx __maybe_unused
1419 sleep_usec(1 * USEC_PER_MS);
1420
1421 completion_mark_complete(completion);
1422 +
1423 + return data;
1424 }
1425
1352 -static void cache_flush_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) {
1426 +static void *cache_flush_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) {
1427 if (!main_cache)
1354 - return;
1428 + return data;
1429
1430 worker_is_busy(UV_EVENT_FLUSH_MAIN);
1431 pgc_flush_pages(main_cache, 0);
1432 +
1433 + return data;
1434 }
1435
1360 -static void cache_evict_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1436 +static void *cache_evict_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1437 if (!main_cache)
1362 - return;
1438 + return data;
1439
1440 worker_is_busy(UV_EVENT_EVICT_MAIN);
1441 pgc_evict_pages(main_cache, 0, 0);
1442 +
1443 + return data;
1444 }
1445
1446 static void after_prep_query(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) {
1447 ;
1448 }
1449
1372 -static void query_prep_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1450 +static void *query_prep_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1451 worker_is_busy(UV_EVENT_PREP_QUERY);
1452 PDC *pdc = data;
1453 rrdeng_prep_query(pdc);
1454 + return data;
1455 }
1456
1457 unsigned rrdeng_target_data_file_size(struct rrdengine_instance *ctx) {
@@ -1411,9 +1490,10 @@ void async_cb(uv_async_t *handle)
1490 #define TIMER_PERIOD_MS (1000)
1491
1492
1414 -static void extent_read_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) {
1493 +static void *extent_read_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) {
1494 EPDL *epdl = data;
1495 epdl_find_extent_and_populate_pages(ctx, epdl, true);
1496 + return data;
1497 }
1498
1499 static void epdl_populate_pages_asynchronously(struct rrdengine_instance *ctx, EPDL *epdl, STORAGE_PRIORITY priority) {
@@ -1434,7 +1514,7 @@ void pdc_route_synchronously(struct rrdengine_instance *ctx, struct page_details
1514 }
1515
1516 #define MAX_RETRIES_TO_START_INDEX (100)
1437 -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) {
1517 +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) {
1518 unsigned count = 0;
1519 worker_is_busy(UV_EVENT_JOURNAL_INDEX_WAIT);
1520
@@ -1443,7 +1523,7 @@ static void journal_v2_indexing_tp_worker(struct rrdengine_instance *ctx __maybe
1523
1524 if (count == MAX_RETRIES_TO_START_INDEX) {
1525 worker_is_idle();
1446 - return;
1526 + return data;
1527 }
1528
1529 struct rrdengine_datafile *datafile = ctx->datafiles.first;
@@ -1475,6 +1555,8 @@ static void journal_v2_indexing_tp_worker(struct rrdengine_instance *ctx __maybe
1555 internal_error(count, "DBENGINE: journal indexing done; %u files processed", count);
1556
1557 worker_is_idle();
1558 +
1559 + return data;
1560 }
1561
1562 static void after_do_cache_flush(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) {
@@ -1513,17 +1595,12 @@ struct rrdeng_buffer_sizes rrdeng_get_buffer_sizes(void) {
1595 };
1596 }
1597
1516 -void timer_cb(uv_timer_t* handle) {
1517 - worker_is_busy(RRDENG_TIMER_CB);
1518 - uv_stop(handle->loop);
1519 - uv_update_time(handle->loop);
1520 -
1521 - worker_set_metric(RRDENG_OPCODES_WAITING, (NETDATA_DOUBLE)rrdeng_cmd_globals.queue.waiting);
1522 - worker_set_metric(RRDENG_WORKS_DISPATCHED, (NETDATA_DOUBLE)__atomic_load_n(&work_request_globals.atomics.dispatched, __ATOMIC_RELAXED));
1523 - worker_set_metric(RRDENG_WORKS_EXECUTING, (NETDATA_DOUBLE)__atomic_load_n(&work_request_globals.atomics.executing, __ATOMIC_RELAXED));
1598 +static void after_cleanup(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) {
1599 + rrdeng_main.cleanup_running--;
1600 +}
1601
1525 - rrdeng_enq_cmd(NULL, RRDENG_OPCODE_FLUSH_INIT, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1526 - rrdeng_enq_cmd(NULL, RRDENG_OPCODE_EVICT_INIT, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1602 +static void *cleanup_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) {
1603 + worker_is_busy(UV_EVENT_BUFFERS_CLEANUP);
1604
1605 rrdeng_cmd_cleanup1();
1606 work_request_cleanup1();
@@ -1550,6 +1627,22 @@ void timer_cb(uv_timer_t* handle) {
1627 julyl_cleanup1();
1628 #endif
1629
1630 + return data;
1631 +}
1632 +
1633 +void timer_cb(uv_timer_t* handle) {
1634 + worker_is_busy(RRDENG_TIMER_CB);
1635 + uv_stop(handle->loop);
1636 + uv_update_time(handle->loop);
1637 +
1638 + worker_set_metric(RRDENG_OPCODES_WAITING, (NETDATA_DOUBLE)rrdeng_cmd_globals.queue.waiting);
1639 + worker_set_metric(RRDENG_WORKS_DISPATCHED, (NETDATA_DOUBLE)__atomic_load_n(&work_request_globals.atomics.dispatched, __ATOMIC_RELAXED));
1640 + worker_set_metric(RRDENG_WORKS_EXECUTING, (NETDATA_DOUBLE)__atomic_load_n(&work_request_globals.atomics.executing, __ATOMIC_RELAXED));
1641 +
1642 + rrdeng_enq_cmd(NULL, RRDENG_OPCODE_FLUSH_INIT, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1643 + rrdeng_enq_cmd(NULL, RRDENG_OPCODE_EVICT_INIT, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1644 + rrdeng_enq_cmd(NULL, RRDENG_OPCODE_CLEANUP, NULL, NULL, STORAGE_PRIORITY_INTERNAL_DBENGINE, NULL, NULL);
1645 +
1646 worker_is_idle();
1647 }
1648
@@ -1593,35 +1686,34 @@ bool rrdeng_dbengine_spawn(struct rrdengine_instance *ctx __maybe_unused) {
1686 void dbengine_event_loop(void* arg) {
1687 sanity_check();
1688 uv_thread_set_name_np(pthread_self(), "DBENGINE");
1689 + service_register(SERVICE_THREAD_TYPE_EVENT_LOOP, NULL, NULL, NULL, true);
1690
1691 worker_register("DBENGINE");
1692
1693 // opcode jobs
1694 worker_register_job_name(RRDENG_OPCODE_NOOP, "noop");
1695
1696 + worker_register_job_name(RRDENG_OPCODE_QUERY, "query");
1697 + worker_register_job_name(RRDENG_OPCODE_EXTENT_WRITE, "extent write");
1698 worker_register_job_name(RRDENG_OPCODE_EXTENT_READ, "extent read");
1603 - worker_register_job_name(RRDENG_OPCODE_PREP_QUERY, "prep query");
1604 - worker_register_job_name(RRDENG_OPCODE_FLUSH_PAGES, "flush pages");
1699 worker_register_job_name(RRDENG_OPCODE_FLUSHED_TO_OPEN, "flushed to open");
1700 + worker_register_job_name(RRDENG_OPCODE_DATABASE_ROTATE, "db rotate");
1701 + worker_register_job_name(RRDENG_OPCODE_JOURNAL_INDEX, "journal index");
1702 worker_register_job_name(RRDENG_OPCODE_FLUSH_INIT, "flush init");
1703 worker_register_job_name(RRDENG_OPCODE_EVICT_INIT, "evict init");
1608 - //worker_register_job_name(RRDENG_OPCODE_DATAFILE_CREATE, "datafile create");
1609 - worker_register_job_name(RRDENG_OPCODE_JOURNAL_FILE_INDEX, "journal file index");
1610 - worker_register_job_name(RRDENG_OPCODE_DATABASE_ROTATE, "db rotate");
1704 worker_register_job_name(RRDENG_OPCODE_CTX_SHUTDOWN, "ctx shutdown");
1705 worker_register_job_name(RRDENG_OPCODE_CTX_QUIESCE, "ctx quiesce");
1706
1707 worker_register_job_name(RRDENG_OPCODE_MAX, "get opcode");
1708
1709 + worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_QUERY, "query cb");
1710 + worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_EXTENT_WRITE, "extent write cb");
1711 worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_EXTENT_READ, "extent read cb");
1617 - worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_PREP_QUERY, "prep query cb");
1618 - worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSH_PAGES, "flush pages cb");
1712 worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSHED_TO_OPEN, "flushed to open cb");
1713 + worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_DATABASE_ROTATE, "db rotate cb");
1714 + worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_JOURNAL_INDEX, "journal index cb");
1715 worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSH_INIT, "flush init cb");
1716 worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_EVICT_INIT, "evict init cb");
1622 - //worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_DATAFILE_CREATE, "datafile create cb");
1623 - worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_JOURNAL_FILE_INDEX, "journal file index cb");
1624 - worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_DATABASE_ROTATE, "db rotate cb");
1717 worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_CTX_SHUTDOWN, "ctx shutdown cb");
1718 worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_CTX_QUIESCE, "ctx quiesce cb");
1719
@@ -1663,19 +1755,18 @@ void dbengine_event_loop(void* arg) {
1755 break;
1756 }
1757
1666 - case RRDENG_OPCODE_PREP_QUERY: {
1758 + case RRDENG_OPCODE_QUERY: {
1759 struct rrdengine_instance *ctx = cmd.ctx;
1760 PDC *pdc = cmd.data;
1761 work_dispatch(ctx, pdc, NULL, opcode, query_prep_tp_worker, after_prep_query);
1762 break;
1763 }
1764
1673 - case RRDENG_OPCODE_FLUSH_PAGES: {
1765 + case RRDENG_OPCODE_EXTENT_WRITE: {
1766 struct rrdengine_instance *ctx = cmd.ctx;
1767 struct page_descr_with_data *base = cmd.data;
1768 struct completion *completion = cmd.completion; // optional
1677 - // for the datafile and the journalfile
1678 - do_flush_extent(ctx, base, completion);
1769 + work_dispatch(ctx, base, completion, opcode, extent_write_tp_worker, after_extent_write);
1770 break;
1771 }
1772
@@ -1704,18 +1795,15 @@ void dbengine_event_loop(void* arg) {
1795 break;
1796 }
1797
1707 -// case RRDENG_OPCODE_DATAFILE_CREATE: {
1708 -// struct rrdengine_instance *ctx = cmd.ctx;
1709 -// struct rrdengine_datafile *datafile = ctx->datafiles.first->prev;
1710 -// if(datafile->pos > rrdeng_target_data_file_size(ctx) &&
1711 -// create_new_datafile_pair(ctx, 1, ctx->last_fileno + 1) == 0) {
1712 -// ++ctx->last_fileno;
1713 -// rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_FILE_INDEX, datafile, NULL, STORAGE_PRIORITY_CRITICAL);
1714 -// }
1715 -// break;
1716 -// }
1717 -
1718 - case RRDENG_OPCODE_JOURNAL_FILE_INDEX: {
1798 + case RRDENG_OPCODE_CLEANUP: {
1799 + if(!rrdeng_main.cleanup_running) {
1800 + rrdeng_main.cleanup_running++;
1801 + work_dispatch(NULL, NULL, NULL, opcode, cleanup_tp_worker, after_cleanup);
1802 + }
1803 + break;
1804 + }
1805 +
1806 + case RRDENG_OPCODE_JOURNAL_INDEX: {
1807 struct rrdengine_instance *ctx = cmd.ctx;
1808 struct rrdengine_datafile *datafile = cmd.data;
1809 if(!__atomic_load_n(&ctx->atomic.migration_to_v2_running, __ATOMIC_RELAXED)) {
database/engine/rrdengine.h
+71 -22
@@ -169,17 +169,36 @@ struct jv2_page_info {
169 typedef enum __attribute__ ((__packed__)) {
170 RRDENG_CHO_UNALIGNED = (1 << 0), // set when this metric is not page aligned according to page alignment
171 RRDENG_FIRST_PAGE_ALLOCATED = (1 << 1), // set when this metric has allocated its first page
172 + RRDENG_1ST_METRIC_WRITER = (1 << 2),
173 } RRDENG_COLLECT_HANDLE_OPTIONS;
174
175 +typedef enum __attribute__ ((__packed__)) {
176 + RRDENG_PAGE_PAST_COLLECTION = (1 << 0),
177 + RRDENG_PAGE_REPEATED_COLLECTION = (1 << 1),
178 + RRDENG_PAGE_BIG_GAP = (1 << 2),
179 + RRDENG_PAGE_GAP = (1 << 3),
180 + RRDENG_PAGE_FUTURE_POINT = (1 << 4),
181 + RRDENG_PAGE_CREATED_IN_FUTURE = (1 << 5),
182 + RRDENG_PAGE_COMPLETED_IN_FUTURE = (1 << 6),
183 + RRDENG_PAGE_UNALIGNED = (1 << 7),
184 + RRDENG_PAGE_CONFLICT = (1 << 8),
185 + RRDENG_PAGE_FULL = (1 << 9),
186 + RRDENG_PAGE_COLLECT_FINALIZE = (1 << 10),
187 + RRDENG_PAGE_UPDATE_EVERY_CHANGE = (1 << 11),
188 + RRDENG_PAGE_STEP_TOO_SMALL = (1 << 12),
189 + RRDENG_PAGE_STEP_UNALIGNED = (1 << 13),
190 +} RRDENG_COLLECT_PAGE_FLAGS;
191 +
192 struct rrdeng_collect_handle {
193 struct metric *metric;
194 struct pgc_page *page;
195 struct pg_alignment *alignment;
196 RRDENG_COLLECT_HANDLE_OPTIONS options;
197 uint8_t type;
180 - // 2 bytes remaining here for future use
198 + RRDENG_COLLECT_PAGE_FLAGS page_flags;
199 uint32_t page_entries_max;
200 uint32_t page_position; // keep track of the current page size, to make sure we don't exceed it
201 + usec_t page_start_time_ut;
202 usec_t page_end_time_ut;
203 usec_t update_every_ut;
204 };
@@ -222,18 +241,18 @@ enum rrdeng_opcode {
241 /* can be used to return empty status or flush the command queue */
242 RRDENG_OPCODE_NOOP = 0,
243
244 + RRDENG_OPCODE_QUERY,
245 + RRDENG_OPCODE_EXTENT_WRITE,
246 RRDENG_OPCODE_EXTENT_READ,
226 - RRDENG_OPCODE_PREP_QUERY,
227 - RRDENG_OPCODE_FLUSH_PAGES,
247 RRDENG_OPCODE_FLUSHED_TO_OPEN,
248 + RRDENG_OPCODE_DATABASE_ROTATE,
249 + RRDENG_OPCODE_JOURNAL_INDEX,
250 RRDENG_OPCODE_FLUSH_INIT,
251 RRDENG_OPCODE_EVICT_INIT,
231 - //RRDENG_OPCODE_DATAFILE_CREATE,
232 - RRDENG_OPCODE_JOURNAL_FILE_INDEX,
233 - RRDENG_OPCODE_DATABASE_ROTATE,
252 RRDENG_OPCODE_CTX_SHUTDOWN,
253 RRDENG_OPCODE_CTX_QUIESCE,
254 RRDENG_OPCODE_CTX_POPULATE_MRG,
255 + RRDENG_OPCODE_CLEANUP,
256
257 RRDENG_OPCODE_MAX
258 };
@@ -309,35 +328,23 @@ void wal_release(WAL *wal);
328 * They only describe operations since DB engine instance load time.
329 */
330 struct rrdengine_statistics {
312 - rrdeng_stats_t metric_API_producers;
313 - rrdeng_stats_t metric_API_consumers;
314 - rrdeng_stats_t pg_cache_insertions;
315 - rrdeng_stats_t pg_cache_deletions;
316 - rrdeng_stats_t pg_cache_hits;
317 - rrdeng_stats_t pg_cache_misses;
318 - rrdeng_stats_t pg_cache_backfills;
319 - rrdeng_stats_t pg_cache_evictions;
331 rrdeng_stats_t before_decompress_bytes;
332 rrdeng_stats_t after_decompress_bytes;
333 rrdeng_stats_t before_compress_bytes;
334 rrdeng_stats_t after_compress_bytes;
335 +
336 rrdeng_stats_t io_write_bytes;
337 rrdeng_stats_t io_write_requests;
338 rrdeng_stats_t io_read_bytes;
339 rrdeng_stats_t io_read_requests;
328 - rrdeng_stats_t io_write_extent_bytes;
329 - rrdeng_stats_t io_write_extents;
330 - rrdeng_stats_t io_read_extent_bytes;
331 - rrdeng_stats_t io_read_extents;
340 +
341 rrdeng_stats_t datafile_creations;
342 rrdeng_stats_t datafile_deletions;
343 rrdeng_stats_t journalfile_creations;
344 rrdeng_stats_t journalfile_deletions;
336 - rrdeng_stats_t page_cache_descriptors;
345 +
346 rrdeng_stats_t io_errors;
347 rrdeng_stats_t fs_errors;
339 - rrdeng_stats_t pg_cache_over_half_dirty_events;
340 - rrdeng_stats_t flushing_pressure_page_deletions;
348 };
349
350 /* I/O errors global counter */
@@ -352,6 +359,8 @@ extern rrdeng_stats_t global_flushing_pressure_page_deletions; /* number of dele
359
360 struct rrdengine_instance {
361 struct {
362 + bool legacy; // true when the db is autonomous for a single host
363 +
364 int tier; // the tier of this ctx
365 uint8_t page_type; // default page type for this context
366
@@ -370,6 +379,8 @@ struct rrdengine_instance {
379 unsigned last_fileno; // newest index of datafile and journalfile
380 unsigned last_flush_fileno; // newest index of datafile received data
381
382 + size_t collectors_running;
383 + size_t collectors_running_duplicate;
384 size_t inflight_queries; // the number of queries currently running
385 uint64_t current_disk_space; // the current disk space size used
386
@@ -402,6 +413,26 @@ struct rrdengine_instance {
413 #define ctx_current_disk_space_increase(ctx, size) __atomic_add_fetch(&(ctx)->atomic.current_disk_space, size, __ATOMIC_RELAXED)
414 #define ctx_current_disk_space_decrease(ctx, size) __atomic_sub_fetch(&(ctx)->atomic.current_disk_space, size, __ATOMIC_RELAXED)
415
416 +static inline void ctx_io_read_op_bytes(struct rrdengine_instance *ctx, size_t bytes) {
417 + __atomic_add_fetch(&ctx->stats.io_read_bytes, bytes, __ATOMIC_RELAXED);
418 + __atomic_add_fetch(&ctx->stats.io_read_requests, 1, __ATOMIC_RELAXED);
419 +}
420 +
421 +static inline void ctx_io_write_op_bytes(struct rrdengine_instance *ctx, size_t bytes) {
422 + __atomic_add_fetch(&ctx->stats.io_write_bytes, bytes, __ATOMIC_RELAXED);
423 + __atomic_add_fetch(&ctx->stats.io_write_requests, 1, __ATOMIC_RELAXED);
424 +}
425 +
426 +static inline void ctx_io_error(struct rrdengine_instance *ctx) {
427 + __atomic_add_fetch(&ctx->stats.io_errors, 1, __ATOMIC_RELAXED);
428 + rrd_stat_atomic_add(&global_io_errors, 1);
429 +}
430 +
431 +static inline void ctx_fs_error(struct rrdengine_instance *ctx) {
432 + __atomic_add_fetch(&ctx->stats.fs_errors, 1, __ATOMIC_RELAXED);
433 + rrd_stat_atomic_add(&global_fs_errors, 1);
434 +}
435 +
436 #define ctx_last_fileno_get(ctx) __atomic_load_n(&(ctx)->atomic.last_fileno, __ATOMIC_RELAXED)
437 #define ctx_last_fileno_increment(ctx) __atomic_add_fetch(&(ctx)->atomic.last_fileno, 1, __ATOMIC_RELAXED)
438
@@ -460,7 +491,7 @@ typedef struct validated_page_descriptor {
491 size_t point_size;
492 size_t entries;
493 uint8_t type;
463 - bool data_on_disk_valid;
494 + bool is_valid;
495 } VALIDATED_PAGE_DESCRIPTOR;
496
497 #define page_entries_by_time(start_time_s, end_time_s, update_every_s) \
@@ -469,7 +500,21 @@ typedef struct validated_page_descriptor {
500 #define page_entries_by_size(page_length_in_bytes, point_size_in_bytes) \
501 ((page_length_in_bytes) / (point_size_in_bytes))
502
503 +VALIDATED_PAGE_DESCRIPTOR validate_page(uuid_t *uuid,
504 + time_t start_time_s,
505 + time_t end_time_s,
506 + time_t update_every_s,
507 + size_t page_length,
508 + uint8_t page_type,
509 + size_t entries,
510 + time_t now_s,
511 + time_t overwrite_zero_update_every_s,
512 + bool have_read_error,
513 + bool minimize_invalid_size,
514 + const char *msg,
515 + RRDENG_COLLECT_PAGE_FLAGS flags);
516 VALIDATED_PAGE_DESCRIPTOR validate_extent_page_descr(const struct rrdeng_extent_page_descr *descr, time_t now_s, time_t overwrite_zero_update_every_s, bool have_read_error);
517 +void collect_page_flags_to_buffer(BUFFER *wb, RRDENG_COLLECT_PAGE_FLAGS flags);
518
519 typedef enum {
520 PAGE_IS_IN_THE_PAST = -1,
@@ -479,4 +524,8 @@ typedef enum {
524
525 TIME_RANGE_COMPARE is_page_in_time_range(time_t page_first_time_s, time_t page_last_time_s, time_t wanted_start_time_s, time_t wanted_end_time_s);
526
527 +static inline time_t max_acceptable_collected_time(void) {
528 + return now_realtime_sec() + 1;
529 +}
530 +
531 #endif /* NETDATA_RRDENGINE_H */
database/engine/rrdengineapi.c
+271 -100
@@ -147,14 +147,20 @@ STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE
147 METRIC *metric;
148
149 metric = mrg_metric_get_and_acquire(main_mrg, &rd->metric_uuid, (Word_t) ctx);
150 - if(!metric) {
151 - metric = rrdeng_metric_get_legacy(db_instance, rrddim_id(rd), rrdset_id(rd->rrdset));
152 - if(metric)
153 - uuid_copy(rd->metric_uuid, *mrg_metric_uuid(main_mrg, metric));
154 - }
150
156 - if(!metric)
157 - metric = rrdeng_metric_create(db_instance, &rd->metric_uuid);
151 + if(unlikely(!metric)) {
152 + if(unlikely(ctx->config.legacy)) {
153 + // this is a single host database
154 + // generate uuid from the chart and dimensions ids
155 + // and overwrite the one supplied by rrddim
156 + metric = rrdeng_metric_get_legacy(db_instance, rrddim_id(rd), rrdset_id(rd->rrdset));
157 + if (metric)
158 + uuid_copy(rd->metric_uuid, *mrg_metric_uuid(main_mrg, metric));
159 + }
160 +
161 + if(likely(!metric))
162 + metric = rrdeng_metric_create(db_instance, &rd->metric_uuid);
163 + }
164
165 #ifdef NETDATA_INTERNAL_CHECKS
166 if(uuid_compare(rd->metric_uuid, *mrg_metric_uuid(main_mrg, metric)) != 0) {
@@ -178,12 +184,75 @@ STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE
184 // ----------------------------------------------------------------------------
185 // collect ops
186
187 +static inline void check_and_fix_mrg_update_every(struct rrdeng_collect_handle *handle) {
188 + if(unlikely((time_t)(handle->update_every_ut / USEC_PER_SEC) != mrg_metric_get_update_every_s(main_mrg, handle->metric))) {
189 + internal_error(true, "DBENGINE: collection handle has update every %ld, but the metric registry has %ld. Fixing it.",
190 + (time_t)(handle->update_every_ut / USEC_PER_SEC), mrg_metric_get_update_every_s(main_mrg, handle->metric));
191 +
192 + if(unlikely(!handle->update_every_ut))
193 + handle->update_every_ut = mrg_metric_get_update_every_s(main_mrg, handle->metric) * USEC_PER_SEC;
194 + else
195 + mrg_metric_set_update_every(main_mrg, handle->metric, (time_t)(handle->update_every_ut / USEC_PER_SEC));
196 + }
197 +}
198 +
199 +static inline bool check_completed_page_consistency(struct rrdeng_collect_handle *handle __maybe_unused) {
200 +#ifdef NETDATA_INTERNAL_CHECKS
201 + if (unlikely(!handle->page || !handle->page_entries_max || !handle->page_position || !handle->page_end_time_ut))
202 + return false;
203 +
204 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
205 +
206 + uuid_t *uuid = mrg_metric_uuid(main_mrg, handle->metric);
207 + time_t start_time_s = pgc_page_start_time_s(handle->page);
208 + time_t end_time_s = pgc_page_end_time_s(handle->page);
209 + time_t update_every_s = pgc_page_update_every_s(handle->page);
210 + size_t page_length = handle->page_position * CTX_POINT_SIZE_BYTES(ctx);
211 + size_t entries = handle->page_position;
212 + time_t overwrite_zero_update_every_s = (time_t)(handle->update_every_ut / USEC_PER_SEC);
213 +
214 + if(end_time_s > max_acceptable_collected_time())
215 + handle->page_flags |= RRDENG_PAGE_COMPLETED_IN_FUTURE;
216 +
217 + VALIDATED_PAGE_DESCRIPTOR vd = validate_page(
218 + uuid,
219 + start_time_s,
220 + end_time_s,
221 + update_every_s,
222 + page_length,
223 + ctx->config.page_type,
224 + entries,
225 + 0, // do not check for future timestamps - we inherit the timestamps of the children
226 + overwrite_zero_update_every_s,
227 + false,
228 + false,
229 + "collected",
230 + handle->page_flags);
231 +
232 + return vd.is_valid;
233 +#else
234 + return true;
235 +#endif
236 +}
237 +
238 /*
239 * Gets a handle for storing metrics to the database.
240 * The handle must be released with rrdeng_store_metric_final().
241 */
242 STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every, STORAGE_METRICS_GROUP *smg) {
186 - METRIC *metric = mrg_metric_dup(main_mrg, (METRIC *)db_metric_handle);
243 + METRIC *metric = (METRIC *)db_metric_handle;
244 + struct rrdengine_instance *ctx = mrg_metric_ctx(metric);
245 +
246 + bool is_1st_metric_writer = true;
247 + if(!mrg_metric_writer_acquire(main_mrg, metric)) {
248 + is_1st_metric_writer = false;
249 + char uuid[UUID_STR_LEN + 1];
250 + uuid_unparse(*mrg_metric_uuid(main_mrg, metric), uuid);
251 + error("DBENGINE: metric '%s' is already collected and should not be collected twice - expect gaps on the charts", uuid);
252 + }
253 +
254 + metric = mrg_metric_dup(main_mrg, metric);
255 +
256 struct rrdeng_collect_handle *handle;
257
258 handle = callocz(1, sizeof(struct rrdeng_collect_handle));
@@ -192,6 +261,17 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metri
261 handle->page_position = 0;
262 handle->page_entries_max = 0;
263 handle->update_every_ut = update_every * USEC_PER_SEC;
264 + handle->options = is_1st_metric_writer ? RRDENG_1ST_METRIC_WRITER : 0;
265 +
266 + __atomic_add_fetch(&ctx->atomic.collectors_running, 1, __ATOMIC_RELAXED);
267 + if(!is_1st_metric_writer)
268 + __atomic_add_fetch(&ctx->atomic.collectors_running_duplicate, 1, __ATOMIC_RELAXED);
269 +
270 + // this is important!
271 + // if we don't set the page_end_time_ut during the first collection
272 + // data collection may be able to go back in time and during the addition of new pages
273 + // clean pages may be found matching ours!
274 + handle->page_end_time_ut = mrg_metric_get_latest_time_s(main_mrg, metric) * USEC_PER_SEC;
275
276 mrg_metric_set_update_every(main_mrg, metric, update_every);
277
@@ -247,6 +327,7 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h
327 pgc_page_to_clean_evict_or_release(main_cache, handle->page);
328
329 else {
330 + check_completed_page_consistency(handle);
331 mrg_metric_set_clean_latest_time_s(main_mrg, handle->metric, pgc_page_end_time_s(handle->page));
332 pgc_page_hot_to_dirty_and_release(main_cache, handle->page);
333 }
@@ -254,15 +335,21 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h
335 mrg_metric_set_hot_latest_time_s(main_mrg, handle->metric, 0);
336
337 handle->page = NULL;
338 + handle->page_flags = 0;
339 handle->page_position = 0;
340 handle->page_entries_max = 0;
341
260 - internal_fatal((time_t)(handle->update_every_ut / USEC_PER_SEC) != mrg_metric_get_update_every_s(main_mrg, handle->metric),
261 - "DBENGINE: the collection handle update every and the metric registry update every are not the same");
342 + // important!
343 + // we should never zero page end time ut, because this will allow
344 + // collection to go back in time
345 + // handle->page_end_time_ut = 0;
346 + // handle->page_start_time_ut;
347 +
348 + check_and_fix_mrg_update_every(handle);
349 }
350
351 static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *handle, struct rrdengine_instance *ctx, usec_t point_in_time_ut, void *data, size_t data_size) {
265 -time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
352 + time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
353 time_t update_every_s = (time_t)(handle->update_every_ut / USEC_PER_SEC);
354
355 PGC_ENTRY page_entry = {
@@ -276,30 +363,50 @@ time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
363 .hot = true
364 };
365
366 + size_t conflicts = 0;
367 bool added = true;
368 PGC_PAGE *page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
281 - if (unlikely(!added)) {
282 - internal_fatal(!pgc_is_page_hot(page),
283 - "DBENGINE CACHE: requested to add a hot page to the main cache, "
284 - "but the page returned is not hot");
369 + while (unlikely(!added)) {
370 + conflicts++;
371
286 - if(unlikely(pgc_page_data_size(main_cache, page) < CTX_POINT_SIZE_BYTES(ctx)))
287 - fatal("DBENGINE: hot page returned from main cache does not have the size for storing 1 point");
372 + char uuid[UUID_STR_LEN + 1];
373 + uuid_unparse(*mrg_metric_uuid(main_mrg, handle->metric), uuid);
374
289 - // copy the point in data
290 - memcpy(pgc_page_data(page), data, CTX_POINT_SIZE_BYTES(ctx));
291 -
292 - // free data
293 - dbengine_page_free(page_entry.data, data_size);
294 -
295 - handle->page_entries_max = pgc_page_data_size(main_cache, page) / CTX_POINT_SIZE_BYTES(ctx);
375 +#ifdef NETDATA_INTERNAL_CHECKS
376 + internal_error(true,
377 +#else
378 + error_limit_static_global_var(erl, 1, 0);
379 + error_limit(&erl,
380 +#endif
381 + "DBENGINE: metric '%s' new page from %ld to %ld, update every %ld, has a conflict in main cache "
382 + "with existing %s page from %ld to %ld, update every %ld - "
383 + "is it collected more than once?",
384 + uuid,
385 + page_entry.start_time_s, page_entry.end_time_s, (time_t)page_entry.update_every_s,
386 + pgc_is_page_hot(page) ? "hot" : "not-hot",
387 + pgc_page_start_time_s(page), pgc_page_end_time_s(page), pgc_page_update_every_s(page)
388 + );
389 +
390 + pgc_page_release(main_cache, page);
391 +
392 + point_in_time_ut -= handle->update_every_ut;
393 + point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
394 + page_entry.start_time_s = point_in_time_s;
395 + page_entry.end_time_s = point_in_time_s;
396 + page = pgc_page_add_and_acquire(main_cache, page_entry, &added);
397 }
297 - else
298 - handle->page_entries_max = data_size / CTX_POINT_SIZE_BYTES(ctx);
398
399 + handle->page_entries_max = data_size / CTX_POINT_SIZE_BYTES(ctx);
400 + handle->page_start_time_ut = point_in_time_ut;
401 handle->page_end_time_ut = point_in_time_ut;
402 handle->page_position = 1; // zero is already in our data
403 handle->page = page;
404 + handle->page_flags = conflicts? RRDENG_PAGE_CONFLICT : 0;
405 +
406 + if(point_in_time_s > max_acceptable_collected_time())
407 + handle->page_flags |= RRDENG_PAGE_CREATED_IN_FUTURE;
408 +
409 + check_and_fix_mrg_update_every(handle);
410 }
411
412 static void *rrdeng_alloc_new_metric_data(struct rrdeng_collect_handle *handle, size_t *data_size) {
@@ -341,14 +448,14 @@ static void *rrdeng_alloc_new_metric_data(struct rrdeng_collect_handle *handle,
448 return dbengine_page_alloc(size);
449 }
450
344 -static void rrdeng_store_metric_next_internal(STORAGE_COLLECT_HANDLE *collection_handle,
345 - usec_t point_in_time_ut,
346 - NETDATA_DOUBLE n,
347 - NETDATA_DOUBLE min_value,
348 - NETDATA_DOUBLE max_value,
349 - uint16_t count,
350 - uint16_t anomaly_count,
351 - SN_FLAGS flags)
451 +static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_handle,
452 + usec_t point_in_time_ut,
453 + NETDATA_DOUBLE n,
454 + NETDATA_DOUBLE min_value,
455 + NETDATA_DOUBLE max_value,
456 + uint16_t count,
457 + uint16_t anomaly_count,
458 + SN_FLAGS flags)
459 {
460 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
461 struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
@@ -372,7 +479,7 @@ static void rrdeng_store_metric_next_internal(STORAGE_COLLECT_HANDLE *collection
479 /* did the other metrics change page? */
480 handle->alignment->page_position <= 1)) {
481 handle->options &= ~RRDENG_CHO_UNALIGNED;
375 -
482 + handle->page_flags |= RRDENG_PAGE_UNALIGNED;
483 rrdeng_store_metric_flush_current_page(collection_handle);
484
485 data = rrdeng_alloc_new_metric_data(handle, &data_size);
@@ -430,6 +537,7 @@ static void rrdeng_store_metric_next_internal(STORAGE_COLLECT_HANDLE *collection
537
538 if(unlikely(++handle->page_position >= handle->page_entries_max)) {
539 internal_fatal(handle->page_position > handle->page_entries_max, "DBENGINE: exceeded page max number of points");
540 + handle->page_flags |= RRDENG_PAGE_FULL;
541 rrdeng_store_metric_flush_current_page(collection_handle);
542 }
543 }
@@ -441,6 +549,40 @@ static void rrdeng_store_metric_next_internal(STORAGE_COLLECT_HANDLE *collection
549 mrg_metric_set_hot_latest_time_s(main_mrg, handle->metric, (time_t) (point_in_time_ut / USEC_PER_SEC));
550 }
551
552 +static void store_metric_next_error_log(struct rrdeng_collect_handle *handle, usec_t point_in_time_ut, const char *msg) {
553 + time_t point_in_time_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
554 + char uuid[UUID_STR_LEN + 1];
555 + uuid_unparse(*mrg_metric_uuid(main_mrg, handle->metric), uuid);
556 +
557 + BUFFER *wb = NULL;
558 + if(handle->page && handle->page_flags) {
559 + wb = buffer_create(0, NULL);
560 + collect_page_flags_to_buffer(wb, handle->page_flags);
561 + }
562 +
563 +#ifdef NETDATA_INTERNAL_CHECKS
564 + internal_error(true,
565 +#else
566 + error_limit_static_global_var(erl, 1, 0);
567 + error_limit(&erl,
568 +#endif
569 + "DBENGINE: metric '%s' collected point at %ld, %s last collection at %ld, "
570 + "update every %ld, %s page from %ld to %ld, position %u (of %u), flags: %s",
571 + uuid,
572 + point_in_time_s,
573 + msg,
574 + (time_t)(handle->page_end_time_ut / USEC_PER_SEC),
575 + (time_t)(handle->update_every_ut / USEC_PER_SEC),
576 + handle->page ? "current" : "*LAST*",
577 + (time_t)(handle->page_start_time_ut / USEC_PER_SEC),
578 + (time_t)(handle->page_end_time_ut / USEC_PER_SEC),
579 + handle->page_position, handle->page_entries_max,
580 + wb ? buffer_tostring(wb) : ""
581 + );
582 +
583 + buffer_free(wb);
584 +}
585 +
586 void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
587 usec_t point_in_time_ut,
588 NETDATA_DOUBLE n,
@@ -452,54 +594,70 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
594 {
595 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
596
597 +#ifdef NETDATA_INTERNAL_CHECKS
598 + if(unlikely(point_in_time_ut > max_acceptable_collected_time() * USEC_PER_SEC))
599 + handle->page_flags |= RRDENG_PAGE_FUTURE_POINT;
600 +#endif
601 +
602 if(likely(handle->page_end_time_ut + handle->update_every_ut == point_in_time_ut)) {
603 // happy path
604 ;
605 }
606 else if(unlikely(point_in_time_ut < handle->page_end_time_ut)) {
460 - error_limit_static_global_var(erl, 1, 0);
461 - error_limit(&erl, "DBENGINE: new point at %llu is older than the last collected %llu, ignoring it",
462 - point_in_time_ut, handle->page_end_time_ut);
607 + handle->page_flags |= RRDENG_PAGE_PAST_COLLECTION;
608 + store_metric_next_error_log(handle, point_in_time_ut, "is older than the");
609 return;
610 }
611
612 else if(unlikely(point_in_time_ut == handle->page_end_time_ut)) {
467 - error_limit_static_global_var(erl, 1, 0);
468 - error_limit(&erl, "DBENGINE: new point time %llu has the same timestamp to the last collected point, ignoring it",
469 - point_in_time_ut);
613 + handle->page_flags |= RRDENG_PAGE_REPEATED_COLLECTION;
614 + store_metric_next_error_log(handle, point_in_time_ut, "is at the same time as the");
615 return;
616 }
617
618 else if(handle->page) {
474 - size_t points_gap = (point_in_time_ut - handle->page_end_time_ut) / handle->update_every_ut;
475 - size_t page_remaining_points = handle->page_entries_max - handle->page_position;
619 + usec_t delta_ut = point_in_time_ut - handle->page_end_time_ut;
620
477 - if(points_gap > page_remaining_points)
621 + if(unlikely(delta_ut < handle->update_every_ut)) {
622 + handle->page_flags |= RRDENG_PAGE_STEP_TOO_SMALL;
623 + rrdeng_store_metric_flush_current_page(collection_handle);
624 + }
625 + else if(unlikely(delta_ut % handle->update_every_ut)) {
626 + handle->page_flags |= RRDENG_PAGE_STEP_UNALIGNED;
627 rrdeng_store_metric_flush_current_page(collection_handle);
628 + }
629 else {
480 - // loop to fill the gap
481 - usec_t last_point_filled_ut = handle->page_end_time_ut + handle->update_every_ut;
482 -
483 - while (last_point_filled_ut < point_in_time_ut) {
484 - rrdeng_store_metric_next_internal(
485 - collection_handle, last_point_filled_ut,
486 - NAN, NAN, NAN,
487 - 1, 0, SN_EMPTY_SLOT);
630 + size_t points_gap = delta_ut / handle->update_every_ut;
631 + size_t page_remaining_points = handle->page_entries_max - handle->page_position;
632
489 - last_point_filled_ut += handle->update_every_ut;
633 + if(points_gap >= page_remaining_points) {
634 + handle->page_flags |= RRDENG_PAGE_BIG_GAP;
635 + rrdeng_store_metric_flush_current_page(collection_handle);
636 + }
637 + else {
638 + // loop to fill the gap
639 + handle->page_flags |= RRDENG_PAGE_GAP;
640 +
641 + usec_t point_in_time_to_stop_ut = point_in_time_ut - handle->update_every_ut;
642 + for(usec_t next_point_in_time = handle->page_end_time_ut + handle->update_every_ut;
643 + next_point_in_time <= point_in_time_to_stop_ut ;
644 + next_point_in_time = handle->page_end_time_ut + handle->update_every_ut) {
645 + rrdeng_store_metric_append_point(
646 + collection_handle,
647 + handle->page_end_time_ut + handle->update_every_ut,
648 + NAN, NAN, NAN,
649 + 1, 0,
650 + SN_EMPTY_SLOT);
651 + }
652 }
653 }
654 }
655
494 - internal_fatal((time_t)(handle->update_every_ut / USEC_PER_SEC) !=
495 - mrg_metric_get_update_every_s(main_mrg, handle->metric),
496 - "DBENGINE: the collection handle update every and the metric registry update every are not the same");
497 -
498 -// FIXME - is this a problem?
499 -// internal_fatal((point_in_time_ut - handle->page_end_time_ut) % handle->update_every_ut,
500 -// "DBENGINE: new point is not aligned to update every");
501 -
502 - rrdeng_store_metric_next_internal(collection_handle, point_in_time_ut, n, min_value, max_value, count, anomaly_count, flags);
656 + rrdeng_store_metric_append_point(collection_handle,
657 + point_in_time_ut,
658 + n, min_value, max_value,
659 + count, anomaly_count,
660 + flags);
661 }
662
663 /*
@@ -508,9 +666,19 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
666 */
667 int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) {
668 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
669 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
670
671 + handle->page_flags |= RRDENG_PAGE_COLLECT_FINALIZE;
672 rrdeng_store_metric_flush_current_page(collection_handle);
673 rrdeng_page_alignment_release(handle->alignment);
674 +
675 + __atomic_sub_fetch(&ctx->atomic.collectors_running, 1, __ATOMIC_RELAXED);
676 + if(!(handle->options & RRDENG_1ST_METRIC_WRITER))
677 + __atomic_sub_fetch(&ctx->atomic.collectors_running_duplicate, 1, __ATOMIC_RELAXED);
678 +
679 + if((handle->options & RRDENG_1ST_METRIC_WRITER) && !mrg_metric_writer_release(main_mrg, handle->metric))
680 + internal_fatal(true, "DBENGINE: metric is already released");
681 +
682 mrg_metric_release(main_mrg, handle->metric);
683 freez(handle);
684
@@ -519,15 +687,15 @@ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) {
687
688 void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every) {
689 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
690 + check_and_fix_mrg_update_every(handle);
691 +
692 METRIC *metric = handle->metric;
693 usec_t update_every_ut = update_every * USEC_PER_SEC;
694
525 - internal_fatal((time_t)(handle->update_every_ut / USEC_PER_SEC) != mrg_metric_get_update_every_s(main_mrg, metric),
526 - "DBENGINE: the collection handle update every and the metric registry update every are not the same");
527 -
695 if(update_every_ut == handle->update_every_ut)
696 return;
697
698 + handle->page_flags |= RRDENG_PAGE_UPDATE_EVERY_CHANGE;
699 rrdeng_store_metric_flush_current_page(collection_handle);
700 mrg_metric_set_update_every(main_mrg, metric, update_every);
701 handle->update_every_ut = update_every_ut;
@@ -809,43 +977,43 @@ void rrdeng_get_37_statistics(struct rrdengine_instance *ctx, unsigned long long
977 if (ctx == NULL)
978 return;
979
812 - array[0] = (uint64_t)ctx->stats.metric_API_producers;
813 - array[1] = (uint64_t)ctx->stats.metric_API_consumers;
980 + array[0] = (uint64_t)__atomic_load_n(&ctx->atomic.collectors_running, __ATOMIC_RELAXED); // API producers
981 + array[1] = (uint64_t)__atomic_load_n(&ctx->atomic.inflight_queries, __ATOMIC_RELAXED); // API consumers
982 array[2] = 0;
983 array[3] = 0;
984 array[4] = 0;
817 - array[5] = (uint64_t)ctx->stats.pg_cache_insertions;
818 - array[6] = (uint64_t)ctx->stats.pg_cache_deletions;
819 - array[7] = (uint64_t)ctx->stats.pg_cache_hits;
820 - array[8] = (uint64_t)ctx->stats.pg_cache_misses;
821 - array[9] = (uint64_t)ctx->stats.pg_cache_backfills;
822 - array[10] = (uint64_t)ctx->stats.pg_cache_evictions;
823 - array[11] = (uint64_t)ctx->stats.before_compress_bytes;
824 - array[12] = (uint64_t)ctx->stats.after_compress_bytes;
825 - array[13] = (uint64_t)ctx->stats.before_decompress_bytes;
826 - array[14] = (uint64_t)ctx->stats.after_decompress_bytes;
827 - array[15] = (uint64_t)ctx->stats.io_write_bytes;
828 - array[16] = (uint64_t)ctx->stats.io_write_requests;
829 - array[17] = (uint64_t)ctx->stats.io_read_bytes;
830 - array[18] = (uint64_t)ctx->stats.io_read_requests;
831 - array[19] = (uint64_t)ctx->stats.io_write_extent_bytes;
832 - array[20] = (uint64_t)ctx->stats.io_write_extents;
833 - array[21] = (uint64_t)ctx->stats.io_read_extent_bytes;
834 - array[22] = (uint64_t)ctx->stats.io_read_extents;
835 - array[23] = (uint64_t)ctx->stats.datafile_creations;
836 - array[24] = (uint64_t)ctx->stats.datafile_deletions;
837 - array[25] = (uint64_t)ctx->stats.journalfile_creations;
838 - array[26] = (uint64_t)ctx->stats.journalfile_deletions;
839 - array[27] = (uint64_t)ctx->stats.page_cache_descriptors;
840 - array[28] = (uint64_t)ctx->stats.io_errors;
841 - array[29] = (uint64_t)ctx->stats.fs_errors;
842 - array[30] = (uint64_t)global_io_errors;
843 - array[31] = (uint64_t)global_fs_errors;
844 - array[32] = (uint64_t)rrdeng_reserved_file_descriptors;
845 - array[33] = (uint64_t)ctx->stats.pg_cache_over_half_dirty_events;
846 - array[34] = (uint64_t)global_pg_cache_over_half_dirty_events;
847 - array[35] = (uint64_t)ctx->stats.flushing_pressure_page_deletions;
848 - array[36] = (uint64_t)global_flushing_pressure_page_deletions;
985 + array[5] = 0; // (uint64_t)ctx->stats.pg_cache_insertions;
986 + array[6] = 0; // (uint64_t)ctx->stats.pg_cache_deletions;
987 + array[7] = 0; // (uint64_t)ctx->stats.pg_cache_hits;
988 + array[8] = 0; // (uint64_t)ctx->stats.pg_cache_misses;
989 + array[9] = 0; // (uint64_t)ctx->stats.pg_cache_backfills;
990 + array[10] = 0; // (uint64_t)ctx->stats.pg_cache_evictions;
991 + array[11] = (uint64_t)__atomic_load_n(&ctx->stats.before_compress_bytes, __ATOMIC_RELAXED); // used
992 + array[12] = (uint64_t)__atomic_load_n(&ctx->stats.after_compress_bytes, __ATOMIC_RELAXED); // used
993 + array[13] = (uint64_t)__atomic_load_n(&ctx->stats.before_decompress_bytes, __ATOMIC_RELAXED);
994 + array[14] = (uint64_t)__atomic_load_n(&ctx->stats.after_decompress_bytes, __ATOMIC_RELAXED);
995 + array[15] = (uint64_t)__atomic_load_n(&ctx->stats.io_write_bytes, __ATOMIC_RELAXED); // used
996 + array[16] = (uint64_t)__atomic_load_n(&ctx->stats.io_write_requests, __ATOMIC_RELAXED); // used
997 + array[17] = (uint64_t)__atomic_load_n(&ctx->stats.io_read_bytes, __ATOMIC_RELAXED);
998 + array[18] = (uint64_t)__atomic_load_n(&ctx->stats.io_read_requests, __ATOMIC_RELAXED); // used
999 + array[19] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.io_write_extent_bytes, __ATOMIC_RELAXED);
1000 + array[20] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.io_write_extents, __ATOMIC_RELAXED);
1001 + array[21] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.io_read_extent_bytes, __ATOMIC_RELAXED);
1002 + array[22] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.io_read_extents, __ATOMIC_RELAXED);
1003 + array[23] = (uint64_t)__atomic_load_n(&ctx->stats.datafile_creations, __ATOMIC_RELAXED);
1004 + array[24] = (uint64_t)__atomic_load_n(&ctx->stats.datafile_deletions, __ATOMIC_RELAXED);
1005 + array[25] = (uint64_t)__atomic_load_n(&ctx->stats.journalfile_creations, __ATOMIC_RELAXED);
1006 + array[26] = (uint64_t)__atomic_load_n(&ctx->stats.journalfile_deletions, __ATOMIC_RELAXED);
1007 + array[27] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.page_cache_descriptors, __ATOMIC_RELAXED);
1008 + array[28] = (uint64_t)__atomic_load_n(&ctx->stats.io_errors, __ATOMIC_RELAXED);
1009 + array[29] = (uint64_t)__atomic_load_n(&ctx->stats.fs_errors, __ATOMIC_RELAXED);
1010 + array[30] = (uint64_t)__atomic_load_n(&global_io_errors, __ATOMIC_RELAXED); // used
1011 + array[31] = (uint64_t)__atomic_load_n(&global_fs_errors, __ATOMIC_RELAXED); // used
1012 + array[32] = (uint64_t)__atomic_load_n(&rrdeng_reserved_file_descriptors, __ATOMIC_RELAXED); // used
1013 + array[33] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.pg_cache_over_half_dirty_events, __ATOMIC_RELAXED);
1014 + array[34] = (uint64_t)__atomic_load_n(&global_pg_cache_over_half_dirty_events, __ATOMIC_RELAXED); // used
1015 + array[35] = 0; // (uint64_t)__atomic_load_n(&ctx->stats.flushing_pressure_page_deletions, __ATOMIC_RELAXED);
1016 + array[36] = (uint64_t)__atomic_load_n(&global_flushing_pressure_page_deletions, __ATOMIC_RELAXED); // used
1017 array[37] = 0; //(uint64_t)pg_cache->active_descriptors;
1018
1019 fatal_assert(RRDENG_NR_STATS == 38);
@@ -944,10 +1112,13 @@ int rrdeng_init(struct rrdengine_instance **ctxp, char *dbfiles_path, unsigned p
1112 if(NULL == ctxp) {
1113 ctx = multidb_ctx[tier];
1114 memset(ctx, 0, sizeof(*ctx));
1115 + ctx->config.legacy = false;
1116 }
1117 else {
1118 *ctxp = ctx = callocz(1, sizeof(*ctx));
1119 + ctx->config.legacy = true;
1120 }
1121 +
1122 ctx->config.tier = (int)tier;
1123 ctx->config.page_type = tier_page_type[tier];
1124 ctx->config.global_compress_alg = RRD_LZ4;
@@ -1100,7 +1271,7 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) {
1271 }
1272 uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1273
1103 - stats.currently_collected_metrics = ctx->stats.metric_API_producers;
1274 + stats.currently_collected_metrics = __atomic_load_n(&ctx->atomic.collectors_running, __ATOMIC_RELAXED);
1275
1276 internal_error(stats.metrics_pages != stats.extents_pages + stats.currently_collected_metrics,
1277 "DBENGINE: metrics pages is %zu, but extents pages is %zu and API consumers is %zu",
database/engine/rrdenginelib.c
-81
@@ -142,87 +142,6 @@ int open_file_for_io(char *path, int flags, uv_file *file, int direct)
142 return fd;
143 }
144
145 -char *get_rrdeng_statistics(struct rrdengine_instance *ctx, char *str, size_t size)
146 -{
147 - snprintfz(str, size,
148 - "metric_API_producers: %ld\n"
149 - "metric_API_consumers: %ld\n"
150 - "page_cache_total_pages: %ld\n"
151 - "page_cache_descriptors: %ld\n"
152 - "page_cache_populated_pages: %ld\n"
153 - "page_cache_committed_pages: %ld\n"
154 - "page_cache_insertions: %ld\n"
155 - "page_cache_deletions: %ld\n"
156 - "page_cache_hits: %ld\n"
157 - "page_cache_misses: %ld\n"
158 - "page_cache_backfills: %ld\n"
159 - "page_cache_evictions: %ld\n"
160 - "compress_before_bytes: %ld\n"
161 - "compress_after_bytes: %ld\n"
162 - "decompress_before_bytes: %ld\n"
163 - "decompress_after_bytes: %ld\n"
164 - "io_write_bytes: %ld\n"
165 - "io_write_requests: %ld\n"
166 - "io_read_bytes: %ld\n"
167 - "io_read_requests: %ld\n"
168 - "io_write_extent_bytes: %ld\n"
169 - "io_write_extents: %ld\n"
170 - "io_read_extent_bytes: %ld\n"
171 - "io_read_extents: %ld\n"
172 - "datafile_creations: %ld\n"
173 - "datafile_deletions: %ld\n"
174 - "journalfile_creations: %ld\n"
175 - "journalfile_deletions: %ld\n"
176 - "io_errors: %ld\n"
177 - "fs_errors: %ld\n"
178 - "global_io_errors: %ld\n"
179 - "global_fs_errors: %ld\n"
180 - "rrdeng_reserved_file_descriptors: %ld\n"
181 - "pg_cache_over_half_dirty_events: %ld\n"
182 - "global_pg_cache_over_half_dirty_events: %ld\n"
183 - "flushing_pressure_page_deletions: %ld\n"
184 - "global_flushing_pressure_page_deletions: %ld\n",
185 - (long)ctx->stats.metric_API_producers,
186 - (long)ctx->stats.metric_API_consumers,
187 - 0L,
188 - (long)ctx->stats.page_cache_descriptors,
189 - 0L,
190 - 0L,
191 - (long)ctx->stats.pg_cache_insertions,
192 - (long)ctx->stats.pg_cache_deletions,
193 - (long)ctx->stats.pg_cache_hits,
194 - (long)ctx->stats.pg_cache_misses,
195 - (long)ctx->stats.pg_cache_backfills,
196 - (long)ctx->stats.pg_cache_evictions,
197 - (long)ctx->stats.before_compress_bytes,
198 - (long)ctx->stats.after_compress_bytes,
199 - (long)ctx->stats.before_decompress_bytes,
200 - (long)ctx->stats.after_decompress_bytes,
201 - (long)ctx->stats.io_write_bytes,
202 - (long)ctx->stats.io_write_requests,
203 - (long)ctx->stats.io_read_bytes,
204 - (long)ctx->stats.io_read_requests,
205 - (long)ctx->stats.io_write_extent_bytes,
206 - (long)ctx->stats.io_write_extents,
207 - (long)ctx->stats.io_read_extent_bytes,
208 - (long)ctx->stats.io_read_extents,
209 - (long)ctx->stats.datafile_creations,
210 - (long)ctx->stats.datafile_deletions,
211 - (long)ctx->stats.journalfile_creations,
212 - (long)ctx->stats.journalfile_deletions,
213 - (long)ctx->stats.io_errors,
214 - (long)ctx->stats.fs_errors,
215 - (long)global_io_errors,
216 - (long)global_fs_errors,
217 - (long)rrdeng_reserved_file_descriptors,
218 - (long)ctx->stats.pg_cache_over_half_dirty_events,
219 - (long)global_pg_cache_over_half_dirty_events,
220 - (long)ctx->stats.flushing_pressure_page_deletions,
221 - (long)global_flushing_pressure_page_deletions
222 - );
223 - return str;
224 -}
225 -
145 int is_legacy_child(const char *machine_guid)
146 {
147 uuid_t uuid;
database/engine/rrdenginelib.h
-1
@@ -92,7 +92,6 @@ static inline int open_file_buffered_io(char *path, int flags, uv_file *file)
92 {
93 return open_file_for_io(path, flags, file, 0);
94 }
95 -char *get_rrdeng_statistics(struct rrdengine_instance *ctx, char *str, size_t size);
95 int compute_multidb_diskspace();
96 int is_legacy_child(const char *machine_guid);
97
database/rrddim.c
+1 -19
@@ -86,26 +86,8 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
86
87 rd->rrd_memory_mode = ctr->memory_mode;
88
89 - if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &(rd->metric_uuid)))) {
89 + if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &(rd->metric_uuid))))
90 uuid_generate(rd->metric_uuid);
91 - bool found_in_sql = false; (void)found_in_sql;
92 -
93 -// bool found_in_sql = true;
94 -// if(unlikely(sql_find_dimension_uuid(st, rd, &rd->metric_uuid))) {
95 -// found_in_sql = false;
96 -// uuid_generate(rd->metric_uuid);
97 -// }
98 -
99 -#ifdef NETDATA_INTERNAL_CHECKS
100 - char uuid_str[UUID_STR_LEN];
101 - uuid_unparse_lower(rd->metric_uuid, uuid_str);
102 - error_report("Dimension UUID for host %s chart [%s] dimension [%s] not found in context. It is now set to %s (%s)",
103 - string2str(host->hostname),
104 - string2str(st->name),
105 - string2str(rd->name),
106 - uuid_str, found_in_sql ? "found in sqlite" : "newly generated");
107 -#endif
108 - }
91
92 // initialize the db tiers
93 {
database/rrdset.c
+1 -17
@@ -373,24 +373,8 @@ static void rrdset_react_callback(const DICTIONARY_ITEM *item __maybe_unused, vo
373
374 if(ctr->react_action & (RRDSET_REACT_NEW | RRDSET_REACT_PLUGIN_UPDATED | RRDSET_REACT_MODULE_UPDATED)) {
375 if (ctr->react_action & RRDSET_REACT_NEW) {
376 - if(unlikely(rrdcontext_find_chart_uuid(st, &st->chart_uuid))) {
376 + if(unlikely(rrdcontext_find_chart_uuid(st, &st->chart_uuid)))
377 uuid_generate(st->chart_uuid);
378 - bool found_in_sql = false; (void)found_in_sql;
379 -
380 -// bool found_in_sql = true;
381 -// if(unlikely(sql_find_chart_uuid(host, st, &st->chart_uuid))) {
382 -// uuid_generate(st->chart_uuid);
383 -// found_in_sql = false;
384 -// }
385 -
386 -#ifdef NETDATA_INTERNAL_CHECKS
387 - char uuid_str[UUID_STR_LEN];
388 - uuid_unparse_lower(st->chart_uuid, uuid_str);
389 - error_report("Chart UUID for host %s chart [%s] not found in context. It is now set to %s (%s)",
390 - string2str(host->hostname),
391 - string2str(st->name), uuid_str, found_in_sql ? "found in sqlite" : "newly generated");
392 -#endif
393 - }
378 }
379 rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE);
380 rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
database/sqlite/sqlite_aclk.c
+1
@@ -492,6 +492,7 @@ static void timer_cb(uv_timer_t* handle)
492
493 static void aclk_database_worker(void *arg)
494 {
495 + service_register(SERVICE_THREAD_TYPE_EVENT_LOOP, NULL, NULL, NULL, true);
496 worker_register("ACLKSYNC");
497 worker_register_job_name(ACLK_DATABASE_NOOP, "noop");
498 worker_register_job_name(ACLK_DATABASE_ORPHAN_HOST, "node orphan");
database/sqlite/sqlite_metadata.c
+1
@@ -1022,6 +1022,7 @@ static void start_metadata_hosts(uv_work_t *req __maybe_unused)
1022
1023 static void metadata_event_loop(void *arg)
1024 {
1025 + service_register(SERVICE_THREAD_TYPE_EVENT_LOOP, NULL, NULL, NULL, true);
1026 worker_register("METASYNC");
1027 worker_register_job_name(METADATA_DATABASE_NOOP, "noop");
1028 worker_register_job_name(METADATA_DATABASE_TIMER, "timer");
health/health.c
+22
@@ -1058,6 +1058,9 @@ void *health_main(void *ptr) {
1058
1059 rrdhost_foreach_read(host) {
1060
1061 + if(unlikely(!service_running(SERVICE_HEALTH)))
1062 + break;
1063 +
1064 if (unlikely(!host->health.health_enabled))
1065 continue;
1066
@@ -1107,6 +1110,9 @@ void *health_main(void *ptr) {
1110 // the first loop is to lookup values from the db
1111 foreach_rrdcalc_in_rrdhost_read(host, rc) {
1112
1113 + if(unlikely(!service_running(SERVICE_HEALTH)))
1114 + break;
1115 +
1116 rrdcalc_update_info_using_rrdset_labels(rc);
1117
1118 if (update_disabled_silenced(host, rc))
@@ -1251,6 +1257,9 @@ void *health_main(void *ptr) {
1257
1258 if (unlikely(runnable && service_running(SERVICE_HEALTH))) {
1259 foreach_rrdcalc_in_rrdhost_read(host, rc) {
1260 + if(unlikely(!service_running(SERVICE_HEALTH)))
1261 + break;
1262 +
1263 if (unlikely(!(rc->run_flags & RRDCALC_FLAG_RUNNABLE)))
1264 continue;
1265
@@ -1431,6 +1440,9 @@ void *health_main(void *ptr) {
1440
1441 // process repeating alarms
1442 foreach_rrdcalc_in_rrdhost_read(host, rc) {
1443 + if(unlikely(!service_running(SERVICE_HEALTH)))
1444 + break;
1445 +
1446 int repeat_every = 0;
1447 if(unlikely(rrdcalc_isrepeating(rc) && rc->delay_up_to_timestamp <= now)) {
1448 if(unlikely(rc->status == RRDCALC_STATUS_WARNING)) {
@@ -1514,6 +1526,9 @@ void *health_main(void *ptr) {
1526 // wait for all notifications to finish before allowing health to be cleaned up
1527 ALARM_ENTRY *ae;
1528 while (NULL != (ae = alarm_notifications_in_progress.head)) {
1529 + if(unlikely(!service_running(SERVICE_HEALTH)))
1530 + break;
1531 +
1532 health_alarm_wait_for_execution(ae);
1533 }
1534 break;
@@ -1525,14 +1540,21 @@ void *health_main(void *ptr) {
1540 // wait for all notifications to finish before allowing health to be cleaned up
1541 ALARM_ENTRY *ae;
1542 while (NULL != (ae = alarm_notifications_in_progress.head)) {
1543 + if(unlikely(!service_running(SERVICE_HEALTH)))
1544 + break;
1545 +
1546 health_alarm_wait_for_execution(ae);
1547 }
1548
1549 #ifdef ENABLE_ACLK
1550 if (netdata_cloud_setting && unlikely(aclk_alert_reloaded) && loop > (marked_aclk_reload_loop + 2)) {
1551 rrdhost_foreach_read(host) {
1552 + if(unlikely(!service_running(SERVICE_HEALTH)))
1553 + break;
1554 +
1555 if (unlikely(!host->health.health_enabled))
1556 continue;
1557 +
1558 sql_queue_removed_alerts_to_aclk(host);
1559 }
1560 aclk_alert_reloaded = 0;
streaming/receiver.c
+8 -4
@@ -330,11 +330,13 @@ static void streaming_parser_thread_cleanup(void *ptr) {
330 parser_destroy(parser);
331 }
332
333 +bool plugin_is_enabled(struct plugind *cd);
334 +
335 static size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, int fd, void *ssl) {
336 size_t result;
337
338 PARSER_USER_OBJECT user = {
337 - .enabled = cd->enabled,
339 + .enabled = plugin_is_enabled(cd),
340 .host = rpt->host,
341 .opaque = rpt,
342 .cd = cd,
@@ -721,12 +723,14 @@ static int rrdpush_receive(struct receiver_state *rpt)
723
724
725 struct plugind cd = {
724 - .enabled = 1,
726 .update_every = default_rrd_update_every,
726 - .pid = 0,
727 .serial_failures = 0,
728 .successful_collections = 0,
729 - .obsolete = 0,
729 + .unsafe = {
730 + .spinlock = NETDATA_SPINLOCK_INITIALIZER,
731 + .running = true,
732 + .enabled = true,
733 + },
734 .started_t = now_realtime_sec(),
735 .next = NULL,
736 .capabilities = 0,
streaming/replication.c
+40 -24
@@ -108,9 +108,10 @@ static struct replication_query *replication_query_prepare(
108 time_t db_last_entry,
109 time_t requested_after,
110 time_t requested_before,
111 + bool requested_enable_streaming,
112 time_t query_after,
113 time_t query_before,
113 - bool enable_streaming,
114 + bool query_enable_streaming,
115 time_t wall_clock_time
116 ) {
117 size_t dimensions = rrdset_number_of_dimensions(st);
@@ -125,11 +126,11 @@ static struct replication_query *replication_query_prepare(
126
127 q->request.after = requested_after,
128 q->request.before = requested_before,
128 - q->request.enable_streaming = enable_streaming,
129 + q->request.enable_streaming = requested_enable_streaming,
130
131 q->query.after = query_after;
132 q->query.before = query_before;
132 - q->query.enable_streaming = enable_streaming;
133 + q->query.enable_streaming = query_enable_streaming;
134
135 q->wall_clock_time = wall_clock_time;
136
@@ -144,6 +145,7 @@ static struct replication_query *replication_query_prepare(
145 q->query.locked_data_collection = true;
146
147 if (st->last_updated.tv_sec > q->query.before) {
148 +#ifdef NETDATA_LOG_REPLICATION_REQUESTS
149 internal_error(true,
150 "STREAM_SENDER REPLAY: 'host:%s/chart:%s' "
151 "has start_streaming = true, "
@@ -152,6 +154,7 @@ static struct replication_query *replication_query_prepare(
154 (unsigned long long) q->query.before,
155 (unsigned long long) st->last_updated.tv_sec
156 );
157 +#endif
158 q->query.before = st->last_updated.tv_sec;
159 }
160 }
@@ -339,13 +342,15 @@ static void replication_query_execute(BUFFER *wb, struct replication_query *q, s
342 #endif
343
344 if(buffer_strlen(wb) > max_msg_size && last_end_time_in_buffer) {
342 - internal_error(true, "REPLICATION: buffer size %zu is more than the max message size %zu for chart '%s' of host '%s'."
343 - "Interrupting replication query at %ld, before the expected %ld.",
344 - buffer_strlen(wb), max_msg_size, rrdset_id(q->st), rrdhost_hostname(q->st->rrdhost),
345 - last_end_time_in_buffer, q->query.before);
346 -
345 q->query.before = last_end_time_in_buffer;
346 q->query.enable_streaming = false;
347 +
348 + internal_error(true, "REPLICATION: buffer size %zu is more than the max message size %zu for chart '%s' of host '%s'. "
349 + "Interrupting replication request (%ld to %ld, %s) at %ld to %ld, %s.",
350 + buffer_strlen(wb), max_msg_size, rrdset_id(q->st), rrdhost_hostname(q->st->rrdhost),
351 + q->request.after, q->request.before, q->request.enable_streaming?"true":"false",
352 + q->query.after, q->query.before, q->query.enable_streaming?"true":"false");
353 +
354 q->query.interrupted = true;
355
356 break;
@@ -424,37 +429,48 @@ static void replication_send_chart_collection_state(BUFFER *wb, RRDSET *st) {
429 );
430 }
431
427 -static struct replication_query *replication_response_prepare(RRDSET *st, bool start_streaming, time_t requested_after, time_t requested_before) {
432 +static struct replication_query *replication_response_prepare(RRDSET *st, bool requested_enable_streaming, time_t requested_after, time_t requested_before) {
433 time_t query_after = requested_after;
434 time_t query_before = requested_before;
435 + bool query_enable_streaming = requested_enable_streaming;
436 +
437 time_t wall_clock_time = now_realtime_sec();
438
439 time_t db_first_entry, db_last_entry;
440 rrdset_get_retention_of_tier_for_collected_chart(st, &db_first_entry, &db_last_entry, wall_clock_time, 0);
441
435 - if (query_after < db_first_entry)
436 - query_after = db_first_entry;
442 + if(requested_after == 0 && requested_before == 0 && requested_enable_streaming == true) {
443 + // no data requested - just enable streaming
444 + ;
445 + }
446 + else {
447 + if (query_after < db_first_entry)
448 + query_after = db_first_entry;
449 +
450 + if (query_before > db_last_entry)
451 + query_before = db_last_entry;
452
438 - if (query_before > db_last_entry)
439 - query_before = db_last_entry;
453 + // if the parent asked us to start streaming, then fill the rest with the data that we have
454 + if (requested_enable_streaming)
455 + query_before = db_last_entry;
456
441 - // if the parent asked us to start streaming, then fill the rest with the data that we have
442 - if (start_streaming)
443 - query_before = db_last_entry;
457 + if (query_after > query_before) {
458 + time_t tmp = query_before;
459 + query_before = query_after;
460 + query_after = tmp;
461 + }
462
445 - if (query_after > query_before) {
446 - time_t tmp = query_before;
447 - query_before = query_after;
448 - query_after = tmp;
463 + query_enable_streaming = (requested_enable_streaming ||
464 + query_before == db_last_entry ||
465 + !requested_after ||
466 + !requested_before) ? true : false;
467 }
468
451 - bool enable_streaming = (start_streaming || query_before == db_last_entry || !requested_after || !requested_before) ? true : false;
452 -
469 return replication_query_prepare(
470 st,
471 db_first_entry, db_last_entry,
456 - requested_after, requested_before,
457 - query_after, query_before, enable_streaming,
472 + requested_after, requested_before, requested_enable_streaming,
473 + query_after, query_before, query_enable_streaming,
474 wall_clock_time);
475 }
476