1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-#include "rrdcontext.h"
4
-#include "sqlite/sqlite_context.h"
5
-#include "aclk/schema-wrappers/context.h"
6
-#include "aclk/aclk_contexts_api.h"
7
-#include "aclk/aclk.h"
8
-#include "storage_engine.h"
9
-
10
-#define MESSAGES_PER_BUNDLE_TO_SEND_TO_HUB_PER_HOST 5000
11
-#define FULL_RETENTION_SCAN_DELAY_AFTER_DB_ROTATION_SECS 120
12
-#define RRDCONTEXT_WORKER_THREAD_HEARTBEAT_USEC (1000 * USEC_PER_MS)
13
-#define RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY 10
14
-
15
-#define LOG_TRANSITIONS false
16
-
17
-#define WORKER_JOB_HOSTS 1
18
-#define WORKER_JOB_CHECK 2
19
-#define WORKER_JOB_SEND 3
20
-#define WORKER_JOB_DEQUEUE 4
21
-#define WORKER_JOB_RETENTION 5
22
-#define WORKER_JOB_QUEUED 6
23
-#define WORKER_JOB_CLEANUP 7
24
-#define WORKER_JOB_CLEANUP_DELETE 8
25
-#define WORKER_JOB_PP_METRIC 9 // post-processing metrics
26
-#define WORKER_JOB_PP_INSTANCE 10 // post-processing instances
27
-#define WORKER_JOB_PP_CONTEXT 11 // post-processing contexts
28
-#define WORKER_JOB_HUB_QUEUE_SIZE 12
29
-#define WORKER_JOB_PP_QUEUE_SIZE 13
30
-
31
-
32
-typedef enum __attribute__ ((__packed__)) {
33
- RRD_FLAG_NONE = 0,
34
- RRD_FLAG_DELETED = (1 << 0), // this is a deleted object (metrics, instances, contexts)
35
- RRD_FLAG_COLLECTED = (1 << 1), // this object is currently being collected
36
- RRD_FLAG_UPDATED = (1 << 2), // this object has updates to propagate
37
- RRD_FLAG_ARCHIVED = (1 << 3), // this object is not currently being collected
38
- RRD_FLAG_OWN_LABELS = (1 << 4), // this instance has its own labels - not linked to an RRDSET
39
- RRD_FLAG_LIVE_RETENTION = (1 << 5), // we have got live retention from the database
40
- RRD_FLAG_QUEUED_FOR_HUB = (1 << 6), // this context is currently queued to be dispatched to hub
41
- RRD_FLAG_QUEUED_FOR_PP = (1 << 7), // this context is currently queued to be post-processed
42
- RRD_FLAG_HIDDEN = (1 << 8), // don't expose this to the hub or the API
43
-
44
- RRD_FLAG_UPDATE_REASON_TRIGGERED = (1 << 9), // the update was triggered by the child object
45
- RRD_FLAG_UPDATE_REASON_LOAD_SQL = (1 << 10), // this object has just been loaded from SQL
46
- RRD_FLAG_UPDATE_REASON_NEW_OBJECT = (1 << 11), // this object has just been created
47
- RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT = (1 << 12), // we received an update on this object
48
- RRD_FLAG_UPDATE_REASON_CHANGED_LINKING = (1 << 13), // an instance or a metric switched RRDSET or RRDDIM
49
- RRD_FLAG_UPDATE_REASON_CHANGED_METADATA = (1 << 14), // this context or instance changed uuid, name, units, title, family, chart type, priority, update every, rrd changed flags
50
- RRD_FLAG_UPDATE_REASON_ZERO_RETENTION = (1 << 15), // this object has no retention
51
- RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T = (1 << 16), // this object changed its oldest time in the db
52
- RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T = (1 << 17), // this object change its latest time in the db
53
- RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED = (1 << 18), // this object has stopped being collected
54
- RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED = (1 << 19), // this object has started being collected
55
- RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD = (1 << 20), // this context belongs to a host that just disconnected
56
- RRD_FLAG_UPDATE_REASON_UNUSED = (1 << 21), // this context is not used anymore
57
- RRD_FLAG_UPDATE_REASON_DB_ROTATION = (1 << 22), // this context changed because of a db rotation
58
-
59
- // action to perform on an object
60
- RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION = (1 << 30), // this object has to update its retention from the db
61
-} RRD_FLAGS;
62
-
63
-#define RRD_FLAG_ALL_UPDATE_REASONS ( \
64
- RRD_FLAG_UPDATE_REASON_TRIGGERED \
65
- |RRD_FLAG_UPDATE_REASON_LOAD_SQL \
66
- |RRD_FLAG_UPDATE_REASON_NEW_OBJECT \
67
- |RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT \
68
- |RRD_FLAG_UPDATE_REASON_CHANGED_LINKING \
69
- |RRD_FLAG_UPDATE_REASON_CHANGED_METADATA \
70
- |RRD_FLAG_UPDATE_REASON_ZERO_RETENTION \
71
- |RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T \
72
- |RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T \
73
- |RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED \
74
- |RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED \
75
- |RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD \
76
- |RRD_FLAG_UPDATE_REASON_DB_ROTATION \
77
- |RRD_FLAG_UPDATE_REASON_UNUSED \
78
- )
79
-
80
-#define RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS ( \
81
- RRD_FLAG_ARCHIVED \
82
- |RRD_FLAG_HIDDEN \
83
- |RRD_FLAG_ALL_UPDATE_REASONS \
84
- )
85
-
86
-#define RRD_FLAGS_REQUIRED_FOR_DELETIONS ( \
87
- RRD_FLAG_DELETED \
88
- |RRD_FLAG_LIVE_RETENTION \
89
-)
90
-
91
-#define RRD_FLAGS_PREVENTING_DELETIONS ( \
92
- RRD_FLAG_QUEUED_FOR_HUB \
93
- |RRD_FLAG_COLLECTED \
94
- |RRD_FLAG_QUEUED_FOR_PP \
95
-)
96
-
97
-// get all the flags of an object
98
-#define rrd_flags_get(obj) __atomic_load_n(&((obj)->flags), __ATOMIC_SEQ_CST)
99
-
100
-// check if ANY of the given flags (bits) is set
101
-#define rrd_flag_check(obj, flag) (rrd_flags_get(obj) & (flag))
102
-
103
-// check if ALL the given flags (bits) are set
104
-#define rrd_flag_check_all(obj, flag) (rrd_flag_check(obj, flag) == (flag))
105
-
106
-// set one or more flags (bits)
107
-#define rrd_flag_set(obj, flag) __atomic_or_fetch(&((obj)->flags), flag, __ATOMIC_SEQ_CST)
108
-
109
-// clear one or more flags (bits)
110
-#define rrd_flag_clear(obj, flag) __atomic_and_fetch(&((obj)->flags), ~(flag), __ATOMIC_SEQ_CST)
111
-
112
-// replace the flags of an object, with the supplied ones
113
-#define rrd_flags_replace(obj, all_flags) __atomic_store_n(&((obj)->flags), all_flags, __ATOMIC_SEQ_CST)
114
-
115
-static inline void
116
-rrd_flag_add_remove_atomic(RRD_FLAGS *flags, RRD_FLAGS check, RRD_FLAGS conditionally_add, RRD_FLAGS always_remove) {
117
- RRD_FLAGS expected, desired;
118
-
119
- do {
120
- expected = *flags;
121
-
122
- desired = expected;
123
- desired &= ~(always_remove);
124
-
125
- if(!(expected & check))
126
- desired |= (check | conditionally_add);
127
-
128
- } while(!__atomic_compare_exchange_n(flags, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
129
-}
130
-
131
-#define rrd_flag_set_collected(obj) \
132
- rrd_flag_add_remove_atomic(&((obj)->flags) \
133
- /* check this flag */ \
134
- , RRD_FLAG_COLLECTED \
135
- \
136
- /* add these flags together with the above, if the above is not already set */ \
137
- , RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED | RRD_FLAG_UPDATED \
138
- \
139
- /* always remove these flags */ \
140
- , RRD_FLAG_ARCHIVED \
141
- | RRD_FLAG_DELETED \
142
- | RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED \
143
- | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION \
144
- | RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD \
145
- )
146
-
147
-#define rrd_flag_set_archived(obj) \
148
- rrd_flag_add_remove_atomic(&((obj)->flags) \
149
- /* check this flag */ \
150
- , RRD_FLAG_ARCHIVED \
151
- \
152
- /* add these flags together with the above, if the above is not already set */ \
153
- , RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED | RRD_FLAG_UPDATED \
154
- \
155
- /* always remove these flags */ \
156
- , RRD_FLAG_COLLECTED \
157
- | RRD_FLAG_DELETED \
158
- | RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED \
159
- | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION \
160
- )
161
-
162
-#define rrd_flag_set_deleted(obj, reason) \
163
- rrd_flag_add_remove_atomic(&((obj)->flags) \
164
- /* check this flag */ \
165
- , RRD_FLAG_DELETED \
166
- \
167
- /* add these flags together with the above, if the above is not already set */ \
168
- , RRD_FLAG_UPDATE_REASON_ZERO_RETENTION | RRD_FLAG_UPDATED | (reason) \
169
- \
170
- /* always remove these flags */ \
171
- , RRD_FLAG_ARCHIVED \
172
- | RRD_FLAG_COLLECTED \
173
- )
174
-
175
-#define rrd_flag_is_collected(obj) rrd_flag_check(obj, RRD_FLAG_COLLECTED)
176
-#define rrd_flag_is_archived(obj) rrd_flag_check(obj, RRD_FLAG_ARCHIVED)
177
-#define rrd_flag_is_deleted(obj) rrd_flag_check(obj, RRD_FLAG_DELETED)
178
-#define rrd_flag_is_updated(obj) rrd_flag_check(obj, RRD_FLAG_UPDATED)
179
-
180
-// mark an object as updated, providing reasons (additional bits)
181
-#define rrd_flag_set_updated(obj, reason) rrd_flag_set(obj, RRD_FLAG_UPDATED | (reason))
182
-
183
-// clear an object as being updated, clearing also all the reasons
184
-#define rrd_flag_unset_updated(obj) rrd_flag_clear(obj, RRD_FLAG_UPDATED | RRD_FLAG_ALL_UPDATE_REASONS)
185
-
186
-
187
-static struct rrdcontext_reason {
188
- RRD_FLAGS flag;
189
- const char *name;
190
- usec_t delay_ut;
191
-} rrdcontext_reasons[] = {
192
- // context related
193
- {RRD_FLAG_UPDATE_REASON_TRIGGERED, "triggered transition", 65 * USEC_PER_SEC },
194
- {RRD_FLAG_UPDATE_REASON_NEW_OBJECT, "object created", 65 * USEC_PER_SEC },
195
- {RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT, "object updated", 65 * USEC_PER_SEC },
196
- {RRD_FLAG_UPDATE_REASON_LOAD_SQL, "loaded from sql", 65 * USEC_PER_SEC },
197
- {RRD_FLAG_UPDATE_REASON_CHANGED_METADATA, "changed metadata", 65 * USEC_PER_SEC },
198
- {RRD_FLAG_UPDATE_REASON_ZERO_RETENTION, "has no retention", 65 * USEC_PER_SEC },
199
- {RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T, "updated first_time_t", 65 * USEC_PER_SEC },
200
- {RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T, "updated last_time_t", 65 * USEC_PER_SEC },
201
- {RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED, "stopped collected", 65 * USEC_PER_SEC },
202
- {RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED, "started collected", 5 * USEC_PER_SEC },
203
- {RRD_FLAG_UPDATE_REASON_UNUSED, "unused", 5 * USEC_PER_SEC },
204
-
205
- // not context related
206
- {RRD_FLAG_UPDATE_REASON_CHANGED_LINKING, "changed rrd link", 65 * USEC_PER_SEC },
207
- {RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD, "child disconnected", 65 * USEC_PER_SEC },
208
- {RRD_FLAG_UPDATE_REASON_DB_ROTATION, "db rotation", 65 * USEC_PER_SEC },
209
- {RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION, "updated retention", 65 * USEC_PER_SEC },
210
-
211
- // terminator
212
- {0, NULL, 0 },
213
-};
214
-
215
-
216
-typedef struct rrdmetric {
217
- uuid_t uuid;
218
-
219
- STRING *id;
220
- STRING *name;
221
-
222
- RRDDIM *rrddim;
223
-
224
- time_t first_time_s;
225
- time_t last_time_s;
226
- RRD_FLAGS flags;
227
-
228
- struct rrdinstance *ri;
229
-} RRDMETRIC;
230
-
231
-typedef struct rrdinstance {
232
- uuid_t uuid;
233
-
234
- STRING *id;
235
- STRING *name;
236
- STRING *title;
237
- STRING *units;
238
- STRING *family;
239
- uint32_t priority;
240
- RRDSET_TYPE chart_type;
241
-
242
- RRD_FLAGS flags; // flags related to this instance
243
- time_t first_time_s;
244
- time_t last_time_s;
245
-
246
- time_t update_every_s; // data collection frequency
247
- RRDSET *rrdset; // pointer to RRDSET when collected, or NULL
248
-
249
- DICTIONARY *rrdlabels; // linked to RRDSET->chart_labels or own version
250
-
251
- struct rrdcontext *rc;
252
- DICTIONARY *rrdmetrics;
253
-
254
- struct {
255
- uint32_t collected_metrics_count; // a temporary variable to detect BEGIN/END without SET
256
- // don't use it for other purposes
257
- // it goes up and then resets to zero, on every iteration
258
- } internal;
259
-} RRDINSTANCE;
260
-
261
-typedef struct rrdcontext {
262
- uint64_t version;
263
-
264
- STRING *id;
265
- STRING *title;
266
- STRING *units;
267
- STRING *family;
268
- uint32_t priority;
269
- RRDSET_TYPE chart_type;
270
-
271
- RRD_FLAGS flags;
272
- time_t first_time_s;
273
- time_t last_time_s;
274
-
275
- VERSIONED_CONTEXT_DATA hub;
276
-
277
- DICTIONARY *rrdinstances;
278
- RRDHOST *rrdhost;
279
-
280
- struct {
281
- RRD_FLAGS queued_flags; // the last flags that triggered the post-processing
282
- usec_t queued_ut; // the last time this was queued
283
- usec_t dequeued_ut; // the last time we sent (or deduplicated) this context
284
- size_t executions; // how many times this context has been processed
285
- } pp;
286
-
287
- struct {
288
- RRD_FLAGS queued_flags; // the last flags that triggered the queueing
289
- usec_t queued_ut; // the last time this was queued
290
- usec_t delay_calc_ut; // the last time we calculated the scheduled_dispatched_ut
291
- usec_t scheduled_dispatch_ut; // the time it was/is scheduled to be sent
292
- usec_t dequeued_ut; // the last time we sent (or deduplicated) this context
293
- size_t dispatches; // the number of times this has been dispatched to hub
294
- } queue;
295
-
296
- netdata_mutex_t mutex;
297
-} RRDCONTEXT;
298
-
299
-// ----------------------------------------------------------------------------
300
-// helper one-liners for RRDMETRIC
301
-
302
-static bool rrdmetric_update_retention(RRDMETRIC *rm);
303
-
304
-static inline RRDMETRIC *rrdmetric_acquired_value(RRDMETRIC_ACQUIRED *rma) {
305
- return dictionary_acquired_item_value((DICTIONARY_ITEM *)rma);
306
-}
307
-
308
-static inline RRDMETRIC_ACQUIRED *rrdmetric_acquired_dup(RRDMETRIC_ACQUIRED *rma) {
309
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
310
- return (RRDMETRIC_ACQUIRED *)dictionary_acquired_item_dup(rm->ri->rrdmetrics, (DICTIONARY_ITEM *)rma);
311
-}
312
-
313
-static inline void rrdmetric_release(RRDMETRIC_ACQUIRED *rma) {
314
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
315
- dictionary_acquired_item_release(rm->ri->rrdmetrics, (DICTIONARY_ITEM *)rma);
316
-}
317
-
318
-const char *rrdmetric_acquired_id(RRDMETRIC_ACQUIRED *rma) {
319
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
320
- return string2str(rm->id);
321
-}
322
-
323
-const char *rrdmetric_acquired_name(RRDMETRIC_ACQUIRED *rma) {
324
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
325
- return string2str(rm->name);
326
-}
327
-
328
-STRING *rrdmetric_acquired_id_dup(RRDMETRIC_ACQUIRED *rma) {
329
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
330
- return string_dup(rm->id);
331
-}
332
-
333
-STRING *rrdmetric_acquired_name_dup(RRDMETRIC_ACQUIRED *rma) {
334
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
335
- return string_dup(rm->name);
336
-}
337
-
338
-NETDATA_DOUBLE rrdmetric_acquired_last_stored_value(RRDMETRIC_ACQUIRED *rma) {
339
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
340
-
341
- if(rm->rrddim)
342
- return rm->rrddim->last_stored_value;
343
-
344
- return NAN;
345
-}
346
-
347
-// ----------------------------------------------------------------------------
348
-// helper one-liners for RRDINSTANCE
349
-
350
-static inline RRDINSTANCE *rrdinstance_acquired_value(RRDINSTANCE_ACQUIRED *ria) {
351
- return dictionary_acquired_item_value((DICTIONARY_ITEM *)ria);
352
-}
353
-
354
-static inline RRDINSTANCE_ACQUIRED *rrdinstance_acquired_dup(RRDINSTANCE_ACQUIRED *ria) {
355
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
356
- return (RRDINSTANCE_ACQUIRED *)dictionary_acquired_item_dup(ri->rc->rrdinstances, (DICTIONARY_ITEM *)ria);
357
-}
358
-
359
-static inline void rrdinstance_release(RRDINSTANCE_ACQUIRED *ria) {
360
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
361
- dictionary_acquired_item_release(ri->rc->rrdinstances, (DICTIONARY_ITEM *)ria);
362
-}
363
-
364
-const char *rrdinstance_acquired_id(RRDINSTANCE_ACQUIRED *ria) {
365
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
366
- return string2str(ri->id);
367
-}
368
-
369
-const char *rrdinstance_acquired_name(RRDINSTANCE_ACQUIRED *ria) {
370
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
371
- return string2str(ri->name);
372
-}
373
-
374
-const char *rrdinstance_acquired_units(RRDINSTANCE_ACQUIRED *ria) {
375
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
376
- return string2str(ri->units);
377
-}
378
-
379
-STRING *rrdinstance_acquired_units_dup(RRDINSTANCE_ACQUIRED *ria) {
380
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
381
- return string_dup(ri->units);
382
-}
383
-
384
-DICTIONARY *rrdinstance_acquired_labels(RRDINSTANCE_ACQUIRED *ria) {
385
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
386
- return ri->rrdlabels;
387
-}
388
-
389
-DICTIONARY *rrdinstance_acquired_functions(RRDINSTANCE_ACQUIRED *ria) {
390
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
391
- if(!ri->rrdset) return NULL;
392
- return ri->rrdset->functions_view;
393
-}
394
-
395
-RRDHOST *rrdinstance_acquired_rrdhost(RRDINSTANCE_ACQUIRED *ria) {
396
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
397
- return ri->rc->rrdhost;
398
-}
399
-
400
-// ----------------------------------------------------------------------------
401
-// helper one-liners for RRDCONTEXT
402
-
403
-static inline RRDCONTEXT *rrdcontext_acquired_value(RRDCONTEXT_ACQUIRED *rca) {
404
- return dictionary_acquired_item_value((DICTIONARY_ITEM *)rca);
405
-}
406
-
407
-const char *rrdcontext_acquired_id(RRDCONTEXT_ACQUIRED *rca) {
408
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
409
- return string2str(rc->id);
410
-}
411
-
412
-bool rrdcontext_acquired_belongs_to_host(RRDCONTEXT_ACQUIRED *rca, RRDHOST *host) {
413
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
414
- return rc->rrdhost == host;
415
-}
416
-
417
-bool rrdinstance_acquired_belongs_to_context(RRDINSTANCE_ACQUIRED *ria, RRDCONTEXT_ACQUIRED *rca) {
418
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
419
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
420
- return ri->rc == rc;
421
-}
422
-
423
-bool rrdmetric_acquired_belongs_to_instance(RRDMETRIC_ACQUIRED *rma, RRDINSTANCE_ACQUIRED *ria) {
424
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
425
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
426
- return rm->ri == ri;
427
-}
428
-
429
-time_t rrdinstance_acquired_update_every(RRDINSTANCE_ACQUIRED *ria) {
430
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
431
- return ri->update_every_s;
432
-}
433
-time_t rrdmetric_acquired_first_entry(RRDMETRIC_ACQUIRED *rma) {
434
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
435
- return rm->first_time_s;
436
-}
437
-time_t rrdmetric_acquired_last_entry(RRDMETRIC_ACQUIRED *rma) {
438
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
439
-
440
- if(rrd_flag_check(rm, RRD_FLAG_COLLECTED))
441
- return 0;
442
-
443
- return rm->last_time_s;
444
-}
445
-
446
-static inline RRDCONTEXT_ACQUIRED *rrdcontext_acquired_dup(RRDCONTEXT_ACQUIRED *rca) {
447
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
448
- return (RRDCONTEXT_ACQUIRED *)dictionary_acquired_item_dup((DICTIONARY *)rc->rrdhost->rrdctx, (DICTIONARY_ITEM *)rca);
449
-}
450
-
451
-static inline void rrdcontext_release(RRDCONTEXT_ACQUIRED *rca) {
452
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
453
- dictionary_acquired_item_release((DICTIONARY *)rc->rrdhost->rrdctx, (DICTIONARY_ITEM *)rca);
454
-}
455
-
456
-static void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, bool worker_jobs);
457
-static void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, bool worker_jobs);
458
-
459
-#define rrdcontext_version_hash(host) rrdcontext_version_hash_with_callback(host, NULL, false, NULL)
460
-static uint64_t rrdcontext_version_hash_with_callback(RRDHOST *host, void (*callback)(RRDCONTEXT *, bool, void *), bool snapshot, void *bundle);
461
-
462
-static void rrdcontext_garbage_collect_single_host(RRDHOST *host, bool worker_jobs);
463
-static void rrdcontext_garbage_collect_for_all_hosts(void);
464
-
465
-#define rrdcontext_lock(rc) netdata_mutex_lock(&((rc)->mutex))
466
-#define rrdcontext_unlock(rc) netdata_mutex_unlock(&((rc)->mutex))
467
-
468
-// ----------------------------------------------------------------------------
469
-// Forward definitions
470
-
471
-static uint64_t rrdcontext_get_next_version(RRDCONTEXT *rc);
472
-static bool check_if_cloud_version_changed_unsafe(RRDCONTEXT *rc, bool sending __maybe_unused);
473
-static void rrdcontext_message_send_unsafe(RRDCONTEXT *rc, bool snapshot __maybe_unused, void *bundle __maybe_unused);
474
-
475
-static void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc);
476
-
477
-static void rrdcontext_dequeue_from_post_processing(RRDCONTEXT *rc);
478
-static void rrdcontext_queue_for_post_processing(RRDCONTEXT *rc, const char *function, RRD_FLAGS flags);
479
-static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAGS reason, bool worker_jobs);
480
-
481
-static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function);
482
-static void rrdinstance_trigger_updates(RRDINSTANCE *ri, const char *function);
483
-static void rrdcontext_trigger_updates(RRDCONTEXT *rc, const char *function);
484
-
485
-// ----------------------------------------------------------------------------
486
-// visualizing flags
487
-
488
-static void rrd_flags_to_buffer_json_array_items(RRD_FLAGS flags, BUFFER *wb) {
489
- if(flags & RRD_FLAG_QUEUED_FOR_HUB)
490
- buffer_json_add_array_item_string(wb, "QUEUED");
491
-
492
- if(flags & RRD_FLAG_DELETED)
493
- buffer_json_add_array_item_string(wb, "DELETED");
494
-
495
- if(flags & RRD_FLAG_COLLECTED)
496
- buffer_json_add_array_item_string(wb, "COLLECTED");
497
-
498
- if(flags & RRD_FLAG_UPDATED)
499
- buffer_json_add_array_item_string(wb, "UPDATED");
500
-
501
- if(flags & RRD_FLAG_ARCHIVED)
502
- buffer_json_add_array_item_string(wb, "ARCHIVED");
503
-
504
- if(flags & RRD_FLAG_OWN_LABELS)
505
- buffer_json_add_array_item_string(wb, "OWN_LABELS");
506
-
507
- if(flags & RRD_FLAG_LIVE_RETENTION)
508
- buffer_json_add_array_item_string(wb, "LIVE_RETENTION");
509
-
510
- if(flags & RRD_FLAG_HIDDEN)
511
- buffer_json_add_array_item_string(wb, "HIDDEN");
512
-
513
- if(flags & RRD_FLAG_QUEUED_FOR_PP)
514
- buffer_json_add_array_item_string(wb, "PENDING_UPDATES");
515
-}
516
-
517
-static void rrd_reasons_to_buffer_json_array_items(RRD_FLAGS flags, BUFFER *wb) {
518
- for(int i = 0, added = 0; rrdcontext_reasons[i].name ; i++) {
519
- if (flags & rrdcontext_reasons[i].flag) {
520
- buffer_json_add_array_item_string(wb, rrdcontext_reasons[i].name);
521
- added++;
522
- }
523
- }
524
-}
525
-
526
-// ----------------------------------------------------------------------------
527
-// RRDMETRIC
528
-
529
-// free the contents of RRDMETRIC.
530
-// RRDMETRIC itself is managed by DICTIONARY - no need to free it here.
531
-static void rrdmetric_free(RRDMETRIC *rm) {
532
- string_freez(rm->id);
533
- string_freez(rm->name);
534
-
535
- rm->id = NULL;
536
- rm->name = NULL;
537
- rm->ri = NULL;
538
-}
539
-
540
-// called when this rrdmetric is inserted to the rrdmetrics dictionary of a rrdinstance
541
-// the constructor of the rrdmetric object
542
-static void rrdmetric_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance) {
543
- RRDMETRIC *rm = value;
544
-
545
- // link it to its parent
546
- rm->ri = rrdinstance;
547
-
548
- // remove flags that we need to figure out at runtime
549
- rm->flags = rm->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics
550
-
551
- // signal the react callback to do the job
552
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
553
-}
554
-
555
-// called when this rrdmetric is deleted from the rrdmetrics dictionary of a rrdinstance
556
-// the destructor of the rrdmetric object
557
-static void rrdmetric_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) {
558
- RRDMETRIC *rm = value;
559
-
560
- internal_error(rm->rrddim, "RRDMETRIC: '%s' is freed but there is a RRDDIM linked to it.", string2str(rm->id));
561
-
562
- // free the resources
563
- rrdmetric_free(rm);
564
-}
565
-
566
-// called when the same rrdmetric is inserted again to the rrdmetrics dictionary of a rrdinstance
567
-// while this is called, the dictionary is write locked, but there may be other users of the object
568
-static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdinstance __maybe_unused) {
569
- RRDMETRIC *rm = old_value;
570
- RRDMETRIC *rm_new = new_value;
571
-
572
- internal_error(rm->id != rm_new->id,
573
- "RRDMETRIC: '%s' cannot change id to '%s'",
574
- string2str(rm->id), string2str(rm_new->id));
575
-
576
- if(uuid_compare(rm->uuid, rm_new->uuid) != 0) {
577
-#ifdef NETDATA_INTERNAL_CHECKS
578
- char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
579
- uuid_unparse(rm->uuid, uuid1);
580
- uuid_unparse(rm_new->uuid, uuid2);
581
-
582
- time_t old_first_time_s = 0;
583
- time_t old_last_time_s = 0;
584
- if(rrdmetric_update_retention(rm)) {
585
- old_first_time_s = rm->first_time_s;
586
- old_last_time_s = rm->last_time_s;
587
- }
588
-
589
- uuid_copy(rm->uuid, rm_new->uuid);
590
-
591
- time_t new_first_time_s = 0;
592
- time_t new_last_time_s = 0;
593
- if(rrdmetric_update_retention(rm)) {
594
- new_first_time_s = rm->first_time_s;
595
- new_last_time_s = rm->last_time_s;
596
- }
597
-
598
- internal_error(true,
599
- "RRDMETRIC: '%s' of instance '%s' of host '%s' changed UUID from '%s' (retention %ld to %ld, %ld secs) to '%s' (retention %ld to %ld, %ld secs)"
600
- , string2str(rm->id)
601
- , string2str(rm->ri->id)
602
- , rrdhost_hostname(rm->ri->rc->rrdhost)
603
- , uuid1, old_first_time_s, old_last_time_s, old_last_time_s - old_first_time_s
604
- , uuid2, new_first_time_s, new_last_time_s, new_last_time_s - new_first_time_s
605
- );
606
-#else
607
- uuid_copy(rm->uuid, rm_new->uuid);
608
-#endif
609
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
610
- }
611
-
612
- if(rm->rrddim && rm_new->rrddim && rm->rrddim != rm_new->rrddim) {
613
- rm->rrddim = rm_new->rrddim;
614
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
615
- }
616
-
617
-#ifdef NETDATA_INTERNAL_CHECKS
618
- if(rm->rrddim && uuid_compare(rm->uuid, rm->rrddim->metric_uuid) != 0) {
619
- char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
620
- uuid_unparse(rm->uuid, uuid1);
621
- uuid_unparse(rm_new->uuid, uuid2);
622
- internal_error(true, "RRDMETRIC: '%s' is linked to RRDDIM '%s' but they have different UUIDs. RRDMETRIC has '%s', RRDDIM has '%s'", string2str(rm->id), rrddim_id(rm->rrddim), uuid1, uuid2);
623
- }
624
-#endif
625
-
626
- if(rm->rrddim != rm_new->rrddim)
627
- rm->rrddim = rm_new->rrddim;
628
-
629
- if(rm->name != rm_new->name) {
630
- STRING *old = rm->name;
631
- rm->name = string_dup(rm_new->name);
632
- string_freez(old);
633
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
634
- }
635
-
636
- if(!rm->first_time_s || (rm_new->first_time_s && rm_new->first_time_s < rm->first_time_s)) {
637
- rm->first_time_s = rm_new->first_time_s;
638
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
639
- }
640
-
641
- if(!rm->last_time_s || (rm_new->last_time_s && rm_new->last_time_s > rm->last_time_s)) {
642
- rm->last_time_s = rm_new->last_time_s;
643
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
644
- }
645
-
646
- rrd_flag_set(rm, rm_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no needs for atomics on rm_new
647
-
648
- if(rrd_flag_is_collected(rm) && rrd_flag_is_archived(rm))
649
- rrd_flag_set_collected(rm);
650
-
651
- if(rrd_flag_check(rm, RRD_FLAG_UPDATED))
652
- rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
653
-
654
- rrdmetric_free(rm_new);
655
-
656
- // the react callback will continue from here
657
- return rrd_flag_is_updated(rm);
658
-}
659
-
660
-// this is called after the insert or the conflict callbacks,
661
-// but the dictionary is now unlocked
662
-static void rrdmetric_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) {
663
- RRDMETRIC *rm = value;
664
- rrdmetric_trigger_updates(rm, __FUNCTION__ );
665
-}
666
-
667
-static void rrdmetrics_create_in_rrdinstance(RRDINSTANCE *ri) {
668
- if(unlikely(!ri)) return;
669
- if(likely(ri->rrdmetrics)) return;
670
-
671
- ri->rrdmetrics = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
672
- &dictionary_stats_category_rrdcontext, sizeof(RRDMETRIC));
673
-
674
- dictionary_register_insert_callback(ri->rrdmetrics, rrdmetric_insert_callback, ri);
675
- dictionary_register_delete_callback(ri->rrdmetrics, rrdmetric_delete_callback, ri);
676
- dictionary_register_conflict_callback(ri->rrdmetrics, rrdmetric_conflict_callback, ri);
677
- dictionary_register_react_callback(ri->rrdmetrics, rrdmetric_react_callback, ri);
678
-}
679
-
680
-static void rrdmetrics_destroy_from_rrdinstance(RRDINSTANCE *ri) {
681
- if(unlikely(!ri || !ri->rrdmetrics)) return;
682
- dictionary_destroy(ri->rrdmetrics);
683
- ri->rrdmetrics = NULL;
684
-}
685
-
686
-// trigger post-processing of the rrdmetric, escalating changes to the rrdinstance it belongs
687
-static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function) {
688
- if(unlikely(rrd_flag_is_collected(rm)) && (!rm->rrddim || rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD)))
689
- rrd_flag_set_archived(rm);
690
-
691
- if(rrd_flag_is_updated(rm) || !rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION)) {
692
- rrd_flag_set_updated(rm->ri, RRD_FLAG_UPDATE_REASON_TRIGGERED);
693
- rrdcontext_queue_for_post_processing(rm->ri->rc, function, rm->flags);
694
- }
695
-}
696
-
697
-// ----------------------------------------------------------------------------
698
-// RRDMETRIC HOOKS ON RRDDIM
699
-
700
-static inline void rrdmetric_from_rrddim(RRDDIM *rd) {
701
- if(unlikely(!rd->rrdset))
702
- fatal("RRDMETRIC: rrddim '%s' does not have a rrdset.", rrddim_id(rd));
703
-
704
- if(unlikely(!rd->rrdset->rrdhost))
705
- fatal("RRDMETRIC: rrdset '%s' does not have a rrdhost", rrdset_id(rd->rrdset));
706
-
707
- if(unlikely(!rd->rrdset->rrdinstance))
708
- fatal("RRDMETRIC: rrdset '%s' does not have a rrdinstance", rrdset_id(rd->rrdset));
709
-
710
- RRDINSTANCE *ri = rrdinstance_acquired_value(rd->rrdset->rrdinstance);
711
-
712
- RRDMETRIC trm = {
713
- .id = string_dup(rd->id),
714
- .name = string_dup(rd->name),
715
- .flags = RRD_FLAG_NONE, // no need for atomics
716
- .rrddim = rd,
717
- };
718
- uuid_copy(trm.uuid, rd->metric_uuid);
719
-
720
- RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)dictionary_set_and_acquire_item(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm));
721
-
722
- if(rd->rrdmetric)
723
- rrdmetric_release(rd->rrdmetric);
724
-
725
- rd->rrdmetric = rma;
726
-}
727
-
728
-#define rrddim_get_rrdmetric(rd) rrddim_get_rrdmetric_with_trace(rd, __FUNCTION__)
729
-static inline RRDMETRIC *rrddim_get_rrdmetric_with_trace(RRDDIM *rd, const char *function) {
730
- if(unlikely(!rd->rrdmetric)) {
731
- error("RRDMETRIC: RRDDIM '%s' is not linked to an RRDMETRIC at %s()", rrddim_id(rd), function);
732
- return NULL;
733
- }
734
-
735
- RRDMETRIC *rm = rrdmetric_acquired_value(rd->rrdmetric);
736
- if(unlikely(!rm)) {
737
- error("RRDMETRIC: RRDDIM '%s' lost the link to its RRDMETRIC at %s()", rrddim_id(rd), function);
738
- return NULL;
739
- }
740
-
741
- if(unlikely(rm->rrddim != rd))
742
- fatal("RRDMETRIC: '%s' is not linked to RRDDIM '%s' at %s()", string2str(rm->id), rrddim_id(rd), function);
743
-
744
- return rm;
745
-}
746
-
747
-static inline void rrdmetric_rrddim_is_freed(RRDDIM *rd) {
748
- RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
749
- if(unlikely(!rm)) return;
750
-
751
- if(unlikely(rrd_flag_is_collected(rm)))
752
- rrd_flag_set_archived(rm);
753
-
754
- rm->rrddim = NULL;
755
- rrdmetric_trigger_updates(rm, __FUNCTION__ );
756
- rrdmetric_release(rd->rrdmetric);
757
- rd->rrdmetric = NULL;
758
-}
759
-
760
-static inline void rrdmetric_updated_rrddim_flags(RRDDIM *rd) {
761
- RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
762
- if(unlikely(!rm)) return;
763
-
764
- if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED|RRDDIM_FLAG_OBSOLETE))) {
765
- if(unlikely(rrd_flag_is_collected(rm)))
766
- rrd_flag_set_archived(rm);
767
- }
768
-
769
- rrdmetric_trigger_updates(rm, __FUNCTION__ );
770
-}
771
-
772
-static inline void rrdmetric_collected_rrddim(RRDDIM *rd) {
773
- RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
774
- if(unlikely(!rm)) return;
775
-
776
- if(unlikely(!rrd_flag_is_collected(rm)))
777
- rrd_flag_set_collected(rm);
778
-
779
- // we use this variable to detect BEGIN/END without SET
780
- rm->ri->internal.collected_metrics_count++;
781
-
782
- rrdmetric_trigger_updates(rm, __FUNCTION__ );
783
-}
784
-
785
-// ----------------------------------------------------------------------------
786
-// RRDINSTANCE
787
-
788
-static void rrdinstance_free(RRDINSTANCE *ri) {
789
-
790
- if(rrd_flag_check(ri, RRD_FLAG_OWN_LABELS))
791
- dictionary_destroy(ri->rrdlabels);
792
-
793
- rrdmetrics_destroy_from_rrdinstance(ri);
794
- string_freez(ri->id);
795
- string_freez(ri->name);
796
- string_freez(ri->title);
797
- string_freez(ri->units);
798
- string_freez(ri->family);
799
-
800
- ri->id = NULL;
801
- ri->name = NULL;
802
- ri->title = NULL;
803
- ri->units = NULL;
804
- ri->family = NULL;
805
- ri->rc = NULL;
806
- ri->rrdlabels = NULL;
807
- ri->rrdmetrics = NULL;
808
- ri->rrdset = NULL;
809
-}
810
-
811
-static void rrdinstance_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdcontext) {
812
- RRDINSTANCE *ri = value;
813
-
814
- // link it to its parent
815
- ri->rc = rrdcontext;
816
-
817
- ri->flags = ri->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics
818
-
819
- if(!ri->name)
820
- ri->name = string_dup(ri->id);
821
-
822
- if(ri->rrdset) {
823
- ri->rrdlabels = ri->rrdset->rrdlabels;
824
- ri->flags &= ~RRD_FLAG_OWN_LABELS; // no need of atomics at the constructor
825
- }
826
- else {
827
- ri->rrdlabels = rrdlabels_create();
828
- ri->flags |= RRD_FLAG_OWN_LABELS; // no need of atomics at the constructor
829
- }
830
-
831
- if(ri->rrdset) {
832
- if(unlikely(rrdset_flag_check(ri->rrdset, RRDSET_FLAG_HIDDEN)))
833
- ri->flags |= RRD_FLAG_HIDDEN; // no need of atomics at the constructor
834
- else
835
- ri->flags &= ~RRD_FLAG_HIDDEN; // no need of atomics at the constructor
836
- }
837
-
838
- rrdmetrics_create_in_rrdinstance(ri);
839
-
840
- // signal the react callback to do the job
841
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
842
-}
843
-
844
-static void rrdinstance_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdcontext __maybe_unused) {
845
- RRDINSTANCE *ri = (RRDINSTANCE *)value;
846
-
847
- internal_error(ri->rrdset, "RRDINSTANCE: '%s' is freed but there is a RRDSET linked to it.", string2str(ri->id));
848
-
849
- rrdinstance_free(ri);
850
-}
851
-
852
-static bool rrdinstance_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdcontext __maybe_unused) {
853
- RRDINSTANCE *ri = (RRDINSTANCE *)old_value;
854
- RRDINSTANCE *ri_new = (RRDINSTANCE *)new_value;
855
-
856
- internal_error(ri->id != ri_new->id,
857
- "RRDINSTANCE: '%s' cannot change id to '%s'",
858
- string2str(ri->id), string2str(ri_new->id));
859
-
860
- if(uuid_compare(ri->uuid, ri_new->uuid) != 0) {
861
-#ifdef NETDATA_INTERNAL_CHECKS
862
- char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
863
- uuid_unparse(ri->uuid, uuid1);
864
- uuid_unparse(ri_new->uuid, uuid2);
865
- internal_error(true, "RRDINSTANCE: '%s' of host '%s' changed UUID from '%s' to '%s'",
866
- string2str(ri->id), rrdhost_hostname(ri->rc->rrdhost), uuid1, uuid2);
867
-#endif
868
-
869
- uuid_copy(ri->uuid, ri_new->uuid);
870
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
871
- }
872
-
873
- if(ri->rrdset && ri_new->rrdset && ri->rrdset != ri_new->rrdset) {
874
- ri->rrdset = ri_new->rrdset;
875
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
876
- }
877
-
878
-#ifdef NETDATA_INTERNAL_CHECKS
879
- if(ri->rrdset && uuid_compare(ri->uuid, ri->rrdset->chart_uuid) != 0) {
880
- char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
881
- uuid_unparse(ri->uuid, uuid1);
882
- uuid_unparse(ri->rrdset->chart_uuid, uuid2);
883
- internal_error(true, "RRDINSTANCE: '%s' is linked to RRDSET '%s' but they have different UUIDs. RRDINSTANCE has '%s', RRDSET has '%s'", string2str(ri->id), rrdset_id(ri->rrdset), uuid1, uuid2);
884
- }
885
-#endif
886
-
887
- if(ri->name != ri_new->name) {
888
- STRING *old = ri->name;
889
- ri->name = string_dup(ri_new->name);
890
- string_freez(old);
891
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
892
- }
893
-
894
- if(ri->title != ri_new->title) {
895
- STRING *old = ri->title;
896
- ri->title = string_dup(ri_new->title);
897
- string_freez(old);
898
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
899
- }
900
-
901
- if(ri->units != ri_new->units) {
902
- STRING *old = ri->units;
903
- ri->units = string_dup(ri_new->units);
904
- string_freez(old);
905
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
906
- }
907
-
908
- if(ri->family != ri_new->family) {
909
- STRING *old = ri->family;
910
- ri->family = string_dup(ri_new->family);
911
- string_freez(old);
912
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
913
- }
914
-
915
- if(ri->chart_type != ri_new->chart_type) {
916
- ri->chart_type = ri_new->chart_type;
917
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
918
- }
919
-
920
- if(ri->priority != ri_new->priority) {
921
- ri->priority = ri_new->priority;
922
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
923
- }
924
-
925
- if(ri->update_every_s != ri_new->update_every_s) {
926
- ri->update_every_s = ri_new->update_every_s;
927
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
928
- }
929
-
930
- if(ri->rrdset != ri_new->rrdset) {
931
- ri->rrdset = ri_new->rrdset;
932
-
933
- if(ri->rrdset && rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) {
934
- DICTIONARY *old = ri->rrdlabels;
935
- ri->rrdlabels = ri->rrdset->rrdlabels;
936
- rrd_flag_clear(ri, RRD_FLAG_OWN_LABELS);
937
- rrdlabels_destroy(old);
938
- }
939
- else if(!ri->rrdset && !rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) {
940
- ri->rrdlabels = rrdlabels_create();
941
- rrd_flag_set(ri, RRD_FLAG_OWN_LABELS);
942
- }
943
- }
944
-
945
- if(ri->rrdset) {
946
- if(unlikely(rrdset_flag_check(ri->rrdset, RRDSET_FLAG_HIDDEN)))
947
- rrd_flag_set(ri, RRD_FLAG_HIDDEN);
948
- else
949
- rrd_flag_clear(ri, RRD_FLAG_HIDDEN);
950
- }
951
-
952
- rrd_flag_set(ri, ri_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on ri_new
953
-
954
- if(rrd_flag_is_collected(ri) && rrd_flag_is_archived(ri))
955
- rrd_flag_set_collected(ri);
956
-
957
- if(rrd_flag_is_updated(ri))
958
- rrd_flag_set(ri, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
959
-
960
- // free the new one
961
- rrdinstance_free(ri_new);
962
-
963
- // the react callback will continue from here
964
- return rrd_flag_is_updated(ri);
965
-}
966
-
967
-static void rrdinstance_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdcontext __maybe_unused) {
968
- RRDINSTANCE *ri = value;
969
-
970
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
971
-}
972
-
973
-void rrdinstances_create_in_rrdcontext(RRDCONTEXT *rc) {
974
- if(unlikely(!rc || rc->rrdinstances)) return;
975
-
976
- rc->rrdinstances = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
977
- &dictionary_stats_category_rrdcontext, sizeof(RRDINSTANCE));
978
-
979
- dictionary_register_insert_callback(rc->rrdinstances, rrdinstance_insert_callback, rc);
980
- dictionary_register_delete_callback(rc->rrdinstances, rrdinstance_delete_callback, rc);
981
- dictionary_register_conflict_callback(rc->rrdinstances, rrdinstance_conflict_callback, rc);
982
- dictionary_register_react_callback(rc->rrdinstances, rrdinstance_react_callback, rc);
983
-}
984
-
985
-void rrdinstances_destroy_from_rrdcontext(RRDCONTEXT *rc) {
986
- if(unlikely(!rc || !rc->rrdinstances)) return;
987
-
988
- dictionary_destroy(rc->rrdinstances);
989
- rc->rrdinstances = NULL;
990
-}
991
-
992
-static void rrdinstance_trigger_updates(RRDINSTANCE *ri, const char *function) {
993
- RRDSET *st = ri->rrdset;
994
-
995
- if(likely(st)) {
996
- if(unlikely((unsigned int) st->priority != ri->priority)) {
997
- ri->priority = st->priority;
998
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
999
- }
1000
- if(unlikely(st->update_every != ri->update_every_s)) {
1001
- ri->update_every_s = st->update_every;
1002
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1003
- }
1004
- }
1005
- else if(unlikely(rrd_flag_is_collected(ri))) {
1006
- // there is no rrdset, but we have it as collected!
1007
-
1008
- rrd_flag_set_archived(ri);
1009
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
1010
- }
1011
-
1012
- if(rrd_flag_is_updated(ri) || !rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION)) {
1013
- rrd_flag_set_updated(ri->rc, RRD_FLAG_UPDATE_REASON_TRIGGERED);
1014
- rrdcontext_queue_for_post_processing(ri->rc, function, ri->flags);
1015
- }
1016
-}
1017
-
1018
-// ----------------------------------------------------------------------------
1019
-// RRDINSTANCE HOOKS ON RRDSET
1020
-
1021
-static inline void rrdinstance_from_rrdset(RRDSET *st) {
1022
- RRDCONTEXT trc = {
1023
- .id = string_dup(st->context),
1024
- .title = string_dup(st->title),
1025
- .units = string_dup(st->units),
1026
- .family = string_dup(st->family),
1027
- .priority = st->priority,
1028
- .chart_type = st->chart_type,
1029
- .flags = RRD_FLAG_NONE, // no need for atomics
1030
- .rrdhost = st->rrdhost,
1031
- };
1032
-
1033
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_set_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(trc.id), &trc, sizeof(trc));
1034
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
1035
-
1036
- RRDINSTANCE tri = {
1037
- .id = string_dup(st->id),
1038
- .name = string_dup(st->name),
1039
- .units = string_dup(st->units),
1040
- .family = string_dup(st->family),
1041
- .title = string_dup(st->title),
1042
- .chart_type = st->chart_type,
1043
- .priority = st->priority,
1044
- .update_every_s = st->update_every,
1045
- .flags = RRD_FLAG_NONE, // no need for atomics
1046
- .rrdset = st,
1047
- };
1048
- uuid_copy(tri.uuid, st->chart_uuid);
1049
-
1050
- RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, string2str(tri.id), &tri, sizeof(tri));
1051
-
1052
- RRDCONTEXT_ACQUIRED *rca_old = st->rrdcontext;
1053
- RRDINSTANCE_ACQUIRED *ria_old = st->rrdinstance;
1054
-
1055
- st->rrdcontext = rca;
1056
- st->rrdinstance = ria;
1057
-
1058
- if(rca == rca_old) {
1059
- rrdcontext_release(rca_old);
1060
- rca_old = NULL;
1061
- }
1062
-
1063
- if(ria == ria_old) {
1064
- rrdinstance_release(ria_old);
1065
- ria_old = NULL;
1066
- }
1067
-
1068
- if(rca_old && ria_old) {
1069
- // Oops! The chart changed context!
1070
-
1071
- // RRDCONTEXT *rc_old = rrdcontext_acquired_value(rca_old);
1072
- RRDINSTANCE *ri_old = rrdinstance_acquired_value(ria_old);
1073
-
1074
- // migrate all dimensions to the new metrics
1075
- RRDDIM *rd;
1076
- rrddim_foreach_read(rd, st) {
1077
- if (!rd->rrdmetric) continue;
1078
-
1079
- RRDMETRIC *rm_old = rrdmetric_acquired_value(rd->rrdmetric);
1080
- rrd_flags_replace(rm_old, RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
1081
- rm_old->rrddim = NULL;
1082
- rm_old->first_time_s = 0;
1083
- rm_old->last_time_s = 0;
1084
-
1085
- rrdmetric_release(rd->rrdmetric);
1086
- rd->rrdmetric = NULL;
1087
-
1088
- rrdmetric_from_rrddim(rd);
1089
- }
1090
- rrddim_foreach_done(rd);
1091
-
1092
- // mark the old instance, ready to be deleted
1093
- if(!rrd_flag_check(ri_old, RRD_FLAG_OWN_LABELS))
1094
- ri_old->rrdlabels = rrdlabels_create();
1095
-
1096
- rrd_flags_replace(ri_old, RRD_FLAG_OWN_LABELS|RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
1097
- ri_old->rrdset = NULL;
1098
- ri_old->first_time_s = 0;
1099
- ri_old->last_time_s = 0;
1100
-
1101
- rrdinstance_trigger_updates(ri_old, __FUNCTION__ );
1102
- rrdinstance_release(ria_old);
1103
-
1104
- /*
1105
- // trigger updates on the old context
1106
- if(!dictionary_entries(rc_old->rrdinstances) && !dictionary_stats_referenced_items(rc_old->rrdinstances)) {
1107
- rrdcontext_lock(rc_old);
1108
- rc_old->flags = ((rc_old->flags & RRD_FLAG_QUEUED)?RRD_FLAG_QUEUED:RRD_FLAG_NONE)|RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION;
1109
- rc_old->first_time_s = 0;
1110
- rc_old->last_time_s = 0;
1111
- rrdcontext_unlock(rc_old);
1112
- rrdcontext_trigger_updates(rc_old, __FUNCTION__ );
1113
- }
1114
- else
1115
- rrdcontext_trigger_updates(rc_old, __FUNCTION__ );
1116
- */
1117
-
1118
- rrdcontext_release(rca_old);
1119
- rca_old = NULL;
1120
- ria_old = NULL;
1121
- }
1122
-
1123
- if(rca_old || ria_old)
1124
- fatal("RRDCONTEXT: cannot switch rrdcontext without switching rrdinstance too");
1125
-}
1126
-
1127
-#define rrdset_get_rrdinstance(st) rrdset_get_rrdinstance_with_trace(st, __FUNCTION__);
1128
-static inline RRDINSTANCE *rrdset_get_rrdinstance_with_trace(RRDSET *st, const char *function) {
1129
- if(unlikely(!st->rrdinstance)) {
1130
- error("RRDINSTANCE: RRDSET '%s' is not linked to an RRDINSTANCE at %s()", rrdset_id(st), function);
1131
- return NULL;
1132
- }
1133
-
1134
- RRDINSTANCE *ri = rrdinstance_acquired_value(st->rrdinstance);
1135
- if(unlikely(!ri)) {
1136
- error("RRDINSTANCE: RRDSET '%s' lost its link to an RRDINSTANCE at %s()", rrdset_id(st), function);
1137
- return NULL;
1138
- }
1139
-
1140
- if(unlikely(ri->rrdset != st))
1141
- fatal("RRDINSTANCE: '%s' is not linked to RRDSET '%s' at %s()", string2str(ri->id), rrdset_id(st), function);
1142
-
1143
- return ri;
1144
-}
1145
-
1146
-static inline void rrdinstance_rrdset_is_freed(RRDSET *st) {
1147
- RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
1148
- if(unlikely(!ri)) return;
1149
-
1150
- rrd_flag_set_archived(ri);
1151
-
1152
- if(!rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) {
1153
- ri->rrdlabels = rrdlabels_create();
1154
- rrdlabels_copy(ri->rrdlabels, st->rrdlabels);
1155
- rrd_flag_set(ri, RRD_FLAG_OWN_LABELS);
1156
- }
1157
-
1158
- ri->rrdset = NULL;
1159
-
1160
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
1161
-
1162
- rrdinstance_release(st->rrdinstance);
1163
- st->rrdinstance = NULL;
1164
-
1165
- rrdcontext_release(st->rrdcontext);
1166
- st->rrdcontext = NULL;
1167
-}
1168
-
1169
-static inline void rrdinstance_rrdset_has_updated_retention(RRDSET *st) {
1170
- RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
1171
- if(unlikely(!ri)) return;
1172
-
1173
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION);
1174
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
1175
-}
1176
-
1177
-static inline void rrdinstance_updated_rrdset_name(RRDSET *st) {
1178
- // the chart may not be initialized when this is called
1179
- if(unlikely(!st->rrdinstance)) return;
1180
-
1181
- RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
1182
- if(unlikely(!ri)) return;
1183
-
1184
- if(st->name != ri->name) {
1185
- STRING *old = ri->name;
1186
- ri->name = string_dup(st->name);
1187
- string_freez(old);
1188
-
1189
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1190
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
1191
- }
1192
-}
1193
-
1194
-static inline void rrdinstance_updated_rrdset_flags_no_action(RRDINSTANCE *ri, RRDSET *st) {
1195
- if(unlikely(ri->rrdset != st))
1196
- fatal("RRDCONTEXT: instance '%s' is not linked to chart '%s' on host '%s'",
1197
- string2str(ri->id), rrdset_id(st), rrdhost_hostname(st->rrdhost));
1198
-
1199
- bool st_is_hidden = rrdset_flag_check(st, RRDSET_FLAG_HIDDEN);
1200
- bool ri_is_hidden = rrd_flag_check(ri, RRD_FLAG_HIDDEN);
1201
-
1202
- if(unlikely(st_is_hidden != ri_is_hidden)) {
1203
- if (unlikely(st_is_hidden && !ri_is_hidden))
1204
- rrd_flag_set_updated(ri, RRD_FLAG_HIDDEN | RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1205
-
1206
- else if (unlikely(!st_is_hidden && ri_is_hidden)) {
1207
- rrd_flag_clear(ri, RRD_FLAG_HIDDEN);
1208
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1209
- }
1210
- }
1211
-}
1212
-
1213
-static inline void rrdinstance_updated_rrdset_flags(RRDSET *st) {
1214
- RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
1215
- if(unlikely(!ri)) return;
1216
-
1217
- if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED|RRDSET_FLAG_OBSOLETE)))
1218
- rrd_flag_set_archived(ri);
1219
-
1220
- rrdinstance_updated_rrdset_flags_no_action(ri, st);
1221
-
1222
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
1223
-}
1224
-
1225
-static inline void rrdinstance_collected_rrdset(RRDSET *st) {
1226
- RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
1227
- if(unlikely(!ri)) return;
1228
-
1229
- rrdinstance_updated_rrdset_flags_no_action(ri, st);
1230
-
1231
- if(unlikely(ri->internal.collected_metrics_count && !rrd_flag_is_collected(ri)))
1232
- rrd_flag_set_collected(ri);
1233
-
1234
- // we use this variable to detect BEGIN/END without SET
1235
- ri->internal.collected_metrics_count = 0;
1236
-
1237
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
1238
-}
1239
-
1240
-// ----------------------------------------------------------------------------
1241
-// RRDCONTEXT
1242
-
1243
-static void rrdcontext_freez(RRDCONTEXT *rc) {
1244
- string_freez(rc->id);
1245
- string_freez(rc->title);
1246
- string_freez(rc->units);
1247
- string_freez(rc->family);
1248
-}
1249
-
1250
-static void rrdcontext_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost) {
1251
- RRDHOST *host = (RRDHOST *)rrdhost;
1252
- RRDCONTEXT *rc = (RRDCONTEXT *)value;
1253
-
1254
- rc->rrdhost = host;
1255
- rc->flags = rc->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics at constructor
1256
-
1257
- if(rc->hub.version) {
1258
- // we are loading data from the SQL database
1259
-
1260
- if(rc->version)
1261
- error("RRDCONTEXT: context '%s' is already initialized with version %"PRIu64", but it is loaded again from SQL with version %"PRIu64"", string2str(rc->id), rc->version, rc->hub.version);
1262
-
1263
- // IMPORTANT
1264
- // replace all string pointers in rc->hub with our own versions
1265
- // the originals are coming from a tmp allocation of sqlite
1266
-
1267
- string_freez(rc->id);
1268
- rc->id = string_strdupz(rc->hub.id);
1269
- rc->hub.id = string2str(rc->id);
1270
-
1271
- string_freez(rc->title);
1272
- rc->title = string_strdupz(rc->hub.title);
1273
- rc->hub.title = string2str(rc->title);
1274
-
1275
- string_freez(rc->units);
1276
- rc->units = string_strdupz(rc->hub.units);
1277
- rc->hub.units = string2str(rc->units);
1278
-
1279
- string_freez(rc->family);
1280
- rc->family = string_strdupz(rc->hub.family);
1281
- rc->hub.family = string2str(rc->family);
1282
-
1283
- rc->chart_type = rrdset_type_id(rc->hub.chart_type);
1284
- rc->hub.chart_type = rrdset_type_name(rc->chart_type);
1285
-
1286
- rc->version = rc->hub.version;
1287
- rc->priority = rc->hub.priority;
1288
- rc->first_time_s = (time_t)rc->hub.first_time_s;
1289
- rc->last_time_s = (time_t)rc->hub.last_time_s;
1290
-
1291
- if(rc->hub.deleted || !rc->hub.first_time_s)
1292
- rrd_flag_set_deleted(rc, RRD_FLAG_NONE);
1293
- else {
1294
- if (rc->last_time_s == 0)
1295
- rrd_flag_set_collected(rc);
1296
- else
1297
- rrd_flag_set_archived(rc);
1298
- }
1299
-
1300
- rc->flags |= RRD_FLAG_UPDATE_REASON_LOAD_SQL; // no need for atomics at constructor
1301
- }
1302
- else {
1303
- // we are adding this context now for the first time
1304
- rc->version = now_realtime_sec();
1305
- }
1306
-
1307
- rrdinstances_create_in_rrdcontext(rc);
1308
- netdata_mutex_init(&rc->mutex);
1309
-
1310
- // signal the react callback to do the job
1311
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
1312
-}
1313
-
1314
-static void rrdcontext_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost __maybe_unused) {
1315
-
1316
- RRDCONTEXT *rc = (RRDCONTEXT *)value;
1317
-
1318
- rrdinstances_destroy_from_rrdcontext(rc);
1319
- netdata_mutex_destroy(&rc->mutex);
1320
- rrdcontext_freez(rc);
1321
-}
1322
-
1323
-static bool rrdcontext_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdhost __maybe_unused) {
1324
- RRDCONTEXT *rc = (RRDCONTEXT *)old_value;
1325
- RRDCONTEXT *rc_new = (RRDCONTEXT *)new_value;
1326
-
1327
- //current rc is not archived, new_rc is archived, don't merge
1328
- if (!rrd_flag_is_archived(rc) && rrd_flag_is_archived(rc_new)) {
1329
- rrdcontext_freez(rc_new);
1330
- return false;
1331
- }
1332
-
1333
- rrdcontext_lock(rc);
1334
-
1335
- if(rc->title != rc_new->title) {
1336
- STRING *old_title = rc->title;
1337
- if (rrd_flag_is_archived(rc) && !rrd_flag_is_archived(rc_new))
1338
- rc->title = string_dup(rc_new->title);
1339
- else
1340
- rc->title = string_2way_merge(rc->title, rc_new->title);
1341
- string_freez(old_title);
1342
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1343
- }
1344
-
1345
- if(rc->units != rc_new->units) {
1346
- STRING *old_units = rc->units;
1347
- rc->units = string_dup(rc_new->units);
1348
- string_freez(old_units);
1349
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1350
- }
1351
-
1352
- if(rc->family != rc_new->family) {
1353
- STRING *old_family = rc->family;
1354
- if (rrd_flag_is_archived(rc) && !rrd_flag_is_archived(rc_new))
1355
- rc->family = string_dup(rc_new->family);
1356
- else
1357
- rc->family = string_2way_merge(rc->family, rc_new->family);
1358
- string_freez(old_family);
1359
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1360
- }
1361
-
1362
- if(rc->chart_type != rc_new->chart_type) {
1363
- rc->chart_type = rc_new->chart_type;
1364
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1365
- }
1366
-
1367
- if(rc->priority != rc_new->priority) {
1368
- rc->priority = rc_new->priority;
1369
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
1370
- }
1371
-
1372
- rrd_flag_set(rc, rc_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on rc_new
1373
-
1374
- if(rrd_flag_is_collected(rc) && rrd_flag_is_archived(rc))
1375
- rrd_flag_set_collected(rc);
1376
-
1377
- if(rrd_flag_is_updated(rc))
1378
- rrd_flag_set(rc, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
1379
-
1380
- rrdcontext_unlock(rc);
1381
-
1382
- // free the resources of the new one
1383
- rrdcontext_freez(rc_new);
1384
-
1385
- // the react callback will continue from here
1386
- return rrd_flag_is_updated(rc);
1387
-}
1388
-
1389
-static void rrdcontext_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost __maybe_unused) {
1390
- RRDCONTEXT *rc = (RRDCONTEXT *)value;
1391
- rrdcontext_trigger_updates(rc, __FUNCTION__ );
1392
-}
1393
-
1394
-static void rrdcontext_trigger_updates(RRDCONTEXT *rc, const char *function) {
1395
- if(rrd_flag_is_updated(rc) || !rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION))
1396
- rrdcontext_queue_for_post_processing(rc, function, rc->flags);
1397
-}
1398
-
1399
-static void rrdcontext_hub_queue_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) {
1400
- RRDCONTEXT *rc = context;
1401
- rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_HUB);
1402
- rc->queue.queued_ut = now_realtime_usec();
1403
- rc->queue.queued_flags = rrd_flags_get(rc);
1404
-}
1405
-
1406
-static void rrdcontext_hub_queue_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) {
1407
- RRDCONTEXT *rc = context;
1408
- rrd_flag_clear(rc, RRD_FLAG_QUEUED_FOR_HUB);
1409
-}
1410
-
1411
-static bool rrdcontext_hub_queue_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *new_context __maybe_unused, void *nothing __maybe_unused) {
1412
- // context and new_context are the same
1413
- // we just need to update the timings
1414
- RRDCONTEXT *rc = context;
1415
- rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_HUB);
1416
- rc->queue.queued_ut = now_realtime_usec();
1417
- rc->queue.queued_flags |= rrd_flags_get(rc);
1418
-
1419
- return true;
1420
-}
1421
-
1422
-static void rrdcontext_post_processing_queue_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) {
1423
- RRDCONTEXT *rc = context;
1424
- rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_PP);
1425
- rc->pp.queued_flags = rc->flags;
1426
- rc->pp.queued_ut = now_realtime_usec();
1427
-}
1428
-
1429
-static void rrdcontext_post_processing_queue_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) {
1430
- RRDCONTEXT *rc = context;
1431
- rrd_flag_clear(rc, RRD_FLAG_QUEUED_FOR_PP);
1432
- rc->pp.dequeued_ut = now_realtime_usec();
1433
-}
1434
-
1435
-static bool rrdcontext_post_processing_queue_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *new_context __maybe_unused, void *nothing __maybe_unused) {
1436
- RRDCONTEXT *rc = context;
1437
- bool changed = false;
1438
-
1439
- if(!(rc->flags & RRD_FLAG_QUEUED_FOR_PP)) {
1440
- rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_PP);
1441
- changed = true;
1442
- }
1443
-
1444
- if(rc->pp.queued_flags != rc->flags) {
1445
- rc->pp.queued_flags |= rc->flags;
1446
- changed = true;
1447
- }
1448
-
1449
- return changed;
1450
-}
1451
-
1452
-void rrdhost_create_rrdcontexts(RRDHOST *host) {
1453
- if(unlikely(!host)) return;
1454
- if(likely(host->rrdctx)) return;
1455
-
1456
- host->rrdctx = (RRDCONTEXTS *)dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
1457
- &dictionary_stats_category_rrdcontext, sizeof(RRDCONTEXT));
1458
-
1459
- dictionary_register_insert_callback((DICTIONARY *)host->rrdctx, rrdcontext_insert_callback, host);
1460
- dictionary_register_delete_callback((DICTIONARY *)host->rrdctx, rrdcontext_delete_callback, host);
1461
- dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx, rrdcontext_conflict_callback, host);
1462
- dictionary_register_react_callback((DICTIONARY *)host->rrdctx, rrdcontext_react_callback, host);
1463
-
1464
- host->rrdctx_hub_queue = (RRDCONTEXTS *)dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_VALUE_LINK_DONT_CLONE, &dictionary_stats_category_rrdcontext, 0);
1465
- dictionary_register_insert_callback((DICTIONARY *)host->rrdctx_hub_queue, rrdcontext_hub_queue_insert_callback, NULL);
1466
- dictionary_register_delete_callback((DICTIONARY *)host->rrdctx_hub_queue, rrdcontext_hub_queue_delete_callback, NULL);
1467
- dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx_hub_queue, rrdcontext_hub_queue_conflict_callback, NULL);
1468
-
1469
- host->rrdctx_post_processing_queue = (RRDCONTEXTS *)dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_VALUE_LINK_DONT_CLONE, &dictionary_stats_category_rrdcontext, 0);
1470
- dictionary_register_insert_callback((DICTIONARY *)host->rrdctx_post_processing_queue, rrdcontext_post_processing_queue_insert_callback, NULL);
1471
- dictionary_register_delete_callback((DICTIONARY *)host->rrdctx_post_processing_queue, rrdcontext_post_processing_queue_delete_callback, NULL);
1472
- dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx_post_processing_queue, rrdcontext_post_processing_queue_conflict_callback, NULL);
1473
-}
1474
-
1475
-void rrdhost_destroy_rrdcontexts(RRDHOST *host) {
1476
- if(unlikely(!host)) return;
1477
- if(unlikely(!host->rrdctx)) return;
1478
-
1479
- DICTIONARY *old;
1480
-
1481
- if(host->rrdctx_hub_queue) {
1482
- old = (DICTIONARY *)host->rrdctx_hub_queue;
1483
- host->rrdctx_hub_queue = NULL;
1484
-
1485
- RRDCONTEXT *rc;
1486
- dfe_start_write(old, rc) {
1487
- dictionary_del(old, string2str(rc->id));
1488
- }
1489
- dfe_done(rc);
1490
- dictionary_destroy(old);
1491
- }
1492
-
1493
- if(host->rrdctx_post_processing_queue) {
1494
- old = (DICTIONARY *)host->rrdctx_post_processing_queue;
1495
- host->rrdctx_post_processing_queue = NULL;
1496
-
1497
- RRDCONTEXT *rc;
1498
- dfe_start_write(old, rc) {
1499
- dictionary_del(old, string2str(rc->id));
1500
- }
1501
- dfe_done(rc);
1502
- dictionary_destroy(old);
1503
- }
1504
-
1505
- old = (DICTIONARY *)host->rrdctx;
1506
- host->rrdctx = NULL;
1507
- dictionary_destroy(old);
1508
-}
1509
-
1510
-// ----------------------------------------------------------------------------
1511
-// public API
1512
-
1513
-void rrdcontext_updated_rrddim(RRDDIM *rd) {
1514
- rrdmetric_from_rrddim(rd);
1515
-}
1516
-
1517
-void rrdcontext_removed_rrddim(RRDDIM *rd) {
1518
- rrdmetric_rrddim_is_freed(rd);
1519
-}
1520
-
1521
-void rrdcontext_updated_rrddim_algorithm(RRDDIM *rd) {
1522
- rrdmetric_updated_rrddim_flags(rd);
1523
-}
1524
-
1525
-void rrdcontext_updated_rrddim_multiplier(RRDDIM *rd) {
1526
- rrdmetric_updated_rrddim_flags(rd);
1527
-}
1528
-
1529
-void rrdcontext_updated_rrddim_divisor(RRDDIM *rd) {
1530
- rrdmetric_updated_rrddim_flags(rd);
1531
-}
1532
-
1533
-void rrdcontext_updated_rrddim_flags(RRDDIM *rd) {
1534
- rrdmetric_updated_rrddim_flags(rd);
1535
-}
1536
-
1537
-void rrdcontext_collected_rrddim(RRDDIM *rd) {
1538
- rrdmetric_collected_rrddim(rd);
1539
-}
1540
-
1541
-void rrdcontext_updated_rrdset(RRDSET *st) {
1542
- rrdinstance_from_rrdset(st);
1543
-}
1544
-
1545
-void rrdcontext_removed_rrdset(RRDSET *st) {
1546
- rrdinstance_rrdset_is_freed(st);
1547
-}
1548
-
1549
-void rrdcontext_updated_retention_rrdset(RRDSET *st) {
1550
- rrdinstance_rrdset_has_updated_retention(st);
1551
-}
1552
-
1553
-void rrdcontext_updated_rrdset_name(RRDSET *st) {
1554
- rrdinstance_updated_rrdset_name(st);
1555
-}
1556
-
1557
-void rrdcontext_updated_rrdset_flags(RRDSET *st) {
1558
- rrdinstance_updated_rrdset_flags(st);
1559
-}
1560
-
1561
-void rrdcontext_collected_rrdset(RRDSET *st) {
1562
- rrdinstance_collected_rrdset(st);
1563
-}
1564
-
1565
-void rrdcontext_host_child_connected(RRDHOST *host) {
1566
- (void)host;
1567
-
1568
- // no need to do anything here
1569
- ;
1570
-}
1571
-
1572
-int rrdcontext_find_dimension_uuid(RRDSET *st, const char *id, uuid_t *store_uuid) {
1573
- if(!st->rrdhost) return 1;
1574
- if(!st->context) return 2;
1575
-
1576
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(st->context));
1577
- if(!rca) return 3;
1578
-
1579
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
1580
-
1581
- RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_get_and_acquire_item(rc->rrdinstances, string2str(st->id));
1582
- if(!ria) {
1583
- rrdcontext_release(rca);
1584
- return 4;
1585
- }
1586
-
1587
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
1588
-
1589
- RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)dictionary_get_and_acquire_item(ri->rrdmetrics, id);
1590
- if(!rma) {
1591
- rrdinstance_release(ria);
1592
- rrdcontext_release(rca);
1593
- return 5;
1594
- }
1595
-
1596
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
1597
-
1598
- uuid_copy(*store_uuid, rm->uuid);
1599
-
1600
- rrdmetric_release(rma);
1601
- rrdinstance_release(ria);
1602
- rrdcontext_release(rca);
1603
- return 0;
1604
-}
1605
-
1606
-int rrdcontext_find_chart_uuid(RRDSET *st, uuid_t *store_uuid) {
1607
- if(!st->rrdhost) return 1;
1608
- if(!st->context) return 2;
1609
-
1610
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(st->context));
1611
- if(!rca) return 3;
1612
-
1613
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
1614
-
1615
- RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_get_and_acquire_item(rc->rrdinstances, string2str(st->id));
1616
- if(!ria) {
1617
- rrdcontext_release(rca);
1618
- return 4;
1619
- }
1620
-
1621
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
1622
- uuid_copy(*store_uuid, ri->uuid);
1623
-
1624
- rrdinstance_release(ria);
1625
- rrdcontext_release(rca);
1626
- return 0;
1627
-}
1628
-
1629
-void rrdcontext_host_child_disconnected(RRDHOST *host) {
1630
- rrdcontext_recalculate_host_retention(host, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD, false);
1631
-}
1632
-
1633
-static usec_t rrdcontext_next_db_rotation_ut = 0;
1634
-void rrdcontext_db_rotation(void) {
1635
- // called when the db rotates its database
1636
- rrdcontext_next_db_rotation_ut = now_realtime_usec() + FULL_RETENTION_SCAN_DELAY_AFTER_DB_ROTATION_SECS * USEC_PER_SEC;
1637
-}
1638
-
1639
-int rrdcontext_foreach_instance_with_rrdset_in_context(RRDHOST *host, const char *context, int (*callback)(RRDSET *st, void *data), void *data) {
1640
- if(unlikely(!host || !context || !*context || !callback))
1641
- return -1;
1642
-
1643
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)host->rrdctx, context);
1644
- if(unlikely(!rca)) return -1;
1645
-
1646
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
1647
- if(unlikely(!rc)) return -1;
1648
-
1649
- int ret = 0;
1650
- RRDINSTANCE *ri;
1651
- dfe_start_read(rc->rrdinstances, ri) {
1652
- if(ri->rrdset) {
1653
- int r = callback(ri->rrdset, data);
1654
- if(r >= 0) ret += r;
1655
- else {
1656
- ret = r;
1657
- break;
1658
- }
1659
- }
1660
- }
1661
- dfe_done(ri);
1662
-
1663
- rrdcontext_release(rca);
1664
-
1665
- return ret;
1666
-}
1667
-
1668
-// ----------------------------------------------------------------------------
1669
-// ACLK interface
1670
-
1671
-static bool rrdhost_check_our_claim_id(const char *claim_id) {
1672
- if(!localhost->aclk_state.claimed_id) return false;
1673
- return (strcasecmp(claim_id, localhost->aclk_state.claimed_id) == 0) ? true : false;
1674
-}
1675
-
1676
-static RRDHOST *rrdhost_find_by_node_id(const char *node_id) {
1677
- uuid_t uuid;
1678
- if (uuid_parse(node_id, uuid))
1679
- return NULL;
1680
-
1681
- RRDHOST *host = NULL;
1682
-
1683
- rrd_rdlock();
1684
- rrdhost_foreach_read(host) {
1685
- if(!host->node_id) continue;
1686
-
1687
- if(uuid_compare(uuid, *host->node_id) == 0)
1688
- break;
1689
- }
1690
- rrd_unlock();
1691
-
1692
- return host;
1693
-}
1694
-
1695
-void rrdcontext_hub_checkpoint_command(void *ptr) {
1696
- struct ctxs_checkpoint *cmd = ptr;
1697
-
1698
- if(!rrdhost_check_our_claim_id(cmd->claim_id)) {
1699
- error("RRDCONTEXT: received checkpoint command for claim_id '%s', node id '%s', but this is not our claim id. Ours '%s', received '%s'. Ignoring command.",
1700
- cmd->claim_id, cmd->node_id,
1701
- localhost->aclk_state.claimed_id?localhost->aclk_state.claimed_id:"NOT SET",
1702
- cmd->claim_id);
1703
-
1704
- return;
1705
- }
1706
-
1707
- RRDHOST *host = rrdhost_find_by_node_id(cmd->node_id);
1708
- if(!host) {
1709
- error("RRDCONTEXT: received checkpoint command for claim id '%s', node id '%s', but there is no node with such node id here. Ignoring command.",
1710
- cmd->claim_id, cmd->node_id);
1711
-
1712
- return;
1713
- }
1714
-
1715
- if(rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) {
1716
- info("RRDCONTEXT: received checkpoint command for claim id '%s', node id '%s', while node '%s' has an active context streaming.",
1717
- cmd->claim_id, cmd->node_id, rrdhost_hostname(host));
1718
-
1719
- // disable it temporarily, so that our worker will not attempt to send messages in parallel
1720
- rrdhost_flag_clear(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS);
1721
- }
1722
-
1723
- uint64_t our_version_hash = rrdcontext_version_hash(host);
1724
-
1725
- if(cmd->version_hash != our_version_hash) {
1726
- error("RRDCONTEXT: received version hash %"PRIu64" for host '%s', does not match our version hash %"PRIu64". Sending snapshot of all contexts.",
1727
- cmd->version_hash, rrdhost_hostname(host), our_version_hash);
1728
-
1729
-#ifdef ENABLE_ACLK
1730
- // prepare the snapshot
1731
- char uuid[UUID_STR_LEN];
1732
- uuid_unparse_lower(*host->node_id, uuid);
1733
- contexts_snapshot_t bundle = contexts_snapshot_new(cmd->claim_id, uuid, our_version_hash);
1734
-
1735
- // do a deep scan on every metric of the host to make sure all our data are updated
1736
- rrdcontext_recalculate_host_retention(host, RRD_FLAG_NONE, false);
1737
-
1738
- // calculate version hash and pack all the messages together in one go
1739
- our_version_hash = rrdcontext_version_hash_with_callback(host, rrdcontext_message_send_unsafe, true, bundle);
1740
-
1741
- // update the version
1742
- contexts_snapshot_set_version(bundle, our_version_hash);
1743
-
1744
- // send it
1745
- aclk_send_contexts_snapshot(bundle);
1746
-#endif
1747
- }
1748
-
1749
- internal_error(true, "RRDCONTEXT: host '%s' enabling streaming of contexts", rrdhost_hostname(host));
1750
- rrdhost_flag_set(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS);
1751
- char node_str[UUID_STR_LEN];
1752
- uuid_unparse_lower(*host->node_id, node_str);
1753
- log_access("ACLK REQ [%s (%s)]: STREAM CONTEXTS ENABLED", node_str, rrdhost_hostname(host));
1754
-}
1755
-
1756
-void rrdcontext_hub_stop_streaming_command(void *ptr) {
1757
- struct stop_streaming_ctxs *cmd = ptr;
1758
-
1759
- if(!rrdhost_check_our_claim_id(cmd->claim_id)) {
1760
- error("RRDCONTEXT: received stop streaming command for claim_id '%s', node id '%s', but this is not our claim id. Ours '%s', received '%s'. Ignoring command.",
1761
- cmd->claim_id, cmd->node_id,
1762
- localhost->aclk_state.claimed_id?localhost->aclk_state.claimed_id:"NOT SET",
1763
- cmd->claim_id);
1764
-
1765
- return;
1766
- }
1767
-
1768
- RRDHOST *host = rrdhost_find_by_node_id(cmd->node_id);
1769
- if(!host) {
1770
- error("RRDCONTEXT: received stop streaming command for claim id '%s', node id '%s', but there is no node with such node id here. Ignoring command.",
1771
- cmd->claim_id, cmd->node_id);
1772
-
1773
- return;
1774
- }
1775
-
1776
- if(!rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) {
1777
- error("RRDCONTEXT: received stop streaming command for claim id '%s', node id '%s', but node '%s' does not have active context streaming. Ignoring command.",
1778
- cmd->claim_id, cmd->node_id, rrdhost_hostname(host));
1779
-
1780
- return;
1781
- }
1782
-
1783
- internal_error(true, "RRDCONTEXT: host '%s' disabling streaming of contexts", rrdhost_hostname(host));
1784
- rrdhost_flag_clear(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS);
1785
-}
1786
-
1787
-// ----------------------------------------------------------------------------
1788
-// web API
1789
-
1790
-struct rrdcontext_to_json {
1791
- BUFFER *wb;
1792
- RRDCONTEXT_TO_JSON_OPTIONS options;
1793
- time_t after;
1794
- time_t before;
1795
- SIMPLE_PATTERN *chart_label_key;
1796
- SIMPLE_PATTERN *chart_labels_filter;
1797
- SIMPLE_PATTERN *chart_dimensions;
1798
- size_t written;
1799
- time_t now;
1800
- time_t combined_first_time_s;
1801
- time_t combined_last_time_s;
1802
- RRD_FLAGS combined_flags;
1803
-};
1804
-
1805
-static inline int rrdmetric_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
1806
- const char *id = dictionary_acquired_item_name(item);
1807
- struct rrdcontext_to_json * t = data;
1808
- RRDMETRIC *rm = value;
1809
- BUFFER *wb = t->wb;
1810
- RRDCONTEXT_TO_JSON_OPTIONS options = t->options;
1811
- time_t after = t->after;
1812
- time_t before = t->before;
1813
-
1814
- if(unlikely(rrd_flag_is_deleted(rm) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
1815
- return 0;
1816
-
1817
- if(after && (!rm->last_time_s || after > rm->last_time_s))
1818
- return 0;
1819
-
1820
- if(before && (!rm->first_time_s || before < rm->first_time_s))
1821
- return 0;
1822
-
1823
- if(t->chart_dimensions
1824
- && !simple_pattern_matches(t->chart_dimensions, string2str(rm->id))
1825
- && !simple_pattern_matches(t->chart_dimensions, string2str(rm->name)))
1826
- return 0;
1827
-
1828
- if(t->written) {
1829
- t->combined_first_time_s = MIN(t->combined_first_time_s, rm->first_time_s);
1830
- t->combined_last_time_s = MAX(t->combined_last_time_s, rm->last_time_s);
1831
- t->combined_flags |= rrd_flags_get(rm);
1832
- }
1833
- else {
1834
- t->combined_first_time_s = rm->first_time_s;
1835
- t->combined_last_time_s = rm->last_time_s;
1836
- t->combined_flags = rrd_flags_get(rm);
1837
- }
1838
-
1839
- buffer_json_member_add_object(wb, id);
1840
-
1841
- if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
1842
- char uuid[UUID_STR_LEN];
1843
- uuid_unparse(rm->uuid, uuid);
1844
- buffer_json_member_add_string(wb, "uuid", uuid);
1845
- }
1846
-
1847
- buffer_json_member_add_string(wb, "name", string2str(rm->name));
1848
- buffer_json_member_add_time_t(wb, "first_time_t", rm->first_time_s);
1849
- buffer_json_member_add_time_t(wb, "last_time_t", rrd_flag_is_collected(rm) ? (long long)t->now : (long long)rm->last_time_s);
1850
- buffer_json_member_add_boolean(wb, "collected", rrd_flag_is_collected(rm));
1851
-
1852
- if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
1853
- buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(rm));
1854
-
1855
- if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
1856
- buffer_json_member_add_array(wb, "flags");
1857
- rrd_flags_to_buffer_json_array_items(rrd_flags_get(rm), wb);
1858
- buffer_json_array_close(wb);
1859
- }
1860
-
1861
- buffer_json_object_close(wb);
1862
- t->written++;
1863
- return 1;
1864
-}
1865
-
1866
-static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
1867
- const char *id = dictionary_acquired_item_name(item);
1868
-
1869
- struct rrdcontext_to_json *t_parent = data;
1870
- RRDINSTANCE *ri = value;
1871
- BUFFER *wb = t_parent->wb;
1872
- RRDCONTEXT_TO_JSON_OPTIONS options = t_parent->options;
1873
- time_t after = t_parent->after;
1874
- time_t before = t_parent->before;
1875
- bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions;
1876
-
1877
- if(unlikely(rrd_flag_is_deleted(ri) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
1878
- return 0;
1879
-
1880
- if(after && (!ri->last_time_s || after > ri->last_time_s))
1881
- return 0;
1882
-
1883
- if(before && (!ri->first_time_s || before < ri->first_time_s))
1884
- return 0;
1885
-
1886
- if(t_parent->chart_label_key && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_label_key, '\0'))
1887
- return 0;
1888
-
1889
- if(t_parent->chart_labels_filter && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_labels_filter, ':'))
1890
- return 0;
1891
-
1892
- time_t first_time_s = ri->first_time_s;
1893
- time_t last_time_s = ri->last_time_s;
1894
- RRD_FLAGS flags = rrd_flags_get(ri);
1895
-
1896
- BUFFER *wb_metrics = NULL;
1897
- if(options & RRDCONTEXT_OPTION_SHOW_METRICS || t_parent->chart_dimensions) {
1898
-
1899
- wb_metrics = buffer_create(4096, &netdata_buffers_statistics.buffers_api);
1900
- buffer_json_initialize(wb_metrics, "\"", "\"", wb->json.depth + 2, false);
1901
-
1902
- struct rrdcontext_to_json t_metrics = {
1903
- .wb = wb_metrics,
1904
- .options = options,
1905
- .chart_label_key = t_parent->chart_label_key,
1906
- .chart_labels_filter = t_parent->chart_labels_filter,
1907
- .chart_dimensions = t_parent->chart_dimensions,
1908
- .after = after,
1909
- .before = before,
1910
- .written = 0,
1911
- .now = t_parent->now,
1912
- };
1913
- dictionary_walkthrough_read(ri->rrdmetrics, rrdmetric_to_json_callback, &t_metrics);
1914
-
1915
- if(has_filter && !t_metrics.written) {
1916
- buffer_free(wb_metrics);
1917
- return 0;
1918
- }
1919
-
1920
- first_time_s = t_metrics.combined_first_time_s;
1921
- last_time_s = t_metrics.combined_last_time_s;
1922
- flags = t_metrics.combined_flags;
1923
- }
1924
-
1925
- if(t_parent->written) {
1926
- t_parent->combined_first_time_s = MIN(t_parent->combined_first_time_s, first_time_s);
1927
- t_parent->combined_last_time_s = MAX(t_parent->combined_last_time_s, last_time_s);
1928
- t_parent->combined_flags |= flags;
1929
- }
1930
- else {
1931
- t_parent->combined_first_time_s = first_time_s;
1932
- t_parent->combined_last_time_s = last_time_s;
1933
- t_parent->combined_flags = flags;
1934
- }
1935
-
1936
- buffer_json_member_add_object(wb, id);
1937
-
1938
- if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
1939
- char uuid[UUID_STR_LEN];
1940
- uuid_unparse(ri->uuid, uuid);
1941
- buffer_json_member_add_string(wb, "uuid", uuid);
1942
- }
1943
-
1944
- buffer_json_member_add_string(wb, "name", string2str(ri->name));
1945
- buffer_json_member_add_string(wb, "context", string2str(ri->rc->id));
1946
- buffer_json_member_add_string(wb, "title", string2str(ri->title));
1947
- buffer_json_member_add_string(wb, "units", string2str(ri->units));
1948
- buffer_json_member_add_string(wb, "family", string2str(ri->family));
1949
- buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(ri->chart_type));
1950
- buffer_json_member_add_uint64(wb, "priority", ri->priority);
1951
- buffer_json_member_add_time_t(wb, "update_every", ri->update_every_s);
1952
- buffer_json_member_add_time_t(wb, "first_time_t", first_time_s);
1953
- buffer_json_member_add_time_t(wb, "last_time_t", (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_s);
1954
- buffer_json_member_add_boolean(wb, "collected", flags & RRD_FLAG_COLLECTED);
1955
-
1956
- if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
1957
- buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(ri));
1958
-
1959
- if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
1960
- buffer_json_member_add_array(wb, "flags");
1961
- rrd_flags_to_buffer_json_array_items(rrd_flags_get(ri), wb);
1962
- buffer_json_array_close(wb);
1963
- }
1964
-
1965
- if(options & RRDCONTEXT_OPTION_SHOW_LABELS && ri->rrdlabels && dictionary_entries(ri->rrdlabels)) {
1966
- buffer_json_member_add_object(wb, "labels");
1967
- rrdlabels_to_buffer_json_members(ri->rrdlabels, wb);
1968
- buffer_json_object_close(wb);
1969
- }
1970
-
1971
- if(wb_metrics) {
1972
- buffer_json_member_add_object(wb, "dimensions");
1973
- buffer_fast_strcat(wb, buffer_tostring(wb_metrics), buffer_strlen(wb_metrics));
1974
- buffer_json_object_close(wb);
1975
-
1976
- buffer_free(wb_metrics);
1977
- }
1978
-
1979
- buffer_json_object_close(wb);
1980
- t_parent->written++;
1981
- return 1;
1982
-}
1983
-
1984
-static inline int rrdcontext_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
1985
- const char *id = dictionary_acquired_item_name(item);
1986
- struct rrdcontext_to_json *t_parent = data;
1987
- RRDCONTEXT *rc = value;
1988
- BUFFER *wb = t_parent->wb;
1989
- RRDCONTEXT_TO_JSON_OPTIONS options = t_parent->options;
1990
- time_t after = t_parent->after;
1991
- time_t before = t_parent->before;
1992
- bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions;
1993
-
1994
- if(unlikely(rrd_flag_check(rc, RRD_FLAG_HIDDEN) && !(options & RRDCONTEXT_OPTION_SHOW_HIDDEN)))
1995
- return 0;
1996
-
1997
- if(unlikely(rrd_flag_is_deleted(rc) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
1998
- return 0;
1999
-
2000
- if(options & RRDCONTEXT_OPTION_DEEPSCAN)
2001
- rrdcontext_recalculate_context_retention(rc, RRD_FLAG_NONE, false);
2002
-
2003
- if(after && (!rc->last_time_s || after > rc->last_time_s))
2004
- return 0;
2005
-
2006
- if(before && (!rc->first_time_s || before < rc->first_time_s))
2007
- return 0;
2008
-
2009
- time_t first_time_s = rc->first_time_s;
2010
- time_t last_time_s = rc->last_time_s;
2011
- RRD_FLAGS flags = rrd_flags_get(rc);
2012
-
2013
- BUFFER *wb_instances = NULL;
2014
- if((options & (RRDCONTEXT_OPTION_SHOW_LABELS|RRDCONTEXT_OPTION_SHOW_INSTANCES|RRDCONTEXT_OPTION_SHOW_METRICS))
2015
- || t_parent->chart_label_key
2016
- || t_parent->chart_labels_filter
2017
- || t_parent->chart_dimensions) {
2018
-
2019
- wb_instances = buffer_create(4096, &netdata_buffers_statistics.buffers_api);
2020
- buffer_json_initialize(wb_instances, "\"", "\"", wb->json.depth + 2, false);
2021
-
2022
- struct rrdcontext_to_json t_instances = {
2023
- .wb = wb_instances,
2024
- .options = options,
2025
- .chart_label_key = t_parent->chart_label_key,
2026
- .chart_labels_filter = t_parent->chart_labels_filter,
2027
- .chart_dimensions = t_parent->chart_dimensions,
2028
- .after = after,
2029
- .before = before,
2030
- .written = 0,
2031
- .now = t_parent->now,
2032
- };
2033
- dictionary_walkthrough_read(rc->rrdinstances, rrdinstance_to_json_callback, &t_instances);
2034
-
2035
- if(has_filter && !t_instances.written) {
2036
- buffer_free(wb_instances);
2037
- return 0;
2038
- }
2039
-
2040
- first_time_s = t_instances.combined_first_time_s;
2041
- last_time_s = t_instances.combined_last_time_s;
2042
- flags = t_instances.combined_flags;
2043
- }
2044
-
2045
- if(!(options & RRDCONTEXT_OPTION_SKIP_ID))
2046
- buffer_json_member_add_object(wb, id);
2047
-
2048
- rrdcontext_lock(rc);
2049
-
2050
- buffer_json_member_add_string(wb, "title", string2str(rc->title));
2051
- buffer_json_member_add_string(wb, "units", string2str(rc->units));
2052
- buffer_json_member_add_string(wb, "family", string2str(rc->family));
2053
- buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rc->chart_type));
2054
- buffer_json_member_add_uint64(wb, "priority", rc->priority);
2055
- buffer_json_member_add_time_t(wb, "first_time_t", first_time_s);
2056
- buffer_json_member_add_time_t(wb, "last_time_t", (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_s);
2057
- buffer_json_member_add_boolean(wb, "collected", (flags & RRD_FLAG_COLLECTED));
2058
-
2059
- if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
2060
- buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(rc));
2061
-
2062
- if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
2063
- buffer_json_member_add_array(wb, "flags");
2064
- rrd_flags_to_buffer_json_array_items(rrd_flags_get(rc), wb);
2065
- buffer_json_array_close(wb);
2066
- }
2067
-
2068
- if(options & RRDCONTEXT_OPTION_SHOW_QUEUED) {
2069
- buffer_json_member_add_array(wb, "queued_reasons");
2070
- rrd_reasons_to_buffer_json_array_items(rc->queue.queued_flags, wb);
2071
- buffer_json_array_close(wb);
2072
-
2073
- buffer_json_member_add_time_t(wb, "last_queued", (time_t)(rc->queue.queued_ut / USEC_PER_SEC));
2074
- buffer_json_member_add_time_t(wb, "scheduled_dispatch", (time_t)(rc->queue.scheduled_dispatch_ut / USEC_PER_SEC));
2075
- buffer_json_member_add_time_t(wb, "last_dequeued", (time_t)(rc->queue.dequeued_ut / USEC_PER_SEC));
2076
- buffer_json_member_add_uint64(wb, "dispatches", rc->queue.dispatches);
2077
- buffer_json_member_add_uint64(wb, "hub_version", rc->hub.version);
2078
- buffer_json_member_add_uint64(wb, "version", rc->version);
2079
-
2080
- buffer_json_member_add_array(wb, "pp_reasons");
2081
- rrd_reasons_to_buffer_json_array_items(rc->pp.queued_flags, wb);
2082
- buffer_json_array_close(wb);
2083
-
2084
- buffer_json_member_add_time_t(wb, "pp_last_queued", (time_t)(rc->pp.queued_ut / USEC_PER_SEC));
2085
- buffer_json_member_add_time_t(wb, "pp_last_dequeued", (time_t)(rc->pp.dequeued_ut / USEC_PER_SEC));
2086
- buffer_json_member_add_uint64(wb, "pp_executed", rc->pp.executions);
2087
- }
2088
-
2089
- rrdcontext_unlock(rc);
2090
-
2091
- if(wb_instances) {
2092
- buffer_json_member_add_object(wb, "charts");
2093
- buffer_fast_strcat(wb, buffer_tostring(wb_instances), buffer_strlen(wb_instances));
2094
- buffer_json_object_close(wb);
2095
-
2096
- buffer_free(wb_instances);
2097
- }
2098
-
2099
- if(!(options & RRDCONTEXT_OPTION_SKIP_ID))
2100
- buffer_json_object_close(wb);
2101
-
2102
- t_parent->written++;
2103
- return 1;
2104
-}
2105
-
2106
-int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, const char *context, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) {
2107
- if(!host->rrdctx) {
2108
- error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host));
2109
- return HTTP_RESP_NOT_FOUND;
2110
- }
2111
-
2112
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)host->rrdctx, context);
2113
- if(!rca) return HTTP_RESP_NOT_FOUND;
2114
-
2115
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
2116
-
2117
- if(after != 0 && before != 0)
2118
- rrdr_relative_window_to_absolute(&after, &before);
2119
-
2120
- buffer_json_initialize(wb, "\"", "\"", 0, true);
2121
- struct rrdcontext_to_json t_contexts = {
2122
- .wb = wb,
2123
- .options = options|RRDCONTEXT_OPTION_SKIP_ID,
2124
- .chart_label_key = chart_label_key,
2125
- .chart_labels_filter = chart_labels_filter,
2126
- .chart_dimensions = chart_dimensions,
2127
- .after = after,
2128
- .before = before,
2129
- .written = 0,
2130
- .now = now_realtime_sec(),
2131
- };
2132
- rrdcontext_to_json_callback((DICTIONARY_ITEM *)rca, rc, &t_contexts);
2133
- buffer_json_finalize(wb);
2134
-
2135
- rrdcontext_release(rca);
2136
-
2137
- if(!t_contexts.written)
2138
- return HTTP_RESP_NOT_FOUND;
2139
-
2140
- return HTTP_RESP_OK;
2141
-}
2142
-
2143
-int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) {
2144
- if(!host->rrdctx) {
2145
- error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host));
2146
- return HTTP_RESP_NOT_FOUND;
2147
- }
2148
-
2149
- char node_uuid[UUID_STR_LEN] = "";
2150
-
2151
- if(host->node_id)
2152
- uuid_unparse(*host->node_id, node_uuid);
2153
-
2154
- if(after != 0 && before != 0)
2155
- rrdr_relative_window_to_absolute(&after, &before);
2156
-
2157
- buffer_json_initialize(wb, "\"", "\"", 0, true);
2158
- buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(host));
2159
- buffer_json_member_add_string(wb, "machine_guid", host->machine_guid);
2160
- buffer_json_member_add_string(wb, "node_id", node_uuid);
2161
- buffer_json_member_add_string(wb, "claim_id", host->aclk_state.claimed_id ? host->aclk_state.claimed_id : "");
2162
-
2163
- if(options & RRDCONTEXT_OPTION_SHOW_LABELS) {
2164
- buffer_json_member_add_object(wb, "host_labels");
2165
- rrdlabels_to_buffer_json_members(host->rrdlabels, wb);
2166
- buffer_json_object_close(wb);
2167
- }
2168
-
2169
- buffer_json_member_add_object(wb, "contexts");
2170
- struct rrdcontext_to_json t_contexts = {
2171
- .wb = wb,
2172
- .options = options,
2173
- .chart_label_key = chart_label_key,
2174
- .chart_labels_filter = chart_labels_filter,
2175
- .chart_dimensions = chart_dimensions,
2176
- .after = after,
2177
- .before = before,
2178
- .written = 0,
2179
- .now = now_realtime_sec(),
2180
- };
2181
- dictionary_walkthrough_read((DICTIONARY *)host->rrdctx, rrdcontext_to_json_callback, &t_contexts);
2182
- buffer_json_object_close(wb);
2183
-
2184
- buffer_json_finalize(wb);
2185
-
2186
- return HTTP_RESP_OK;
2187
-}
2188
-
2189
-// ----------------------------------------------------------------------------
2190
-// weights API
2191
-
2192
-static void metric_entry_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
2193
- struct metric_entry *t = value;
2194
- t->rca = rrdcontext_acquired_dup(t->rca);
2195
- t->ria = rrdinstance_acquired_dup(t->ria);
2196
- t->rma = rrdmetric_acquired_dup(t->rma);
2197
-}
2198
-static void metric_entry_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
2199
- struct metric_entry *t = value;
2200
- rrdcontext_release(t->rca);
2201
- rrdinstance_release(t->ria);
2202
- rrdmetric_release(t->rma);
2203
-}
2204
-static bool metric_entry_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value __maybe_unused, void *new_value __maybe_unused, void *data __maybe_unused) {
2205
- internal_fatal("RRDCONTEXT: %s() detected a conflict on a metric pointer!", __FUNCTION__);
2206
- return false;
2207
-}
2208
-
2209
-DICTIONARY *rrdcontext_all_metrics_to_dict(RRDHOST *host, SIMPLE_PATTERN *contexts) {
2210
- if(!host || !host->rrdctx)
2211
- return NULL;
2212
-
2213
- DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE, &dictionary_stats_category_rrdcontext, 0);
2214
- dictionary_register_insert_callback(dict, metric_entry_insert_callback, NULL);
2215
- dictionary_register_delete_callback(dict, metric_entry_delete_callback, NULL);
2216
- dictionary_register_conflict_callback(dict, metric_entry_conflict_callback, NULL);
2217
-
2218
- RRDCONTEXT *rc;
2219
- dfe_start_reentrant((DICTIONARY *)host->rrdctx, rc) {
2220
- if(rrd_flag_is_deleted(rc))
2221
- continue;
2222
-
2223
- if(contexts && !simple_pattern_matches(contexts, string2str(rc->id)))
2224
- continue;
2225
-
2226
- RRDINSTANCE *ri;
2227
- dfe_start_read(rc->rrdinstances, ri) {
2228
- if(rrd_flag_is_deleted(ri))
2229
- continue;
2230
-
2231
- RRDMETRIC *rm;
2232
- dfe_start_read(ri->rrdmetrics, rm) {
2233
- if(rrd_flag_is_deleted(rm))
2234
- continue;
2235
-
2236
- struct metric_entry tmp = {
2237
- .rca = (RRDCONTEXT_ACQUIRED *)rc_dfe.item,
2238
- .ria = (RRDINSTANCE_ACQUIRED *)ri_dfe.item,
2239
- .rma = (RRDMETRIC_ACQUIRED *)rm_dfe.item,
2240
- };
2241
-
2242
- char buffer[20 + 1];
2243
- ssize_t len = snprintfz(buffer, 20, "%p", rm);
2244
- dictionary_set_advanced(dict, buffer, len + 1, &tmp, sizeof(struct metric_entry), NULL);
2245
- }
2246
- dfe_done(rm);
2247
- }
2248
- dfe_done(ri);
2249
- }
2250
- dfe_done(rc);
2251
-
2252
- return dict;
2253
-}
2254
-
2255
-// ----------------------------------------------------------------------------
2256
-// query API
2257
-
2258
-typedef struct query_target_locals {
2259
- time_t start_s;
2260
-
2261
- QUERY_TARGET *qt;
2262
-
2263
- RRDSET *st;
2264
-
2265
- const char *scope_hosts;
2266
- const char *scope_contexts;
2267
-
2268
- const char *hosts;
2269
- const char *contexts;
2270
- const char *charts;
2271
- const char *dimensions;
2272
- const char *chart_label_key;
2273
- const char *labels;
2274
- const char *alerts;
2275
-
2276
- long long after;
2277
- long long before;
2278
- bool match_ids;
2279
- bool match_names;
2280
-
2281
- size_t metrics_skipped_due_to_not_matching_timeframe;
2282
-} QUERY_TARGET_LOCALS;
2283
-
2284
-static __thread QUERY_TARGET thread_query_target = {};
2285
-void query_target_release(QUERY_TARGET *qt) {
2286
- if(unlikely(!qt || !qt->used)) return;
2287
-
2288
- simple_pattern_free(qt->hosts.scope_pattern);
2289
- qt->hosts.scope_pattern = NULL;
2290
-
2291
- simple_pattern_free(qt->hosts.pattern);
2292
- qt->hosts.pattern = NULL;
2293
-
2294
- simple_pattern_free(qt->contexts.scope_pattern);
2295
- qt->contexts.scope_pattern = NULL;
2296
-
2297
- simple_pattern_free(qt->contexts.pattern);
2298
- qt->contexts.pattern = NULL;
2299
-
2300
- simple_pattern_free(qt->instances.pattern);
2301
- qt->instances.pattern = NULL;
2302
-
2303
- simple_pattern_free(qt->instances.chart_label_key_pattern);
2304
- qt->instances.chart_label_key_pattern = NULL;
2305
-
2306
- simple_pattern_free(qt->instances.labels_pattern);
2307
- qt->instances.labels_pattern = NULL;
2308
-
2309
- simple_pattern_free(qt->query.pattern);
2310
- qt->query.pattern = NULL;
2311
-
2312
- // release the query
2313
- for(size_t i = 0, used = qt->query.used; i < used ;i++) {
2314
- QUERY_METRIC *qm = query_metric(qt, i);
2315
-
2316
- // reset the plans
2317
- for(size_t p = 0; p < qm->plan.used; p++) {
2318
- internal_fatal(qm->plan.array[p].initialized &&
2319
- !qm->plan.array[p].finalized,
2320
- "QUERY: left-over initialized plan");
2321
-
2322
- qm->plan.array[p].initialized = false;
2323
- qm->plan.array[p].finalized = false;
2324
- }
2325
- qm->plan.used = 0;
2326
-
2327
- // reset the tiers
2328
- for(size_t tier = 0; tier < storage_tiers ;tier++) {
2329
- if(qm->tiers[tier].db_metric_handle) {
2330
- STORAGE_ENGINE *eng = qm->tiers[tier].eng;
2331
- eng->api.metric_release(qm->tiers[tier].db_metric_handle);
2332
- qm->tiers[tier].db_metric_handle = NULL;
2333
- qm->tiers[tier].eng = NULL;
2334
- }
2335
- }
2336
- }
2337
- qt->query.used = 0;
2338
-
2339
- // release the dimensions
2340
- for(size_t i = 0, used = qt->dimensions.used; i < used ; i++) {
2341
- QUERY_DIMENSION *qd = query_dimension(qt, i);
2342
- rrdmetric_release(qd->rma);
2343
- qd->rma = NULL;
2344
- }
2345
- qt->dimensions.used = 0;
2346
-
2347
- // release the instances
2348
- for(size_t i = 0, used = qt->instances.used; i < used ;i++) {
2349
- QUERY_INSTANCE *qi = &qt->instances.array[i];
2350
-
2351
- rrdinstance_release(qi->ria);
2352
- qi->ria = NULL;
2353
-
2354
- string_freez(qi->id_fqdn);
2355
- qi->id_fqdn = NULL;
2356
-
2357
- string_freez(qi->name_fqdn);
2358
- qi->name_fqdn = NULL;
2359
- }
2360
- qt->instances.used = 0;
2361
-
2362
- // release the contexts
2363
- for(size_t i = 0, used = qt->contexts.used; i < used ;i++) {
2364
- QUERY_CONTEXT *qc = query_context(qt, i);
2365
- rrdcontext_release(qc->rca);
2366
- qc->rca = NULL;
2367
- }
2368
- qt->contexts.used = 0;
2369
-
2370
- // release the hosts
2371
- for(size_t i = 0, used = qt->hosts.used; i < used ;i++) {
2372
- QUERY_HOST *qh = query_host(qt, i);
2373
- qh->host = NULL;
2374
- }
2375
- qt->hosts.used = 0;
2376
-
2377
- qt->db.minimum_latest_update_every_s = 0;
2378
- qt->db.first_time_s = 0;
2379
- qt->db.last_time_s = 0;
2380
-
2381
- qt->group_by.used = 0;
2382
-
2383
- qt->id[0] = '\0';
2384
-
2385
- qt->used = false;
2386
-}
2387
-void query_target_free(void) {
2388
- QUERY_TARGET *qt = &thread_query_target;
2389
-
2390
- if(qt->used)
2391
- query_target_release(qt);
2392
-
2393
- __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->query.size * sizeof(QUERY_METRIC), __ATOMIC_RELAXED);
2394
- freez(qt->query.array);
2395
- qt->query.array = NULL;
2396
- qt->query.size = 0;
2397
-
2398
- __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->dimensions.size * sizeof(RRDMETRIC_ACQUIRED *), __ATOMIC_RELAXED);
2399
- freez(qt->dimensions.array);
2400
- qt->dimensions.array = NULL;
2401
- qt->dimensions.size = 0;
2402
-
2403
- __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->instances.size * sizeof(RRDINSTANCE_ACQUIRED *), __ATOMIC_RELAXED);
2404
- freez(qt->instances.array);
2405
- qt->instances.array = NULL;
2406
- qt->instances.size = 0;
2407
-
2408
- __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->contexts.size * sizeof(RRDCONTEXT_ACQUIRED *), __ATOMIC_RELAXED);
2409
- freez(qt->contexts.array);
2410
- qt->contexts.array = NULL;
2411
- qt->contexts.size = 0;
2412
-
2413
- __atomic_sub_fetch(&netdata_buffers_statistics.query_targets_size, qt->hosts.size * sizeof(RRDHOST *), __ATOMIC_RELAXED);
2414
- freez(qt->hosts.array);
2415
- qt->hosts.array = NULL;
2416
- qt->hosts.size = 0;
2417
-}
2418
-
2419
-static void query_target_add_metric(QUERY_TARGET_LOCALS *qtl, QUERY_HOST *qh, QUERY_CONTEXT *qc,
2420
- QUERY_INSTANCE *qi, QUERY_DIMENSION *qd) {
2421
- QUERY_TARGET *qt = qtl->qt;
2422
- RRDMETRIC *rm = rrdmetric_acquired_value(qd->rma);
2423
- RRDINSTANCE *ri = rm->ri;
2424
-
2425
- time_t common_first_time_s = 0;
2426
- time_t common_last_time_s = 0;
2427
- time_t common_update_every_s = 0;
2428
- size_t tiers_added = 0;
2429
-
2430
- struct {
2431
- STORAGE_ENGINE *eng;
2432
- STORAGE_METRIC_HANDLE *db_metric_handle;
2433
- time_t db_first_time_s;
2434
- time_t db_last_time_s;
2435
- time_t db_update_every_s;
2436
- } tier_retention[storage_tiers];
2437
-
2438
- for (size_t tier = 0; tier < storage_tiers; tier++) {
2439
- STORAGE_ENGINE *eng = qh->host->db[tier].eng;
2440
- tier_retention[tier].eng = eng;
2441
- tier_retention[tier].db_update_every_s = (time_t) (qh->host->db[tier].tier_grouping * ri->update_every_s);
2442
-
2443
- if(rm->rrddim && rm->rrddim->tiers[tier].db_metric_handle)
2444
- tier_retention[tier].db_metric_handle = eng->api.metric_dup(rm->rrddim->tiers[tier].db_metric_handle);
2445
- else
2446
- tier_retention[tier].db_metric_handle = eng->api.metric_get(qh->host->db[tier].instance, &rm->uuid);
2447
-
2448
- if(tier_retention[tier].db_metric_handle) {
2449
- tier_retention[tier].db_first_time_s = tier_retention[tier].eng->api.query_ops.oldest_time_s(tier_retention[tier].db_metric_handle);
2450
- tier_retention[tier].db_last_time_s = tier_retention[tier].eng->api.query_ops.latest_time_s(tier_retention[tier].db_metric_handle);
2451
-
2452
- if(!common_first_time_s)
2453
- common_first_time_s = tier_retention[tier].db_first_time_s;
2454
- else if(tier_retention[tier].db_first_time_s)
2455
- common_first_time_s = MIN(common_first_time_s, tier_retention[tier].db_first_time_s);
2456
-
2457
- if(!common_last_time_s)
2458
- common_last_time_s = tier_retention[tier].db_last_time_s;
2459
- else
2460
- common_last_time_s = MAX(common_last_time_s, tier_retention[tier].db_last_time_s);
2461
-
2462
- if(!common_update_every_s)
2463
- common_update_every_s = tier_retention[tier].db_update_every_s;
2464
- else if(tier_retention[tier].db_update_every_s)
2465
- common_update_every_s = MIN(common_update_every_s, tier_retention[tier].db_update_every_s);
2466
-
2467
- tiers_added++;
2468
- }
2469
- else {
2470
- tier_retention[tier].db_first_time_s = 0;
2471
- tier_retention[tier].db_last_time_s = 0;
2472
- tier_retention[tier].db_update_every_s = 0;
2473
- }
2474
- }
2475
-
2476
- bool release_retention = true;
2477
- bool timeframe_matches =
2478
- (tiers_added
2479
- && (common_first_time_s - common_update_every_s * 2) <= qt->window.before
2480
- && (common_last_time_s + common_update_every_s * 2) >= qt->window.after
2481
- ) ? true : false;
2482
-
2483
- if(timeframe_matches) {
2484
- RRDR_DIMENSION_FLAGS options = RRDR_DIMENSION_DEFAULT;
2485
-
2486
- if (rrd_flag_check(rm, RRD_FLAG_HIDDEN)
2487
- || (rm->rrddim && rrddim_option_check(rm->rrddim, RRDDIM_OPTION_HIDDEN))) {
2488
- options |= RRDR_DIMENSION_HIDDEN;
2489
- options &= ~RRDR_DIMENSION_SELECTED;
2490
- }
2491
-
2492
- if (qt->query.pattern) {
2493
- // we have a dimensions pattern
2494
- // lets see if this dimension is selected
2495
-
2496
- if ((qtl->match_ids && simple_pattern_matches(qt->query.pattern, string2str(rm->id)))
2497
- || (qtl->match_names && simple_pattern_matches(qt->query.pattern, string2str(rm->name)))
2498
- ) {
2499
- // it matches the pattern
2500
- options |= (RRDR_DIMENSION_SELECTED | RRDR_DIMENSION_NONZERO);
2501
- options &= ~RRDR_DIMENSION_HIDDEN;
2502
- }
2503
- else {
2504
- // it does not match the pattern
2505
- options |= RRDR_DIMENSION_HIDDEN;
2506
- options &= ~RRDR_DIMENSION_SELECTED;
2507
- }
2508
- }
2509
- else {
2510
- // we don't have a dimensions pattern
2511
- // so this is a selected dimension
2512
- // if it is not hidden
2513
- if(!(options & RRDR_DIMENSION_HIDDEN))
2514
- options |= RRDR_DIMENSION_SELECTED;
2515
- }
2516
-
2517
- if((options & RRDR_DIMENSION_HIDDEN) && (options & RRDR_DIMENSION_SELECTED))
2518
- options &= ~RRDR_DIMENSION_HIDDEN;
2519
-
2520
- if(!(options & RRDR_DIMENSION_HIDDEN) || (qt->request.options & RRDR_OPTION_PERCENTAGE)) {
2521
- // we have a non-hidden dimension
2522
- // let's add it to the query metrics
2523
-
2524
- if(ri->rrdset)
2525
- ri->rrdset->last_accessed_time_s = qtl->start_s;
2526
-
2527
- if (qt->query.used == qt->query.size) {
2528
- size_t old_mem = qt->query.size * sizeof(*qt->query.array);
2529
- qt->query.size = (qt->query.size) ? qt->query.size * 2 : 1;
2530
- size_t new_mem = qt->query.size * sizeof(*qt->query.array);
2531
- qt->query.array = reallocz(qt->query.array, new_mem);
2532
-
2533
- __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
2534
- }
2535
- QUERY_METRIC *qm = &qt->query.array[qt->query.used++];
2536
- memset(qm, 0, sizeof(*qm));
2537
-
2538
- qm->status = options;
2539
-
2540
- qm->link.query_host_id = qh->slot;
2541
- qm->link.query_context_id = qc->slot;
2542
- qm->link.query_instance_id = qi->slot;
2543
- qm->link.query_dimension_id = qd->slot;
2544
-
2545
- if (!qt->db.first_time_s || common_first_time_s < qt->db.first_time_s)
2546
- qt->db.first_time_s = common_first_time_s;
2547
-
2548
- if (!qt->db.last_time_s || common_last_time_s > qt->db.last_time_s)
2549
- qt->db.last_time_s = common_last_time_s;
2550
-
2551
- for (size_t tier = 0; tier < storage_tiers; tier++) {
2552
- qm->tiers[tier].eng = tier_retention[tier].eng;
2553
- qm->tiers[tier].db_metric_handle = tier_retention[tier].db_metric_handle;
2554
- qm->tiers[tier].db_first_time_s = tier_retention[tier].db_first_time_s;
2555
- qm->tiers[tier].db_last_time_s = tier_retention[tier].db_last_time_s;
2556
- qm->tiers[tier].db_update_every_s = tier_retention[tier].db_update_every_s;
2557
- }
2558
-
2559
- release_retention = false;
2560
-
2561
- qi->metrics.selected++;
2562
- qc->metrics.selected++;
2563
- qh->metrics.selected++;
2564
- }
2565
- else {
2566
- qi->metrics.excluded++;
2567
- qc->metrics.excluded++;
2568
- qh->metrics.excluded++;
2569
-
2570
- qd->status |= QUERY_STATUS_DIMENSION_HIDDEN;
2571
- }
2572
- }
2573
- else {
2574
- qi->metrics.excluded++;
2575
- qc->metrics.excluded++;
2576
- qh->metrics.excluded++;
2577
-
2578
- qd->status |= QUERY_STATUS_DIMENSION_NODATA;
2579
- qtl->metrics_skipped_due_to_not_matching_timeframe++;
2580
- }
2581
-
2582
- if(release_retention) {
2583
- // cleanup anything we allocated to the retention we will not use
2584
- for(size_t tier = 0; tier < storage_tiers ;tier++) {
2585
- if (tier_retention[tier].db_metric_handle)
2586
- tier_retention[tier].eng->api.metric_release(tier_retention[tier].db_metric_handle);
2587
- }
2588
- }
2589
-}
2590
-
2591
-static void query_target_add_dimension(QUERY_TARGET_LOCALS *qtl, QUERY_HOST *qh, QUERY_CONTEXT *qc, QUERY_INSTANCE *qi,
2592
- RRDMETRIC_ACQUIRED *rma, bool queryable_instance) {
2593
- QUERY_TARGET *qt = qtl->qt;
2594
-
2595
- RRDMETRIC *rm = rrdmetric_acquired_value(rma);
2596
- if(rrd_flag_is_deleted(rm))
2597
- return;
2598
-
2599
- if(qt->dimensions.used == qt->dimensions.size) {
2600
- size_t old_mem = qt->dimensions.size * sizeof(*qt->dimensions.array);
2601
- qt->dimensions.size = (qt->dimensions.size) ? qt->dimensions.size * 2 : 1;
2602
- size_t new_mem = qt->dimensions.size * sizeof(*qt->dimensions.array);
2603
- qt->dimensions.array = reallocz(qt->dimensions.array, new_mem);
2604
-
2605
- __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
2606
- }
2607
- QUERY_DIMENSION *qd = &qt->dimensions.array[qt->dimensions.used];
2608
- memset(qd, 0, sizeof(*qd));
2609
-
2610
- qd->slot = qt->dimensions.used++;
2611
- qd->rma = rrdmetric_acquired_dup(rma);
2612
- qd->status = QUERY_STATUS_NONE;
2613
-
2614
- if(!queryable_instance) {
2615
- qi->metrics.excluded++;
2616
- qc->metrics.excluded++;
2617
- qh->metrics.excluded++;
2618
- qd->status |= QUERY_STATUS_EXCLUDED;
2619
- return;
2620
- }
2621
-
2622
- query_target_add_metric(qtl, qh, qc, qi, qd);
2623
-}
2624
-
2625
-static inline STRING *rrdinstance_id_fqdn_v1(RRDINSTANCE_ACQUIRED *ria) {
2626
- if(unlikely(!ria))
2627
- return NULL;
2628
-
2629
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
2630
- return string_dup(ri->id);
2631
-}
2632
-
2633
-static inline STRING *rrdinstance_name_fqdn_v1(RRDINSTANCE_ACQUIRED *ria) {
2634
- if(unlikely(!ria))
2635
- return NULL;
2636
-
2637
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
2638
- return string_dup(ri->name);
2639
-}
2640
-
2641
-static inline STRING *rrdinstance_id_fqdn_v2(RRDINSTANCE_ACQUIRED *ria) {
2642
- if(unlikely(!ria))
2643
- return NULL;
2644
-
2645
- char buffer[RRD_ID_LENGTH_MAX + 1];
2646
-
2647
- RRDHOST *host = rrdinstance_acquired_rrdhost(ria);
2648
- snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s@%s", rrdinstance_acquired_id(ria), host->machine_guid);
2649
- return string_strdupz(buffer);
2650
-}
2651
-
2652
-static inline STRING *rrdinstance_name_fqdn_v2(RRDINSTANCE_ACQUIRED *ria) {
2653
- if(unlikely(!ria))
2654
- return NULL;
2655
-
2656
- char buffer[RRD_ID_LENGTH_MAX + 1];
2657
-
2658
- RRDHOST *host = rrdinstance_acquired_rrdhost(ria);
2659
- snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s@%s", rrdinstance_acquired_name(ria), rrdhost_hostname(host));
2660
- return string_strdupz(buffer);
2661
-}
2662
-
2663
-RRDSET *rrdinstance_acquired_rrdset(RRDINSTANCE_ACQUIRED *ria) {
2664
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
2665
- return ri->rrdset;
2666
-}
2667
-
2668
-const char *rrdcontext_acquired_units(RRDCONTEXT_ACQUIRED *rca) {
2669
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
2670
- return string2str(rc->units);
2671
-}
2672
-
2673
-RRDSET_TYPE rrdcontext_acquired_chart_type(RRDCONTEXT_ACQUIRED *rca) {
2674
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
2675
- return rc->chart_type;
2676
-}
2677
-
2678
-const char *rrdcontext_acquired_title(RRDCONTEXT_ACQUIRED *rca) {
2679
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
2680
- return string2str(rc->title);
2681
-}
2682
-
2683
-static void query_target_eval_instance_rrdcalc(QUERY_TARGET_LOCALS *qtl __maybe_unused,
2684
- QUERY_HOST *qh, QUERY_CONTEXT *qc, QUERY_INSTANCE *qi) {
2685
- RRDSET *st = rrdinstance_acquired_rrdset(qi->ria);
2686
- if (st) {
2687
- netdata_rwlock_rdlock(&st->alerts.rwlock);
2688
- if (st->alerts.base) {
2689
- for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) {
2690
- switch(rc->status) {
2691
- case RRDCALC_STATUS_CLEAR:
2692
- qi->alerts.clear++;
2693
- qc->alerts.clear++;
2694
- qh->alerts.clear++;
2695
- break;
2696
-
2697
- case RRDCALC_STATUS_WARNING:
2698
- qi->alerts.warning++;
2699
- qc->alerts.warning++;
2700
- qh->alerts.warning++;
2701
- break;
2702
-
2703
- case RRDCALC_STATUS_CRITICAL:
2704
- qi->alerts.critical++;
2705
- qc->alerts.critical++;
2706
- qh->alerts.critical++;
2707
- break;
2708
-
2709
- default:
2710
- case RRDCALC_STATUS_UNINITIALIZED:
2711
- case RRDCALC_STATUS_UNDEFINED:
2712
- case RRDCALC_STATUS_REMOVED:
2713
- qi->alerts.other++;
2714
- qc->alerts.other++;
2715
- qh->alerts.other++;
2716
- break;
2717
- }
2718
- }
2719
- }
2720
- netdata_rwlock_unlock(&st->alerts.rwlock);
2721
- }
2722
-}
2723
-
2724
-static bool query_target_match_alert_pattern(QUERY_INSTANCE *qi, SIMPLE_PATTERN *pattern) {
2725
- if(!pattern)
2726
- return true;
2727
-
2728
- RRDSET *st = rrdinstance_acquired_rrdset(qi->ria);
2729
- if (!st)
2730
- return false;
2731
-
2732
- BUFFER *wb = NULL;
2733
- bool matched = false;
2734
- netdata_rwlock_rdlock(&st->alerts.rwlock);
2735
- if (st->alerts.base) {
2736
- for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) {
2737
- if(simple_pattern_matches(pattern, string2str(rc->name))) {
2738
- matched = true;
2739
- break;
2740
- }
2741
-
2742
- if(!wb)
2743
- wb = buffer_create(0, NULL);
2744
- else
2745
- buffer_flush(wb);
2746
-
2747
- buffer_fast_strcat(wb, string2str(rc->name), string_strlen(rc->name));
2748
- buffer_fast_strcat(wb, ":", 1);
2749
- buffer_strcat(wb, rrdcalc_status2string(rc->status));
2750
-
2751
- if(simple_pattern_matches(pattern, buffer_tostring(wb))) {
2752
- matched = true;
2753
- break;
2754
- }
2755
- }
2756
- }
2757
- netdata_rwlock_unlock(&st->alerts.rwlock);
2758
-
2759
- buffer_free(wb);
2760
- return matched;
2761
-}
2762
-
2763
-static void query_target_add_instance(QUERY_TARGET_LOCALS *qtl, QUERY_HOST *qh, QUERY_CONTEXT *qc,
2764
- RRDINSTANCE_ACQUIRED *ria, bool queryable_instance, bool filter_instances) {
2765
- QUERY_TARGET *qt = qtl->qt;
2766
-
2767
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
2768
- if(rrd_flag_is_deleted(ri))
2769
- return;
2770
-
2771
- if(qt->instances.used == qt->instances.size) {
2772
- size_t old_mem = qt->instances.size * sizeof(*qt->instances.array);
2773
- qt->instances.size = (qt->instances.size) ? qt->instances.size * 2 : 1;
2774
- size_t new_mem = qt->instances.size * sizeof(*qt->instances.array);
2775
- qt->instances.array = reallocz(qt->instances.array, new_mem);
2776
-
2777
- __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
2778
- }
2779
- QUERY_INSTANCE *qi = &qt->instances.array[qt->instances.used];
2780
- memset(qi, 0, sizeof(*qi));
2781
-
2782
- qi->slot = qt->instances.used;
2783
- qt->instances.used++;
2784
- qi->ria = rrdinstance_acquired_dup(ria);
2785
- qi->query_host_id = qh->slot;
2786
-
2787
- if(qt->request.version <= 1) {
2788
- qi->id_fqdn = rrdinstance_id_fqdn_v1(ria);
2789
- qi->name_fqdn = rrdinstance_name_fqdn_v1(ria);
2790
- }
2791
- else {
2792
- qi->id_fqdn = rrdinstance_id_fqdn_v2(ria);
2793
- qi->name_fqdn = rrdinstance_name_fqdn_v2(ria);
2794
- }
2795
-
2796
- if(qt->db.minimum_latest_update_every_s == 0 || ri->update_every_s < qt->db.minimum_latest_update_every_s)
2797
- qt->db.minimum_latest_update_every_s = ri->update_every_s;
2798
-
2799
- if(queryable_instance && filter_instances) {
2800
- queryable_instance = false;
2801
- if(!qt->instances.pattern
2802
- || (qtl->match_ids && simple_pattern_matches(qt->instances.pattern, string2str(ri->id)))
2803
- || (qtl->match_ids && simple_pattern_matches(qt->instances.pattern, string2str(qi->id_fqdn)))
2804
- || (qtl->match_names && simple_pattern_matches(qt->instances.pattern, string2str(ri->name)))
2805
- || (qtl->match_names && simple_pattern_matches(qt->instances.pattern, string2str(qi->name_fqdn)))
2806
- )
2807
- queryable_instance = true;
2808
- }
2809
-
2810
- if(queryable_instance) {
2811
- if ((qt->instances.chart_label_key_pattern && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, qt->instances.chart_label_key_pattern, ':')) ||
2812
- (qt->instances.labels_pattern && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, qt->instances.labels_pattern, ':')))
2813
- queryable_instance = false;
2814
- }
2815
-
2816
- if(queryable_instance) {
2817
- if(qt->instances.alerts_pattern && !query_target_match_alert_pattern(qi, qt->instances.alerts_pattern))
2818
- queryable_instance = false;
2819
- }
2820
-
2821
- if(queryable_instance && qt->request.version >= 2)
2822
- query_target_eval_instance_rrdcalc(qtl, qh, qc, qi);
2823
-
2824
- size_t added = 0;
2825
-
2826
- if(unlikely(qt->request.rma)) {
2827
- query_target_add_dimension(qtl, qh, qc, qi, qt->request.rma, queryable_instance);
2828
- added++;
2829
- }
2830
- else {
2831
- RRDMETRIC *rm;
2832
- dfe_start_read(ri->rrdmetrics, rm) {
2833
- query_target_add_dimension(qtl, qh, qc, qi, (RRDMETRIC_ACQUIRED *) rm_dfe.item, queryable_instance);
2834
- added++;
2835
- }
2836
- dfe_done(rm);
2837
- }
2838
-
2839
- if(!added) {
2840
- qc->instances.excluded++;
2841
- qh->instances.excluded++;
2842
-
2843
- qt->instances.used--;
2844
- rrdinstance_release(ria);
2845
- qi->ria = NULL;
2846
-
2847
- string_freez(qi->id_fqdn);
2848
- qi->id_fqdn = NULL;
2849
-
2850
- string_freez(qi->name_fqdn);
2851
- qi->name_fqdn = NULL;
2852
- }
2853
- else {
2854
- qc->instances.selected++;
2855
- qh->instances.selected++;
2856
- }
2857
-}
2858
-
2859
-static void query_target_add_context(QUERY_TARGET_LOCALS *qtl, QUERY_HOST *qh, RRDCONTEXT_ACQUIRED *rca, bool queryable_context) {
2860
- QUERY_TARGET *qt = qtl->qt;
2861
-
2862
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
2863
- if(rrd_flag_is_deleted(rc))
2864
- return;
2865
-
2866
- if(qt->contexts.used == qt->contexts.size) {
2867
- size_t old_mem = qt->contexts.size * sizeof(*qt->contexts.array);
2868
- qt->contexts.size = (qt->contexts.size) ? qt->contexts.size * 2 : 1;
2869
- size_t new_mem = qt->contexts.size * sizeof(*qt->contexts.array);
2870
- qt->contexts.array = reallocz(qt->contexts.array, new_mem);
2871
-
2872
- __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
2873
- }
2874
- QUERY_CONTEXT *qc = &qt->contexts.array[qt->contexts.used];
2875
- memset(qc, 0, sizeof(*qc));
2876
- qc->slot = qt->contexts.used++;
2877
- qc->rca = rrdcontext_acquired_dup(rca);
2878
-
2879
- size_t added = 0;
2880
- if(unlikely(qt->request.ria)) {
2881
- query_target_add_instance(qtl, qh, qc, qt->request.ria, queryable_context, false);
2882
- added++;
2883
- }
2884
- else if(unlikely(qtl->st && qtl->st->rrdcontext == rca && qtl->st->rrdinstance)) {
2885
- query_target_add_instance(qtl, qh, qc, qtl->st->rrdinstance, queryable_context, false);
2886
- added++;
2887
- }
2888
- else {
2889
- RRDINSTANCE *ri;
2890
- dfe_start_read(rc->rrdinstances, ri) {
2891
- query_target_add_instance(qtl, qh, qc, (RRDINSTANCE_ACQUIRED *)ri_dfe.item, queryable_context, true);
2892
- added++;
2893
- }
2894
- dfe_done(ri);
2895
- }
2896
-
2897
- if(!added) {
2898
- qt->contexts.used--;
2899
- rrdcontext_release(rca);
2900
- }
2901
-}
2902
-
2903
-static void query_target_add_host(QUERY_TARGET_LOCALS *qtl, RRDHOST *host, bool queryable_host) {
2904
- QUERY_TARGET *qt = qtl->qt;
2905
-
2906
- if(qt->hosts.used == qt->hosts.size) {
2907
- size_t old_mem = qt->hosts.size * sizeof(*qt->hosts.array);
2908
- qt->hosts.size = (qt->hosts.size) ? qt->hosts.size * 2 : 1;
2909
- size_t new_mem = qt->hosts.size * sizeof(*qt->hosts.array);
2910
- qt->hosts.array = reallocz(qt->hosts.array, new_mem);
2911
-
2912
- __atomic_add_fetch(&netdata_buffers_statistics.query_targets_size, new_mem - old_mem, __ATOMIC_RELAXED);
2913
- }
2914
- QUERY_HOST *qh = &qt->hosts.array[qt->hosts.used];
2915
- memset(qh, 0, sizeof(*qh));
2916
- qh->slot = qt->hosts.used++;
2917
- qh->host = host;
2918
-
2919
- if(host->node_id)
2920
- uuid_unparse_lower(*host->node_id, qh->node_id);
2921
- else
2922
- qh->node_id[0] = '\0';
2923
-
2924
- // is the chart given valid?
2925
- if(unlikely(qtl->st && (!qtl->st->rrdinstance || !qtl->st->rrdcontext))) {
2926
- error("QUERY TARGET: RRDSET '%s' given, but it is not linked to rrdcontext structures. Linking it now.", rrdset_name(qtl->st));
2927
- rrdinstance_from_rrdset(qtl->st);
2928
-
2929
- if(unlikely(qtl->st && (!qtl->st->rrdinstance || !qtl->st->rrdcontext))) {
2930
- error("QUERY TARGET: RRDSET '%s' given, but failed to be linked to rrdcontext structures. Switching to context query.",
2931
- rrdset_name(qtl->st));
2932
-
2933
- if (!is_valid_sp(qtl->charts))
2934
- qtl->charts = rrdset_name(qtl->st);
2935
-
2936
- qtl->st = NULL;
2937
- }
2938
- }
2939
-
2940
- size_t added = 0;
2941
- if(unlikely(qt->request.rca)) {
2942
- query_target_add_context(qtl, qh, qt->request.rca, true);
2943
- added++;
2944
- }
2945
- else if(unlikely(qtl->st)) {
2946
- // single chart data queries
2947
- query_target_add_context(qtl, qh, qtl->st->rrdcontext, true);
2948
- added++;
2949
- }
2950
- else {
2951
- // context pattern queries
2952
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)host->rrdctx, qtl->scope_contexts);
2953
- if(likely(rca)) {
2954
- // we found it!
2955
-
2956
- bool queryable_context = queryable_host;
2957
- if(queryable_context && qt->contexts.pattern && !simple_pattern_matches(qt->contexts.pattern, rrdcontext_acquired_id(rca)))
2958
- queryable_context = false;
2959
-
2960
- query_target_add_context(qtl, qh, rca, queryable_context);
2961
- rrdcontext_release(rca);
2962
- added++;
2963
- }
2964
- else {
2965
- // Probably it is a pattern, we need to search for it...
2966
- RRDCONTEXT *rc;
2967
- dfe_start_read((DICTIONARY *)host->rrdctx, rc) {
2968
- if(qt->contexts.scope_pattern && !simple_pattern_matches(qt->contexts.scope_pattern, string2str(rc->id)))
2969
- continue;
2970
-
2971
- bool queryable_context = queryable_host;
2972
- if(queryable_context && qt->contexts.pattern && !simple_pattern_matches(qt->contexts.pattern, string2str(rc->id)))
2973
- queryable_context = false;
2974
-
2975
- query_target_add_context(qtl, qh, (RRDCONTEXT_ACQUIRED *)rc_dfe.item, queryable_context);
2976
- added++;
2977
- }
2978
- dfe_done(rc);
2979
- }
2980
- }
2981
-
2982
- if(!added) {
2983
- qt->hosts.used--;
2984
- }
2985
-}
2986
-
2987
-void query_target_generate_name(QUERY_TARGET *qt) {
2988
- char options_buffer[100 + 1];
2989
- web_client_api_request_v1_data_options_to_string(options_buffer, 100, qt->request.options);
2990
-
2991
- char resampling_buffer[20 + 1] = "";
2992
- if(qt->request.resampling_time > 1)
2993
- snprintfz(resampling_buffer, 20, "/resampling:%lld", (long long)qt->request.resampling_time);
2994
-
2995
- char tier_buffer[20 + 1] = "";
2996
- if(qt->request.options & RRDR_OPTION_SELECTED_TIER)
2997
- snprintfz(tier_buffer, 20, "/tier:%zu", qt->request.tier);
2998
-
2999
- if(qt->request.st)
3000
- snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "chart://hosts:%s/instance:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s"
3001
- , rrdhost_hostname(qt->request.st->rrdhost)
3002
- , rrdset_name(qt->request.st)
3003
- , (qt->request.dimensions) ? qt->request.dimensions : "*"
3004
- , (long long)qt->request.after
3005
- , (long long)qt->request.before
3006
- , qt->request.points
3007
- , time_grouping_tostring(qt->request.time_group_method)
3008
- , qt->request.time_group_options ? qt->request.time_group_options : ""
3009
- , options_buffer
3010
- , resampling_buffer
3011
- , tier_buffer
3012
- );
3013
- else if(qt->request.host && qt->request.rca && qt->request.ria && qt->request.rma)
3014
- snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "metric://hosts:%s/context:%s/instance:%s/dimension:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s"
3015
- , rrdhost_hostname(qt->request.host)
3016
- , rrdcontext_acquired_id(qt->request.rca)
3017
- , rrdinstance_acquired_id(qt->request.ria)
3018
- , rrdmetric_acquired_id(qt->request.rma)
3019
- , (long long)qt->request.after
3020
- , (long long)qt->request.before
3021
- , qt->request.points
3022
- , time_grouping_tostring(qt->request.time_group_method)
3023
- , qt->request.time_group_options ? qt->request.time_group_options : ""
3024
- , options_buffer
3025
- , resampling_buffer
3026
- , tier_buffer
3027
- );
3028
- else
3029
- snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "context://hosts:%s/contexts:%s/instances:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s"
3030
- , (qt->request.host) ? rrdhost_hostname(qt->request.host) : ((qt->request.hosts) ? qt->request.hosts : "*")
3031
- , (qt->request.contexts) ? qt->request.contexts : "*"
3032
- , (qt->request.charts) ? qt->request.charts : "*"
3033
- , (qt->request.dimensions) ? qt->request.dimensions : "*"
3034
- , (long long)qt->request.after
3035
- , (long long)qt->request.before
3036
- , qt->request.points
3037
- , time_grouping_tostring(qt->request.time_group_method)
3038
- , qt->request.time_group_options ? qt->request.time_group_options : ""
3039
- , options_buffer
3040
- , resampling_buffer
3041
- , tier_buffer
3042
- );
3043
-
3044
- json_fix_string(qt->id);
3045
-}
3046
-
3047
-QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr) {
3048
- if(!service_running(ABILITY_DATA_QUERIES))
3049
- return NULL;
3050
-
3051
- QUERY_TARGET *qt = &thread_query_target;
3052
-
3053
- if(qt->used)
3054
- fatal("QUERY TARGET: this query target is already used (%zu queries made with this QUERY_TARGET so far).", qt->queries);
3055
-
3056
- qt->used = true;
3057
- qt->queries++;
3058
-
3059
- if(!qtr->received_ut)
3060
- qtr->received_ut = now_monotonic_usec();
3061
-
3062
- qt->timings.received_ut = qtr->received_ut;
3063
-
3064
- if(qtr->hosts && !qtr->scope_hosts)
3065
- qtr->scope_hosts = qtr->hosts;
3066
-
3067
- if(qtr->contexts && !qtr->scope_contexts)
3068
- qtr->scope_contexts = qtr->contexts;
3069
-
3070
- memset(&qt->query_stats, 0, sizeof(qt->query_stats));
3071
-
3072
- // copy the request into query_thread_target
3073
- qt->request = *qtr;
3074
-
3075
- query_target_generate_name(qt);
3076
- qt->window.after = qt->request.after;
3077
- qt->window.before = qt->request.before;
3078
- rrdr_relative_window_to_absolute(&qt->window.after, &qt->window.before);
3079
-
3080
- // prepare our local variables - we need these across all these functions
3081
- QUERY_TARGET_LOCALS qtl = {
3082
- .qt = qt,
3083
- .start_s = now_realtime_sec(),
3084
- .st = qt->request.st,
3085
- .scope_hosts = qt->request.scope_hosts,
3086
- .scope_contexts = qt->request.scope_contexts,
3087
- .hosts = qt->request.hosts,
3088
- .contexts = qt->request.contexts,
3089
- .charts = qt->request.charts,
3090
- .dimensions = qt->request.dimensions,
3091
- .chart_label_key = qt->request.chart_label_key,
3092
- .labels = qt->request.labels,
3093
- .alerts = qt->request.alerts,
3094
- };
3095
-
3096
- RRDHOST *host = qt->request.host;
3097
-
3098
- qt->db.minimum_latest_update_every_s = 0; // it will be updated by query_target_add_query()
3099
-
3100
- // prepare all the patterns
3101
- qt->hosts.pattern = is_valid_sp(qtl.hosts) ? simple_pattern_create(qtl.hosts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3102
- qt->hosts.scope_pattern = is_valid_sp(qtl.scope_hosts) ? simple_pattern_create(qtl.scope_hosts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3103
-
3104
- qt->contexts.pattern = is_valid_sp(qtl.contexts) ? simple_pattern_create(qtl.contexts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3105
- qt->contexts.scope_pattern = is_valid_sp(qtl.scope_contexts) ? simple_pattern_create(qtl.scope_contexts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3106
-
3107
- qt->instances.pattern = is_valid_sp(qtl.charts) ? simple_pattern_create(qtl.charts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3108
- qt->query.pattern = is_valid_sp(qtl.dimensions) ? simple_pattern_create(qtl.dimensions, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3109
- qt->instances.chart_label_key_pattern = is_valid_sp(qtl.chart_label_key) ? simple_pattern_create(qtl.chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3110
- qt->instances.labels_pattern = is_valid_sp(qtl.labels) ? simple_pattern_create(qtl.labels, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3111
- qt->instances.alerts_pattern = is_valid_sp(qtl.alerts) ? simple_pattern_create(qtl.alerts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL;
3112
-
3113
- qtl.match_ids = qt->request.options & RRDR_OPTION_MATCH_IDS;
3114
- qtl.match_names = qt->request.options & RRDR_OPTION_MATCH_NAMES;
3115
- if(likely(!qtl.match_ids && !qtl.match_names))
3116
- qtl.match_ids = qtl.match_names = true;
3117
-
3118
- // verify that the chart belongs to the host we are interested
3119
- if(qtl.st) {
3120
- if (!host) {
3121
- // It is NULL, set it ourselves.
3122
- host = qtl.st->rrdhost;
3123
- }
3124
- else if (unlikely(host != qtl.st->rrdhost)) {
3125
- // Oops! A different host!
3126
- error("QUERY TARGET: RRDSET '%s' given does not belong to host '%s'. Switching query host to '%s'",
3127
- rrdset_name(qtl.st), rrdhost_hostname(host), rrdhost_hostname(qtl.st->rrdhost));
3128
- host = qtl.st->rrdhost;
3129
- }
3130
- }
3131
-
3132
- if(host) {
3133
- // single host query
3134
- query_target_add_host(&qtl, host, true);
3135
- qtl.hosts = rrdhost_hostname(host);
3136
- }
3137
- else {
3138
- char uuid[UUID_STR_LEN];
3139
-
3140
- // multi host query
3141
- rrd_rdlock();
3142
- rrdhost_foreach_read(host) {
3143
-
3144
- if(!host->node_id)
3145
- uuid_unparse_lower(*host->node_id, uuid);
3146
- else
3147
- uuid[0] = '\0';
3148
-
3149
- if(!qt->hosts.scope_pattern ||
3150
- simple_pattern_matches(qt->hosts.scope_pattern, rrdhost_hostname(host)) ||
3151
- simple_pattern_matches(qt->hosts.scope_pattern, host->machine_guid) ||
3152
- (*uuid && simple_pattern_matches(qt->hosts.scope_pattern, uuid))) {
3153
-
3154
- bool queryable_host = false;
3155
- if(!qt->hosts.pattern ||
3156
- simple_pattern_matches(qt->hosts.pattern, rrdhost_hostname(host)) ||
3157
- simple_pattern_matches(qt->hosts.pattern, host->machine_guid) ||
3158
- simple_pattern_matches(qt->hosts.pattern, uuid))
3159
- queryable_host = true;
3160
-
3161
- query_target_add_host(&qtl, host, queryable_host);
3162
- }
3163
- }
3164
- rrd_unlock();
3165
- }
3166
-
3167
- query_target_calculate_window(qt);
3168
-
3169
- qt->timings.preprocessed_ut = now_monotonic_usec();
3170
-
3171
- return qt;
3172
-}
3173
-
3174
-
3175
-// ----------------------------------------------------------------------------
3176
-// load from SQL
3177
-
3178
-static void rrdinstance_load_clabel(SQL_CLABEL_DATA *sld, void *data) {
3179
- RRDINSTANCE *ri = data;
3180
- rrdlabels_add(ri->rrdlabels, sld->label_key, sld->label_value, sld->label_source);
3181
-}
3182
-
3183
-static void rrdinstance_load_dimension(SQL_DIMENSION_DATA *sd, void *data) {
3184
- RRDINSTANCE *ri = data;
3185
-
3186
- RRDMETRIC trm = {
3187
- .id = string_strdupz(sd->id),
3188
- .name = string_strdupz(sd->name),
3189
- .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomic
3190
- };
3191
- if(sd->hidden) trm.flags |= RRD_FLAG_HIDDEN;
3192
-
3193
- uuid_copy(trm.uuid, sd->dim_id);
3194
-
3195
- dictionary_set(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm));
3196
-}
3197
-
3198
-static void rrdinstance_load_chart_callback(SQL_CHART_DATA *sc, void *data) {
3199
- RRDHOST *host = data;
3200
-
3201
- RRDCONTEXT tc = {
3202
- .id = string_strdupz(sc->context),
3203
- .title = string_strdupz(sc->title),
3204
- .units = string_strdupz(sc->units),
3205
- .family = string_strdupz(sc->family),
3206
- .priority = sc->priority,
3207
- .chart_type = sc->chart_type,
3208
- .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics
3209
- .rrdhost = host,
3210
- };
3211
-
3212
- RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_set_and_acquire_item((DICTIONARY *)host->rrdctx, string2str(tc.id), &tc, sizeof(tc));
3213
- RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
3214
-
3215
- RRDINSTANCE tri = {
3216
- .id = string_strdupz(sc->id),
3217
- .name = string_strdupz(sc->name),
3218
- .title = string_strdupz(sc->title),
3219
- .units = string_strdupz(sc->units),
3220
- .family = string_strdupz(sc->family),
3221
- .chart_type = sc->chart_type,
3222
- .priority = sc->priority,
3223
- .update_every_s = sc->update_every,
3224
- .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics
3225
- };
3226
- uuid_copy(tri.uuid, sc->chart_id);
3227
-
3228
- RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, sc->id, &tri, sizeof(tri));
3229
- RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
3230
-
3231
- ctx_get_dimension_list(&ri->uuid, rrdinstance_load_dimension, ri);
3232
- ctx_get_label_list(&ri->uuid, rrdinstance_load_clabel, ri);
3233
- rrdinstance_trigger_updates(ri, __FUNCTION__ );
3234
- rrdinstance_release(ria);
3235
- rrdcontext_release(rca);
3236
-}
3237
-
3238
-static void rrdcontext_load_context_callback(VERSIONED_CONTEXT_DATA *ctx_data, void *data) {
3239
- RRDHOST *host = data;
3240
- (void)host;
3241
-
3242
- RRDCONTEXT trc = {
3243
- .id = string_strdupz(ctx_data->id),
3244
- .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics
3245
-
3246
- // no need to set more data here
3247
- // we only need the hub data
3248
-
3249
- .hub = *ctx_data,
3250
- };
3251
- dictionary_set((DICTIONARY *)host->rrdctx, string2str(trc.id), &trc, sizeof(trc));
3252
-}
3253
-
3254
-void rrdhost_load_rrdcontext_data(RRDHOST *host) {
3255
- if(host->rrdctx) return;
3256
-
3257
- rrdhost_create_rrdcontexts(host);
3258
- ctx_get_context_list(&host->host_uuid, rrdcontext_load_context_callback, host);
3259
- ctx_get_chart_list(&host->host_uuid, rrdinstance_load_chart_callback, host);
3260
-
3261
- RRDCONTEXT *rc;
3262
- dfe_start_read((DICTIONARY *)host->rrdctx, rc) {
3263
- rrdcontext_trigger_updates(rc, __FUNCTION__ );
3264
- }
3265
- dfe_done(rc);
3266
-
3267
- rrdcontext_garbage_collect_single_host(host, false);
3268
-}
3269
-
3270
-// ----------------------------------------------------------------------------
3271
-// version hash calculation
3272
-
3273
-static uint64_t rrdcontext_version_hash_with_callback(
3274
- RRDHOST *host,
3275
- void (*callback)(RRDCONTEXT *, bool, void *),
3276
- bool snapshot,
3277
- void *bundle) {
3278
-
3279
- if(unlikely(!host || !host->rrdctx)) return 0;
3280
-
3281
- RRDCONTEXT *rc;
3282
- uint64_t hash = 0;
3283
-
3284
- // loop through all contexts of the host
3285
- dfe_start_read((DICTIONARY *)host->rrdctx, rc) {
3286
-
3287
- rrdcontext_lock(rc);
3288
-
3289
- if(unlikely(rrd_flag_check(rc, RRD_FLAG_HIDDEN))) {
3290
- rrdcontext_unlock(rc);
3291
- continue;
3292
- }
3293
-
3294
- if(unlikely(callback))
3295
- callback(rc, snapshot, bundle);
3296
-
3297
- // skip any deleted contexts
3298
- if(unlikely(rrd_flag_is_deleted(rc))) {
3299
- rrdcontext_unlock(rc);
3300
- continue;
3301
- }
3302
-
3303
- // we use rc->hub.* which has the latest
3304
- // metadata we have sent to the hub
3305
-
3306
- // if a context is currently queued, rc->hub.* does NOT
3307
- // reflect the queued changes. rc->hub.* is updated with
3308
- // their metadata, after messages are dispatched to hub.
3309
-
3310
- // when the context is being collected,
3311
- // rc->hub.last_time_t is already zero
3312
-
3313
- hash += rc->hub.version + rc->hub.last_time_s - rc->hub.first_time_s;
3314
-
3315
- rrdcontext_unlock(rc);
3316
-
3317
- }
3318
- dfe_done(rc);
3319
-
3320
- return hash;
3321
-}
3322
-
3323
-// ----------------------------------------------------------------------------
3324
-// retention recalculation
3325
-
3326
-static void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, bool worker_jobs) {
3327
- rrdcontext_post_process_updates(rc, true, reason, worker_jobs);
3328
-}
3329
-
3330
-static void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, bool worker_jobs) {
3331
- if(unlikely(!host || !host->rrdctx)) return;
3332
-
3333
- RRDCONTEXT *rc;
3334
- dfe_start_read((DICTIONARY *)host->rrdctx, rc) {
3335
- rrdcontext_recalculate_context_retention(rc, reason, worker_jobs);
3336
- }
3337
- dfe_done(rc);
3338
-}
3339
-
3340
-static void rrdcontext_recalculate_retention_all_hosts(void) {
3341
- rrdcontext_next_db_rotation_ut = 0;
3342
- rrd_rdlock();
3343
- RRDHOST *host;
3344
- rrdhost_foreach_read(host) {
3345
- worker_is_busy(WORKER_JOB_RETENTION);
3346
- rrdcontext_recalculate_host_retention(host, RRD_FLAG_UPDATE_REASON_DB_ROTATION, true);
3347
- }
3348
- rrd_unlock();
3349
-}
3350
-
3351
-// ----------------------------------------------------------------------------
3352
-// garbage collector
3353
-
3354
-static bool rrdmetric_update_retention(RRDMETRIC *rm) {
3355
- time_t min_first_time_t = LONG_MAX, max_last_time_t = 0;
3356
-
3357
- if(rm->rrddim) {
3358
- min_first_time_t = rrddim_first_entry_s(rm->rrddim);
3359
- max_last_time_t = rrddim_last_entry_s(rm->rrddim);
3360
- }
3361
- else {
3362
- RRDHOST *rrdhost = rm->ri->rc->rrdhost;
3363
- for (size_t tier = 0; tier < storage_tiers; tier++) {
3364
- STORAGE_ENGINE *eng = rrdhost->db[tier].eng;
3365
-
3366
- time_t first_time_t, last_time_t;
3367
- if (eng->api.metric_retention_by_uuid(rrdhost->db[tier].instance, &rm->uuid, &first_time_t, &last_time_t)) {
3368
- if (first_time_t < min_first_time_t)
3369
- min_first_time_t = first_time_t;
3370
-
3371
- if (last_time_t > max_last_time_t)
3372
- max_last_time_t = last_time_t;
3373
- }
3374
- }
3375
- }
3376
-
3377
- if((min_first_time_t == LONG_MAX || min_first_time_t == 0) && max_last_time_t == 0)
3378
- return false;
3379
-
3380
- if(min_first_time_t == LONG_MAX)
3381
- min_first_time_t = 0;
3382
-
3383
- if(min_first_time_t > max_last_time_t) {
3384
- internal_error(true, "RRDMETRIC: retention of '%s' is flipped, first_time_t = %ld, last_time_t = %ld", string2str(rm->id), min_first_time_t, max_last_time_t);
3385
- time_t tmp = min_first_time_t;
3386
- min_first_time_t = max_last_time_t;
3387
- max_last_time_t = tmp;
3388
- }
3389
-
3390
- // check if retention changed
3391
-
3392
- if (min_first_time_t != rm->first_time_s) {
3393
- rm->first_time_s = min_first_time_t;
3394
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3395
- }
3396
-
3397
- if (max_last_time_t != rm->last_time_s) {
3398
- rm->last_time_s = max_last_time_t;
3399
- rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3400
- }
3401
-
3402
- if(unlikely(!rm->first_time_s && !rm->last_time_s))
3403
- rrd_flag_set_deleted(rm, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3404
-
3405
- rrd_flag_set(rm, RRD_FLAG_LIVE_RETENTION);
3406
-
3407
- return true;
3408
-}
3409
-
3410
-static inline bool rrdmetric_should_be_deleted(RRDMETRIC *rm) {
3411
- if(likely(!rrd_flag_check(rm, RRD_FLAGS_REQUIRED_FOR_DELETIONS)))
3412
- return false;
3413
-
3414
- if(likely(rrd_flag_check(rm, RRD_FLAGS_PREVENTING_DELETIONS)))
3415
- return false;
3416
-
3417
- if(likely(rm->rrddim))
3418
- return false;
3419
-
3420
- rrdmetric_update_retention(rm);
3421
- if(rm->first_time_s || rm->last_time_s)
3422
- return false;
3423
-
3424
- return true;
3425
-}
3426
-
3427
-static inline bool rrdinstance_should_be_deleted(RRDINSTANCE *ri) {
3428
- if(likely(!rrd_flag_check(ri, RRD_FLAGS_REQUIRED_FOR_DELETIONS)))
3429
- return false;
3430
-
3431
- if(likely(rrd_flag_check(ri, RRD_FLAGS_PREVENTING_DELETIONS)))
3432
- return false;
3433
-
3434
- if(likely(ri->rrdset))
3435
- return false;
3436
-
3437
- if(unlikely(dictionary_referenced_items(ri->rrdmetrics) != 0))
3438
- return false;
3439
-
3440
- if(unlikely(dictionary_entries(ri->rrdmetrics) != 0))
3441
- return false;
3442
-
3443
- if(ri->first_time_s || ri->last_time_s)
3444
- return false;
3445
-
3446
- return true;
3447
-}
3448
-
3449
-static inline bool rrdcontext_should_be_deleted(RRDCONTEXT *rc) {
3450
- if(likely(!rrd_flag_check(rc, RRD_FLAGS_REQUIRED_FOR_DELETIONS)))
3451
- return false;
3452
-
3453
- if(likely(rrd_flag_check(rc, RRD_FLAGS_PREVENTING_DELETIONS)))
3454
- return false;
3455
-
3456
- if(unlikely(dictionary_referenced_items(rc->rrdinstances) != 0))
3457
- return false;
3458
-
3459
- if(unlikely(dictionary_entries(rc->rrdinstances) != 0))
3460
- return false;
3461
-
3462
- if(unlikely(rc->first_time_s || rc->last_time_s))
3463
- return false;
3464
-
3465
- return true;
3466
-}
3467
-
3468
-void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc) {
3469
- // we need to refresh the string pointers in rc->hub
3470
- // in case the context changed values
3471
- rc->hub.id = string2str(rc->id);
3472
- rc->hub.title = string2str(rc->title);
3473
- rc->hub.units = string2str(rc->units);
3474
- rc->hub.family = string2str(rc->family);
3475
-
3476
- // delete it from SQL
3477
- if(ctx_delete_context(&rc->rrdhost->host_uuid, &rc->hub) != 0)
3478
- error("RRDCONTEXT: failed to delete context '%s' version %"PRIu64" from SQL.", rc->hub.id, rc->hub.version);
3479
-}
3480
-
3481
-static void rrdcontext_garbage_collect_single_host(RRDHOST *host, bool worker_jobs) {
3482
-
3483
- internal_error(true, "RRDCONTEXT: garbage collecting context structures of host '%s'", rrdhost_hostname(host));
3484
-
3485
- RRDCONTEXT *rc;
3486
- dfe_start_reentrant((DICTIONARY *)host->rrdctx, rc) {
3487
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
3488
-
3489
- if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP);
3490
-
3491
- rrdcontext_lock(rc);
3492
-
3493
- RRDINSTANCE *ri;
3494
- dfe_start_reentrant(rc->rrdinstances, ri) {
3495
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
3496
-
3497
- RRDMETRIC *rm;
3498
- dfe_start_write(ri->rrdmetrics, rm) {
3499
- if(rrdmetric_should_be_deleted(rm)) {
3500
- if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP_DELETE);
3501
- if(!dictionary_del(ri->rrdmetrics, string2str(rm->id)))
3502
- error("RRDCONTEXT: metric '%s' of instance '%s' of context '%s' of host '%s', failed to be deleted from rrdmetrics dictionary.",
3503
- string2str(rm->id),
3504
- string2str(ri->id),
3505
- string2str(rc->id),
3506
- rrdhost_hostname(host));
3507
- else
3508
- internal_error(
3509
- true,
3510
- "RRDCONTEXT: metric '%s' of instance '%s' of context '%s' of host '%s', deleted from rrdmetrics dictionary.",
3511
- string2str(rm->id),
3512
- string2str(ri->id),
3513
- string2str(rc->id),
3514
- rrdhost_hostname(host));
3515
- }
3516
- }
3517
- dfe_done(rm);
3518
-
3519
- if(rrdinstance_should_be_deleted(ri)) {
3520
- if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP_DELETE);
3521
- if(!dictionary_del(rc->rrdinstances, string2str(ri->id)))
3522
- error("RRDCONTEXT: instance '%s' of context '%s' of host '%s', failed to be deleted from rrdmetrics dictionary.",
3523
- string2str(ri->id),
3524
- string2str(rc->id),
3525
- rrdhost_hostname(host));
3526
- else
3527
- internal_error(
3528
- true,
3529
- "RRDCONTEXT: instance '%s' of context '%s' of host '%s', deleted from rrdmetrics dictionary.",
3530
- string2str(ri->id),
3531
- string2str(rc->id),
3532
- rrdhost_hostname(host));
3533
- }
3534
- }
3535
- dfe_done(ri);
3536
-
3537
- if(unlikely(rrdcontext_should_be_deleted(rc))) {
3538
- if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP_DELETE);
3539
- rrdcontext_dequeue_from_post_processing(rc);
3540
- rrdcontext_delete_from_sql_unsafe(rc);
3541
-
3542
- if(!dictionary_del((DICTIONARY *)host->rrdctx, string2str(rc->id)))
3543
- error("RRDCONTEXT: context '%s' of host '%s', failed to be deleted from rrdmetrics dictionary.",
3544
- string2str(rc->id),
3545
- rrdhost_hostname(host));
3546
- else
3547
- internal_error(
3548
- true,
3549
- "RRDCONTEXT: context '%s' of host '%s', deleted from rrdmetrics dictionary.",
3550
- string2str(rc->id),
3551
- rrdhost_hostname(host));
3552
- }
3553
-
3554
- // the item is referenced in the dictionary
3555
- // so, it is still here to unlock, even if we have deleted it
3556
- rrdcontext_unlock(rc);
3557
- }
3558
- dfe_done(rc);
3559
-}
3560
-
3561
-static void rrdcontext_garbage_collect_for_all_hosts(void) {
3562
- rrd_rdlock();
3563
- RRDHOST *host;
3564
- rrdhost_foreach_read(host) {
3565
- rrdcontext_garbage_collect_single_host(host, true);
3566
- }
3567
- rrd_unlock();
3568
-}
3569
-
3570
-// ----------------------------------------------------------------------------
3571
-// post processing
3572
-
3573
-static void rrdmetric_process_updates(RRDMETRIC *rm, bool force, RRD_FLAGS reason, bool worker_jobs) {
3574
- if(reason != RRD_FLAG_NONE)
3575
- rrd_flag_set_updated(rm, reason);
3576
-
3577
- if(!force && !rrd_flag_is_updated(rm) && rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION) && !rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION))
3578
- return;
3579
-
3580
- if(worker_jobs)
3581
- worker_is_busy(WORKER_JOB_PP_METRIC);
3582
-
3583
- if(reason & RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD) {
3584
- rrd_flag_set_archived(rm);
3585
- rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD);
3586
- }
3587
- if(rrd_flag_is_deleted(rm) && (reason & RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION))
3588
- rrd_flag_set_archived(rm);
3589
-
3590
- rrdmetric_update_retention(rm);
3591
-
3592
- rrd_flag_unset_updated(rm);
3593
-}
3594
-
3595
-static void rrdinstance_post_process_updates(RRDINSTANCE *ri, bool force, RRD_FLAGS reason, bool worker_jobs) {
3596
- if(reason != RRD_FLAG_NONE)
3597
- rrd_flag_set_updated(ri, reason);
3598
-
3599
- if(!force && !rrd_flag_is_updated(ri) && rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION))
3600
- return;
3601
-
3602
- if(worker_jobs)
3603
- worker_is_busy(WORKER_JOB_PP_INSTANCE);
3604
-
3605
- time_t min_first_time_t = LONG_MAX, max_last_time_t = 0;
3606
- size_t metrics_active = 0, metrics_deleted = 0;
3607
- bool live_retention = true, currently_collected = false;
3608
- if(dictionary_entries(ri->rrdmetrics) > 0) {
3609
- RRDMETRIC *rm;
3610
- dfe_start_read((DICTIONARY *)ri->rrdmetrics, rm) {
3611
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
3612
-
3613
- RRD_FLAGS reason_to_pass = reason;
3614
- if(rrd_flag_check(ri, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION))
3615
- reason_to_pass |= RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION;
3616
-
3617
- rrdmetric_process_updates(rm, force, reason_to_pass, worker_jobs);
3618
-
3619
- if(unlikely(!rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION)))
3620
- live_retention = false;
3621
-
3622
- if (unlikely((rrdmetric_should_be_deleted(rm)))) {
3623
- metrics_deleted++;
3624
- continue;
3625
- }
3626
-
3627
- if(!currently_collected && rrd_flag_check(rm, RRD_FLAG_COLLECTED) && rm->first_time_s)
3628
- currently_collected = true;
3629
-
3630
- metrics_active++;
3631
-
3632
- if (rm->first_time_s && rm->first_time_s < min_first_time_t)
3633
- min_first_time_t = rm->first_time_s;
3634
-
3635
- if (rm->last_time_s && rm->last_time_s > max_last_time_t)
3636
- max_last_time_t = rm->last_time_s;
3637
- }
3638
- dfe_done(rm);
3639
- }
3640
-
3641
- if(unlikely(live_retention && !rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION)))
3642
- rrd_flag_set(ri, RRD_FLAG_LIVE_RETENTION);
3643
- else if(unlikely(!live_retention && rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION)))
3644
- rrd_flag_clear(ri, RRD_FLAG_LIVE_RETENTION);
3645
-
3646
- if(unlikely(!metrics_active)) {
3647
- // no metrics available
3648
-
3649
- if(ri->first_time_s) {
3650
- ri->first_time_s = 0;
3651
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3652
- }
3653
-
3654
- if(ri->last_time_s) {
3655
- ri->last_time_s = 0;
3656
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3657
- }
3658
-
3659
- rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3660
- }
3661
- else {
3662
- // we have active metrics...
3663
-
3664
- if (unlikely(min_first_time_t == LONG_MAX))
3665
- min_first_time_t = 0;
3666
-
3667
- if (unlikely(min_first_time_t == 0 || max_last_time_t == 0)) {
3668
- if(ri->first_time_s) {
3669
- ri->first_time_s = 0;
3670
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3671
- }
3672
-
3673
- if(ri->last_time_s) {
3674
- ri->last_time_s = 0;
3675
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3676
- }
3677
-
3678
- if(likely(live_retention))
3679
- rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3680
- }
3681
- else {
3682
- rrd_flag_clear(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3683
-
3684
- if (unlikely(ri->first_time_s != min_first_time_t)) {
3685
- ri->first_time_s = min_first_time_t;
3686
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3687
- }
3688
-
3689
- if (unlikely(ri->last_time_s != max_last_time_t)) {
3690
- ri->last_time_s = max_last_time_t;
3691
- rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3692
- }
3693
-
3694
- if(likely(currently_collected))
3695
- rrd_flag_set_collected(ri);
3696
- else
3697
- rrd_flag_set_archived(ri);
3698
- }
3699
- }
3700
-
3701
- rrd_flag_unset_updated(ri);
3702
-}
3703
-
3704
-static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAGS reason, bool worker_jobs) {
3705
- if(reason != RRD_FLAG_NONE)
3706
- rrd_flag_set_updated(rc, reason);
3707
-
3708
- if(worker_jobs)
3709
- worker_is_busy(WORKER_JOB_PP_CONTEXT);
3710
-
3711
- size_t min_priority_collected = LONG_MAX;
3712
- size_t min_priority_not_collected = LONG_MAX;
3713
- size_t min_priority = LONG_MAX;
3714
- time_t min_first_time_t = LONG_MAX, max_last_time_t = 0;
3715
- size_t instances_active = 0, instances_deleted = 0;
3716
- bool live_retention = true, currently_collected = false, hidden = true;
3717
- if(dictionary_entries(rc->rrdinstances) > 0) {
3718
- RRDINSTANCE *ri;
3719
- dfe_start_reentrant(rc->rrdinstances, ri) {
3720
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
3721
-
3722
- RRD_FLAGS reason_to_pass = reason;
3723
- if(rrd_flag_check(rc, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION))
3724
- reason_to_pass |= RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION;
3725
-
3726
- rrdinstance_post_process_updates(ri, force, reason_to_pass, worker_jobs);
3727
-
3728
- if(unlikely(hidden && !rrd_flag_check(ri, RRD_FLAG_HIDDEN)))
3729
- hidden = false;
3730
-
3731
- if(unlikely(live_retention && !rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION)))
3732
- live_retention = false;
3733
-
3734
- if (unlikely(rrdinstance_should_be_deleted(ri))) {
3735
- instances_deleted++;
3736
- continue;
3737
- }
3738
-
3739
- if(unlikely(!currently_collected && rrd_flag_is_collected(ri) && ri->first_time_s))
3740
- currently_collected = true;
3741
-
3742
- internal_error(rc->units != ri->units,
3743
- "RRDCONTEXT: '%s' rrdinstance '%s' has different units, context '%s', instance '%s'",
3744
- string2str(rc->id), string2str(ri->id),
3745
- string2str(rc->units), string2str(ri->units));
3746
-
3747
- instances_active++;
3748
-
3749
- if (ri->priority >= RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY) {
3750
- if(rrd_flag_check(ri, RRD_FLAG_COLLECTED)) {
3751
- if(ri->priority < min_priority_collected)
3752
- min_priority_collected = ri->priority;
3753
- }
3754
- else {
3755
- if(ri->priority < min_priority_not_collected)
3756
- min_priority_not_collected = ri->priority;
3757
- }
3758
- }
3759
-
3760
- if (ri->first_time_s && ri->first_time_s < min_first_time_t)
3761
- min_first_time_t = ri->first_time_s;
3762
-
3763
- if (ri->last_time_s && ri->last_time_s > max_last_time_t)
3764
- max_last_time_t = ri->last_time_s;
3765
- }
3766
- dfe_done(ri);
3767
-
3768
- if(min_priority_collected != LONG_MAX)
3769
- // use the collected priority
3770
- min_priority = min_priority_collected;
3771
- else
3772
- // use the non-collected priority
3773
- min_priority = min_priority_not_collected;
3774
- }
3775
-
3776
- {
3777
- bool previous_hidden = rrd_flag_check(rc, RRD_FLAG_HIDDEN);
3778
- if (hidden != previous_hidden) {
3779
- if (hidden && !rrd_flag_check(rc, RRD_FLAG_HIDDEN))
3780
- rrd_flag_set(rc, RRD_FLAG_HIDDEN);
3781
- else if (!hidden && rrd_flag_check(rc, RRD_FLAG_HIDDEN))
3782
- rrd_flag_clear(rc, RRD_FLAG_HIDDEN);
3783
- }
3784
-
3785
- bool previous_live_retention = rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION);
3786
- if (live_retention != previous_live_retention) {
3787
- if (live_retention && !rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION))
3788
- rrd_flag_set(rc, RRD_FLAG_LIVE_RETENTION);
3789
- else if (!live_retention && rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION))
3790
- rrd_flag_clear(rc, RRD_FLAG_LIVE_RETENTION);
3791
- }
3792
- }
3793
-
3794
- rrdcontext_lock(rc);
3795
- rc->pp.executions++;
3796
-
3797
- if(unlikely(!instances_active)) {
3798
- // we had some instances, but they are gone now...
3799
-
3800
- if(rc->first_time_s) {
3801
- rc->first_time_s = 0;
3802
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3803
- }
3804
-
3805
- if(rc->last_time_s) {
3806
- rc->last_time_s = 0;
3807
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3808
- }
3809
-
3810
- rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3811
- }
3812
- else {
3813
- // we have some active instances...
3814
-
3815
- if (unlikely(min_first_time_t == LONG_MAX))
3816
- min_first_time_t = 0;
3817
-
3818
- if (unlikely(min_first_time_t == 0 && max_last_time_t == 0)) {
3819
- if(rc->first_time_s) {
3820
- rc->first_time_s = 0;
3821
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3822
- }
3823
-
3824
- if(rc->last_time_s) {
3825
- rc->last_time_s = 0;
3826
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3827
- }
3828
-
3829
- rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3830
- }
3831
- else {
3832
- rrd_flag_clear(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
3833
-
3834
- if (unlikely(rc->first_time_s != min_first_time_t)) {
3835
- rc->first_time_s = min_first_time_t;
3836
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
3837
- }
3838
-
3839
- if (rc->last_time_s != max_last_time_t) {
3840
- rc->last_time_s = max_last_time_t;
3841
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
3842
- }
3843
-
3844
- if(likely(currently_collected))
3845
- rrd_flag_set_collected(rc);
3846
- else
3847
- rrd_flag_set_archived(rc);
3848
- }
3849
-
3850
- if (min_priority != LONG_MAX && rc->priority != min_priority) {
3851
- rc->priority = min_priority;
3852
- rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
3853
- }
3854
- }
3855
-
3856
- if(unlikely(rrd_flag_is_updated(rc) && rc->rrdhost->rrdctx_hub_queue)) {
3857
- if(check_if_cloud_version_changed_unsafe(rc, false)) {
3858
- rc->version = rrdcontext_get_next_version(rc);
3859
- dictionary_set((DICTIONARY *)rc->rrdhost->rrdctx_hub_queue,
3860
- string2str(rc->id), rc, sizeof(*rc));
3861
- }
3862
- }
3863
-
3864
- rrd_flag_unset_updated(rc);
3865
- rrdcontext_unlock(rc);
3866
-}
3867
-
3868
-static void rrdcontext_queue_for_post_processing(RRDCONTEXT *rc, const char *function __maybe_unused, RRD_FLAGS flags __maybe_unused) {
3869
- if(unlikely(!rc->rrdhost->rrdctx_post_processing_queue)) return;
3870
-
3871
- if(!rrd_flag_check(rc, RRD_FLAG_QUEUED_FOR_PP)) {
3872
- dictionary_set((DICTIONARY *)rc->rrdhost->rrdctx_post_processing_queue,
3873
- string2str(rc->id),
3874
- rc,
3875
- sizeof(*rc));
3876
-
3877
-#if(defined(NETDATA_INTERNAL_CHECKS) && defined(LOG_POST_PROCESSING_QUEUE_INSERTIONS))
3878
- {
3879
- BUFFER *wb_flags = buffer_create(1000);
3880
- rrd_flags_to_buffer(flags, wb_flags);
3881
-
3882
- BUFFER *wb_reasons = buffer_create(1000);
3883
- rrd_reasons_to_buffer(flags, wb_reasons);
3884
-
3885
- internal_error(true, "RRDCONTEXT: '%s' update triggered by function %s(), due to flags: %s, reasons: %s",
3886
- string2str(rc->id), function,
3887
- buffer_tostring(wb_flags),
3888
- buffer_tostring(wb_reasons));
3889
-
3890
- buffer_free(wb_reasons);
3891
- buffer_free(wb_flags);
3892
- }
3893
-#endif
3894
- }
3895
-}
3896
-
3897
-static void rrdcontext_dequeue_from_post_processing(RRDCONTEXT *rc) {
3898
- if(unlikely(!rc->rrdhost->rrdctx_post_processing_queue)) return;
3899
- dictionary_del((DICTIONARY *)rc->rrdhost->rrdctx_post_processing_queue, string2str(rc->id));
3900
-}
3901
-
3902
-static void rrdcontext_post_process_queued_contexts(RRDHOST *host) {
3903
- if(unlikely(!host->rrdctx_post_processing_queue)) return;
3904
-
3905
- RRDCONTEXT *rc;
3906
- dfe_start_reentrant((DICTIONARY *)host->rrdctx_post_processing_queue, rc) {
3907
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
3908
-
3909
- rrdcontext_dequeue_from_post_processing(rc);
3910
- rrdcontext_post_process_updates(rc, false, RRD_FLAG_NONE, true);
3911
- }
3912
- dfe_done(rc);
3913
-}
3914
-
3915
-// ----------------------------------------------------------------------------
3916
-// dispatching contexts to cloud
3917
-
3918
-static uint64_t rrdcontext_get_next_version(RRDCONTEXT *rc) {
3919
- time_t now = now_realtime_sec();
3920
- uint64_t version = MAX(rc->version, rc->hub.version);
3921
- version = MAX((uint64_t)now, version);
3922
- version++;
3923
- return version;
3924
-}
3925
-
3926
-static void rrdcontext_message_send_unsafe(RRDCONTEXT *rc, bool snapshot __maybe_unused, void *bundle __maybe_unused) {
3927
-
3928
- // save it, so that we know the last version we sent to hub
3929
- rc->version = rc->hub.version = rrdcontext_get_next_version(rc);
3930
- rc->hub.id = string2str(rc->id);
3931
- rc->hub.title = string2str(rc->title);
3932
- rc->hub.units = string2str(rc->units);
3933
- rc->hub.family = string2str(rc->family);
3934
- rc->hub.chart_type = rrdset_type_name(rc->chart_type);
3935
- rc->hub.priority = rc->priority;
3936
- rc->hub.first_time_s = rc->first_time_s;
3937
- rc->hub.last_time_s = rrd_flag_is_collected(rc) ? 0 : rc->last_time_s;
3938
- rc->hub.deleted = rrd_flag_is_deleted(rc) ? true : false;
3939
-
3940
-#ifdef ENABLE_ACLK
3941
- struct context_updated message = {
3942
- .id = rc->hub.id,
3943
- .version = rc->hub.version,
3944
- .title = rc->hub.title,
3945
- .units = rc->hub.units,
3946
- .family = rc->hub.family,
3947
- .chart_type = rc->hub.chart_type,
3948
- .priority = rc->hub.priority,
3949
- .first_entry = rc->hub.first_time_s,
3950
- .last_entry = rc->hub.last_time_s,
3951
- .deleted = rc->hub.deleted,
3952
- };
3953
-
3954
- if(likely(!rrd_flag_check(rc, RRD_FLAG_HIDDEN))) {
3955
- if (snapshot) {
3956
- if (!rc->hub.deleted)
3957
- contexts_snapshot_add_ctx_update(bundle, &message);
3958
- }
3959
- else
3960
- contexts_updated_add_ctx_update(bundle, &message);
3961
- }
3962
-#endif
3963
-
3964
- // store it to SQL
3965
-
3966
- if(rrd_flag_is_deleted(rc))
3967
- rrdcontext_delete_from_sql_unsafe(rc);
3968
-
3969
- else if (ctx_store_context(&rc->rrdhost->host_uuid, &rc->hub) != 0)
3970
- error("RRDCONTEXT: failed to save context '%s' version %"PRIu64" to SQL.", rc->hub.id, rc->hub.version);
3971
-}
3972
-
3973
-static bool check_if_cloud_version_changed_unsafe(RRDCONTEXT *rc, bool sending __maybe_unused) {
3974
- bool id_changed = false,
3975
- title_changed = false,
3976
- units_changed = false,
3977
- family_changed = false,
3978
- chart_type_changed = false,
3979
- priority_changed = false,
3980
- first_time_changed = false,
3981
- last_time_changed = false,
3982
- deleted_changed = false;
3983
-
3984
- RRD_FLAGS flags = rrd_flags_get(rc);
3985
-
3986
- if(unlikely(string2str(rc->id) != rc->hub.id))
3987
- id_changed = true;
3988
-
3989
- if(unlikely(string2str(rc->title) != rc->hub.title))
3990
- title_changed = true;
3991
-
3992
- if(unlikely(string2str(rc->units) != rc->hub.units))
3993
- units_changed = true;
3994
-
3995
- if(unlikely(string2str(rc->family) != rc->hub.family))
3996
- family_changed = true;
3997
-
3998
- if(unlikely(rrdset_type_name(rc->chart_type) != rc->hub.chart_type))
3999
- chart_type_changed = true;
4000
-
4001
- if(unlikely(rc->priority != rc->hub.priority))
4002
- priority_changed = true;
4003
-
4004
- if(unlikely((uint64_t)rc->first_time_s != rc->hub.first_time_s))
4005
- first_time_changed = true;
4006
-
4007
- if(unlikely((uint64_t)((flags & RRD_FLAG_COLLECTED) ? 0 : rc->last_time_s) != rc->hub.last_time_s))
4008
- last_time_changed = true;
4009
-
4010
- if(unlikely(((flags & RRD_FLAG_DELETED) ? true : false) != rc->hub.deleted))
4011
- deleted_changed = true;
4012
-
4013
- if(unlikely(id_changed || title_changed || units_changed || family_changed || chart_type_changed || priority_changed || first_time_changed || last_time_changed || deleted_changed)) {
4014
-
4015
- internal_error(LOG_TRANSITIONS,
4016
- "RRDCONTEXT: %s NEW VERSION '%s'%s of host '%s', version %"PRIu64", title '%s'%s, units '%s'%s, family '%s'%s, chart type '%s'%s, priority %u%s, first_time_t %ld%s, last_time_t %ld%s, deleted '%s'%s, (queued for %llu ms, expected %llu ms)",
4017
- sending?"SENDING":"QUEUE",
4018
- string2str(rc->id), id_changed ? " (CHANGED)" : "",
4019
- rrdhost_hostname(rc->rrdhost),
4020
- rc->version,
4021
- string2str(rc->title), title_changed ? " (CHANGED)" : "",
4022
- string2str(rc->units), units_changed ? " (CHANGED)" : "",
4023
- string2str(rc->family), family_changed ? " (CHANGED)" : "",
4024
- rrdset_type_name(rc->chart_type), chart_type_changed ? " (CHANGED)" : "",
4025
- rc->priority, priority_changed ? " (CHANGED)" : "",
4026
- rc->first_time_s, first_time_changed ? " (CHANGED)" : "",
4027
- (flags & RRD_FLAG_COLLECTED) ? 0 : rc->last_time_s, last_time_changed ? " (CHANGED)" : "",
4028
- (flags & RRD_FLAG_DELETED) ? "true" : "false", deleted_changed ? " (CHANGED)" : "",
4029
- sending ? (now_realtime_usec() - rc->queue.queued_ut) / USEC_PER_MS : 0,
4030
- sending ? (rc->queue.scheduled_dispatch_ut - rc->queue.queued_ut) / USEC_PER_MS : 0
4031
- );
4032
-
4033
- return true;
4034
- }
4035
-
4036
- return false;
4037
-}
4038
-
4039
-static inline usec_t rrdcontext_calculate_queued_dispatch_time_ut(RRDCONTEXT *rc, usec_t now_ut) {
4040
-
4041
- if(likely(rc->queue.delay_calc_ut >= rc->queue.queued_ut))
4042
- return rc->queue.scheduled_dispatch_ut;
4043
-
4044
- RRD_FLAGS flags = rc->queue.queued_flags;
4045
-
4046
- usec_t delay = LONG_MAX;
4047
- int i;
4048
- struct rrdcontext_reason *reason;
4049
- for(i = 0, reason = &rrdcontext_reasons[i]; reason->name ; reason = &rrdcontext_reasons[++i]) {
4050
- if(unlikely(flags & reason->flag)) {
4051
- if(reason->delay_ut < delay)
4052
- delay = reason->delay_ut;
4053
- }
4054
- }
4055
-
4056
- if(unlikely(delay == LONG_MAX)) {
4057
- internal_error(true, "RRDCONTEXT: '%s', cannot find minimum delay of flags %x", string2str(rc->id), (unsigned int)flags);
4058
- delay = 60 * USEC_PER_SEC;
4059
- }
4060
-
4061
- rc->queue.delay_calc_ut = now_ut;
4062
- usec_t dispatch_ut = rc->queue.scheduled_dispatch_ut = rc->queue.queued_ut + delay;
4063
- return dispatch_ut;
4064
-}
4065
-
4066
-static void rrdcontext_dequeue_from_hub_queue(RRDCONTEXT *rc) {
4067
- dictionary_del((DICTIONARY *)rc->rrdhost->rrdctx_hub_queue, string2str(rc->id));
4068
-}
4069
-
4070
-static void rrdcontext_dispatch_queued_contexts_to_hub(RRDHOST *host, usec_t now_ut) {
4071
-
4072
- // check if we have received a streaming command for this host
4073
- if(!rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS) || !aclk_connected || !host->rrdctx_hub_queue)
4074
- return;
4075
-
4076
- // check if there are queued items to send
4077
- if(!dictionary_entries((DICTIONARY *)host->rrdctx_hub_queue))
4078
- return;
4079
-
4080
- if(!host->node_id)
4081
- return;
4082
-
4083
- size_t messages_added = 0;
4084
- contexts_updated_t bundle = NULL;
4085
-
4086
- RRDCONTEXT *rc;
4087
- dfe_start_reentrant((DICTIONARY *)host->rrdctx_hub_queue, rc) {
4088
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
4089
-
4090
- if(unlikely(messages_added >= MESSAGES_PER_BUNDLE_TO_SEND_TO_HUB_PER_HOST))
4091
- break;
4092
-
4093
- worker_is_busy(WORKER_JOB_QUEUED);
4094
- usec_t dispatch_ut = rrdcontext_calculate_queued_dispatch_time_ut(rc, now_ut);
4095
- char *claim_id = get_agent_claimid();
4096
-
4097
- if(unlikely(now_ut >= dispatch_ut) && claim_id) {
4098
- worker_is_busy(WORKER_JOB_CHECK);
4099
-
4100
- rrdcontext_lock(rc);
4101
-
4102
- if(check_if_cloud_version_changed_unsafe(rc, true)) {
4103
- worker_is_busy(WORKER_JOB_SEND);
4104
-
4105
-#ifdef ENABLE_ACLK
4106
- if(!bundle) {
4107
- // prepare the bundle to send the messages
4108
- char uuid[UUID_STR_LEN];
4109
- uuid_unparse_lower(*host->node_id, uuid);
4110
-
4111
- bundle = contexts_updated_new(claim_id, uuid, 0, now_ut);
4112
- }
4113
-#endif
4114
- // update the hub data of the context, give a new version, pack the message
4115
- // and save an update to SQL
4116
- rrdcontext_message_send_unsafe(rc, false, bundle);
4117
- messages_added++;
4118
-
4119
- rc->queue.dispatches++;
4120
- rc->queue.dequeued_ut = now_ut;
4121
- }
4122
- else
4123
- rc->version = rc->hub.version;
4124
-
4125
- // remove it from the queue
4126
- worker_is_busy(WORKER_JOB_DEQUEUE);
4127
- rrdcontext_dequeue_from_hub_queue(rc);
4128
-
4129
- if(unlikely(rrdcontext_should_be_deleted(rc))) {
4130
- // this is a deleted context - delete it forever...
4131
-
4132
- worker_is_busy(WORKER_JOB_CLEANUP_DELETE);
4133
-
4134
- rrdcontext_dequeue_from_post_processing(rc);
4135
- rrdcontext_delete_from_sql_unsafe(rc);
4136
-
4137
- STRING *id = string_dup(rc->id);
4138
- rrdcontext_unlock(rc);
4139
-
4140
- // delete it from the master dictionary
4141
- if(!dictionary_del((DICTIONARY *)host->rrdctx, string2str(rc->id)))
4142
- error("RRDCONTEXT: '%s' of host '%s' failed to be deleted from rrdcontext dictionary.",
4143
- string2str(id), rrdhost_hostname(host));
4144
-
4145
- string_freez(id);
4146
- }
4147
- else
4148
- rrdcontext_unlock(rc);
4149
- }
4150
- freez(claim_id);
4151
- }
4152
- dfe_done(rc);
4153
-
4154
-#ifdef ENABLE_ACLK
4155
- if(service_running(SERVICE_CONTEXT) && bundle) {
4156
- // we have a bundle to send messages
4157
-
4158
- // update the version hash
4159
- contexts_updated_update_version_hash(bundle, rrdcontext_version_hash(host));
4160
-
4161
- // send it
4162
- aclk_send_contexts_updated(bundle);
4163
- }
4164
- else if(bundle)
4165
- contexts_updated_delete(bundle);
4166
-#endif
4167
-
4168
-}
4169
-
4170
-// ----------------------------------------------------------------------------
4171
-// worker thread
4172
-
4173
-static void rrdcontext_main_cleanup(void *ptr) {
4174
- struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
4175
- static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
4176
-
4177
- // custom code
4178
- worker_unregister();
4179
-
4180
- static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
4181
-}
4182
-
4183
-void *rrdcontext_main(void *ptr) {
4184
- netdata_thread_cleanup_push(rrdcontext_main_cleanup, ptr);
4185
-
4186
- worker_register("RRDCONTEXT");
4187
- worker_register_job_name(WORKER_JOB_HOSTS, "hosts");
4188
- worker_register_job_name(WORKER_JOB_CHECK, "dedup checks");
4189
- worker_register_job_name(WORKER_JOB_SEND, "sent contexts");
4190
- worker_register_job_name(WORKER_JOB_DEQUEUE, "deduplicated contexts");
4191
- worker_register_job_name(WORKER_JOB_RETENTION, "metrics retention");
4192
- worker_register_job_name(WORKER_JOB_QUEUED, "queued contexts");
4193
- worker_register_job_name(WORKER_JOB_CLEANUP, "cleanups");
4194
- worker_register_job_name(WORKER_JOB_CLEANUP_DELETE, "deletes");
4195
- worker_register_job_name(WORKER_JOB_PP_METRIC, "check metrics");
4196
- worker_register_job_name(WORKER_JOB_PP_INSTANCE, "check instances");
4197
- worker_register_job_name(WORKER_JOB_PP_CONTEXT, "check contexts");
4198
-
4199
- worker_register_job_custom_metric(WORKER_JOB_HUB_QUEUE_SIZE, "hub queue size", "contexts", WORKER_METRIC_ABSOLUTE);
4200
- worker_register_job_custom_metric(WORKER_JOB_PP_QUEUE_SIZE, "post processing queue size", "contexts", WORKER_METRIC_ABSOLUTE);
4201
-
4202
- heartbeat_t hb;
4203
- heartbeat_init(&hb);
4204
- usec_t step = RRDCONTEXT_WORKER_THREAD_HEARTBEAT_USEC;
4205
-
4206
- while (service_running(SERVICE_CONTEXT)) {
4207
- worker_is_idle();
4208
- heartbeat_next(&hb, step);
4209
-
4210
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
4211
-
4212
- usec_t now_ut = now_realtime_usec();
4213
-
4214
- if(rrdcontext_next_db_rotation_ut && now_ut > rrdcontext_next_db_rotation_ut) {
4215
- rrdcontext_recalculate_retention_all_hosts();
4216
- rrdcontext_garbage_collect_for_all_hosts();
4217
- rrdcontext_next_db_rotation_ut = 0;
4218
- }
4219
-
4220
- size_t hub_queued_contexts_for_all_hosts = 0;
4221
- size_t pp_queued_contexts_for_all_hosts = 0;
4222
-
4223
- rrd_rdlock();
4224
- RRDHOST *host;
4225
- rrdhost_foreach_read(host) {
4226
- if(unlikely(!service_running(SERVICE_CONTEXT))) break;
4227
-
4228
- worker_is_busy(WORKER_JOB_HOSTS);
4229
-
4230
- if(host->rrdctx_post_processing_queue) {
4231
- pp_queued_contexts_for_all_hosts +=
4232
- dictionary_entries((DICTIONARY *)host->rrdctx_post_processing_queue);
4233
- rrdcontext_post_process_queued_contexts(host);
4234
- }
4235
-
4236
- if(host->rrdctx_hub_queue) {
4237
- hub_queued_contexts_for_all_hosts += dictionary_entries((DICTIONARY *)host->rrdctx_hub_queue);
4238
- rrdcontext_dispatch_queued_contexts_to_hub(host, now_ut);
4239
- }
4240
- }
4241
- rrd_unlock();
4242
-
4243
- worker_set_metric(WORKER_JOB_HUB_QUEUE_SIZE, (NETDATA_DOUBLE)hub_queued_contexts_for_all_hosts);
4244
- worker_set_metric(WORKER_JOB_PP_QUEUE_SIZE, (NETDATA_DOUBLE)pp_queued_contexts_for_all_hosts);
4245
- }
4246
-
4247
- netdata_thread_cleanup_pop(1);
4248
- return NULL;
4249
-}