1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "rrdcontext-internal.h"
4
+
5
+typedef enum {
6
+ RRDCONTEXT_QUEUE_INVALID = 0,
7
+ RRDCONTEXT_QUEUE_ADDED,
8
+ RRDCONTEXT_QUEUE_FOUND,
9
+} RRDCONTEXT_QUEUE_STATUS;
10
+
11
+static inline RRDCONTEXT_QUEUE_STATUS rrdcontext_queue_add(RRDCONTEXT_QUEUE_JudyLSet *queue, RRDCONTEXT *rc, Word_t *idx, bool having_lock) {
12
+ RRDCONTEXT_QUEUE_STATUS ret = RRDCONTEXT_QUEUE_INVALID;
13
+ if(!queue || !rc || !idx) return ret;
14
+
15
+ if(!having_lock)
16
+ spinlock_lock(&queue->spinlock);
17
+
18
+ if(*idx) {
19
+ fatal_assert(RRDCONTEXT_QUEUE_GET(queue, *idx) == rc);
20
+ ret = RRDCONTEXT_QUEUE_FOUND;
21
+ }
22
+ else {
23
+ *idx = queue->id++;
24
+ RRDCONTEXT_QUEUE_SET(queue, *idx, rc);
25
+ __atomic_add_fetch(&queue->version, 1, __ATOMIC_RELAXED);
26
+ __atomic_add_fetch(&queue->entries, 1, __ATOMIC_RELAXED);
27
+ ret = RRDCONTEXT_QUEUE_ADDED;
28
+ }
29
+
30
+ if(!having_lock)
31
+ spinlock_unlock(&queue->spinlock);
32
+
33
+ return ret;
34
+}
35
+
36
+void rrdcontext_add_to_hub_queue(RRDCONTEXT *rc) {
37
+ if(!rc || !rc->rrdhost) return;
38
+
39
+ spinlock_lock(&rc->rrdhost->rrdctx.hub_queue.spinlock);
40
+
41
+ RRDCONTEXT_QUEUE_STATUS ret = rrdcontext_queue_add(&rc->rrdhost->rrdctx.hub_queue, rc, &rc->queue.idx, true);
42
+
43
+ if(ret == RRDCONTEXT_QUEUE_ADDED) {
44
+ rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_HUB);
45
+ rc->queue.queued_ut = now_realtime_usec();
46
+ rc->queue.queued_flags = rrd_flags_get(rc);
47
+ }
48
+ else if(ret == RRDCONTEXT_QUEUE_FOUND) {
49
+ rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_HUB);
50
+ rc->queue.queued_ut = now_realtime_usec();
51
+ rc->queue.queued_flags |= rrd_flags_get(rc);
52
+ }
53
+
54
+ spinlock_unlock(&rc->rrdhost->rrdctx.hub_queue.spinlock);
55
+}
56
+
57
+void rrdcontext_add_to_pp_queue(RRDCONTEXT *rc) {
58
+ if(!rc || !rc->rrdhost) return;
59
+
60
+ spinlock_lock(&rc->rrdhost->rrdctx.pp_queue.spinlock);
61
+
62
+ RRDCONTEXT_QUEUE_STATUS ret = rrdcontext_queue_add(&rc->rrdhost->rrdctx.pp_queue, rc, &rc->pp.idx, true);
63
+
64
+ if(ret == RRDCONTEXT_QUEUE_ADDED) {
65
+ rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_PP);
66
+ rc->pp.queued_flags = rc->flags;
67
+ rc->pp.queued_ut = now_realtime_usec();
68
+ }
69
+ else if(ret == RRDCONTEXT_QUEUE_FOUND) {
70
+ rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_PP);
71
+ rc->pp.queued_flags |= rc->flags;
72
+ }
73
+
74
+ spinlock_unlock(&rc->rrdhost->rrdctx.pp_queue.spinlock);
75
+}
76
+
77
+static inline bool rrdcontext_queue_del(RRDCONTEXT_QUEUE_JudyLSet *queue, RRDCONTEXT *rc, Word_t *idx, bool having_lock) {
78
+ bool ret = false;
79
+ if(!queue || !rc || !idx) return ret;
80
+
81
+ if(!having_lock)
82
+ spinlock_lock(&queue->spinlock);
83
+
84
+ RRDCONTEXT *rc_found = RRDCONTEXT_QUEUE_GET(queue, *idx);
85
+
86
+ if(rc_found == rc) {
87
+ RRDCONTEXT_QUEUE_DEL(queue, *idx);
88
+ __atomic_add_fetch(&queue->version, 1, __ATOMIC_RELAXED);
89
+ __atomic_sub_fetch(&queue->entries, 1, __ATOMIC_RELAXED);
90
+ ret = true;
91
+ }
92
+ *idx = 0;
93
+
94
+ if(!having_lock)
95
+ spinlock_unlock(&queue->spinlock);
96
+
97
+ return ret;
98
+}
99
+
100
+void rrdcontext_del_from_hub_queue(RRDCONTEXT *rc, bool having_lock) {
101
+ if(!rc || !rc->rrdhost) return;
102
+ if(!having_lock)
103
+ spinlock_lock(&rc->rrdhost->rrdctx.hub_queue.spinlock);
104
+
105
+ if(rrdcontext_queue_del(&rc->rrdhost->rrdctx.hub_queue, rc, &rc->queue.idx, true)) {
106
+ rrd_flag_clear(rc, RRD_FLAG_QUEUED_FOR_HUB);
107
+ }
108
+
109
+ if(!having_lock)
110
+ spinlock_unlock(&rc->rrdhost->rrdctx.hub_queue.spinlock);
111
+}
112
+
113
+void rrdcontext_del_from_pp_queue(RRDCONTEXT *rc, bool having_lock) {
114
+ if(!rc || !rc->rrdhost) return;
115
+
116
+ if(!having_lock)
117
+ spinlock_lock(&rc->rrdhost->rrdctx.pp_queue.spinlock);
118
+
119
+ if(rrdcontext_queue_del(&rc->rrdhost->rrdctx.pp_queue, rc, &rc->pp.idx, true)) {
120
+ rrd_flag_clear(rc, RRD_FLAG_QUEUED_FOR_PP);
121
+ rc->pp.dequeued_ut = now_realtime_usec();
122
+ }
123
+
124
+ if(!having_lock)
125
+ spinlock_unlock(&rc->rrdhost->rrdctx.pp_queue.spinlock);
126
+}
127
+
128
+
129
+uint32_t rrdcontext_queue_version(RRDCONTEXT_QUEUE_JudyLSet *queue) {
130
+ return __atomic_load_n(&queue->version, __ATOMIC_RELAXED);
131
+}
132
+
133
+int32_t rrdcontext_queue_entries(RRDCONTEXT_QUEUE_JudyLSet *queue) {
134
+ return __atomic_load_n(&queue->entries, __ATOMIC_RELAXED);
135
+}
136
+
137
+void rrdcontext_post_process_queued_contexts(RRDHOST *host) {
138
+
139
+ spinlock_lock(&host->rrdctx.pp_queue.spinlock);
140
+ Word_t idx = 0;
141
+ for(RRDCONTEXT *rc = RRDCONTEXT_QUEUE_FIRST(&host->rrdctx.pp_queue, &idx);
142
+ rc;
143
+ rc = RRDCONTEXT_QUEUE_NEXT(&host->rrdctx.pp_queue, &idx)) {
144
+ if(unlikely(!service_running(SERVICE_CONTEXT))) break;
145
+
146
+ const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(host->rrdctx.contexts, string2str(rc->id));
147
+ bool do_it = dictionary_acquired_item_value(item) == rc;
148
+
149
+ if(do_it)
150
+ rrdcontext_del_from_pp_queue(rc, true);
151
+
152
+ spinlock_unlock(&host->rrdctx.pp_queue.spinlock);
153
+
154
+ if(item) {
155
+ if (do_it)
156
+ rrdcontext_post_process_updates(rc, false, RRD_FLAG_NONE, true);
157
+
158
+ dictionary_acquired_item_release(host->rrdctx.contexts, item);
159
+ }
160
+
161
+ spinlock_lock(&host->rrdctx.pp_queue.spinlock);
162
+ }
163
+
164
+ spinlock_unlock(&host->rrdctx.pp_queue.spinlock);
165
+}
166
+
167
+void rrdcontext_dispatch_queued_contexts_to_hub(RRDHOST *host, usec_t now_ut) {
168
+ // check if we have received a streaming command for this host
169
+ if(UUIDiszero(host->node_id) || !rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS) || !aclk_online_for_contexts())
170
+ return;
171
+
172
+ // check if there are queued items to send
173
+ if(!rrdcontext_queue_entries(&host->rrdctx.hub_queue))
174
+ return;
175
+
176
+ size_t messages_added = 0;
177
+ contexts_updated_t bundle = NULL;
178
+
179
+ spinlock_lock(&host->rrdctx.hub_queue.spinlock);
180
+ Word_t idx = 0;
181
+ for(RRDCONTEXT *rc = RRDCONTEXT_QUEUE_FIRST(&host->rrdctx.hub_queue, &idx);
182
+ rc;
183
+ rc = RRDCONTEXT_QUEUE_NEXT(&host->rrdctx.hub_queue, &idx)) {
184
+ if(unlikely(!service_running(SERVICE_CONTEXT))) break;
185
+
186
+ if(unlikely(messages_added >= MESSAGES_PER_BUNDLE_TO_SEND_TO_HUB_PER_HOST))
187
+ break;
188
+
189
+ const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(host->rrdctx.contexts, string2str(rc->id));
190
+ bool do_it = dictionary_acquired_item_value(item) == rc;
191
+
192
+ if(do_it) {
193
+ worker_is_busy(WORKER_JOB_DEQUEUE);
194
+ rrdcontext_del_from_hub_queue(rc, true);
195
+ }
196
+
197
+ spinlock_unlock(&host->rrdctx.hub_queue.spinlock);
198
+
199
+ if(item) {
200
+ if (do_it) {
201
+ worker_is_busy(WORKER_JOB_QUEUED);
202
+ usec_t dispatch_ut = rrdcontext_calculate_queued_dispatch_time_ut(rc, now_ut);
203
+ CLAIM_ID claim_id = claim_id_get();
204
+
205
+ if(unlikely(now_ut >= dispatch_ut) && claim_id_is_set(claim_id)) {
206
+ worker_is_busy(WORKER_JOB_CHECK);
207
+
208
+ rrdcontext_lock(rc);
209
+
210
+ if(check_if_cloud_version_changed_unsafe(rc, true)) {
211
+ worker_is_busy(WORKER_JOB_SEND);
212
+
213
+ if(!bundle) {
214
+ // prepare the bundle to send the messages
215
+ char uuid_str[UUID_STR_LEN];
216
+ uuid_unparse_lower(host->node_id.uuid, uuid_str);
217
+
218
+ bundle = contexts_updated_new(claim_id.str, uuid_str, 0, now_ut);
219
+ }
220
+ // update the hub data of the context, give a new version, pack the message
221
+ // and save an update to SQL
222
+ rrdcontext_message_send_unsafe(rc, false, bundle);
223
+ messages_added++;
224
+
225
+ rc->queue.dispatches++;
226
+ rc->queue.dequeued_ut = now_ut;
227
+ }
228
+ else
229
+ rc->version = rc->hub.version;
230
+
231
+ if(unlikely(rrdcontext_should_be_deleted(rc))) {
232
+ // this is a deleted context - delete it forever...
233
+
234
+ worker_is_busy(WORKER_JOB_CLEANUP_DELETE);
235
+
236
+ rrdcontext_dequeue_from_post_processing(rc);
237
+ rrdcontext_delete_from_sql_unsafe(rc);
238
+
239
+ STRING *id = string_dup(rc->id);
240
+ rrdcontext_unlock(rc);
241
+
242
+ // delete it from the master dictionary
243
+ if(!dictionary_del(host->rrdctx.contexts, string2str(rc->id)))
244
+ netdata_log_error("RRDCONTEXT: '%s' of host '%s' failed to be deleted from rrdcontext dictionary.",
245
+ string2str(id), rrdhost_hostname(host));
246
+
247
+ string_freez(id);
248
+ }
249
+ else
250
+ rrdcontext_unlock(rc);
251
+ }
252
+ }
253
+
254
+ dictionary_acquired_item_release(host->rrdctx.contexts, item);
255
+ }
256
+
257
+ spinlock_lock(&host->rrdctx.hub_queue.spinlock);
258
+ }
259
+ spinlock_unlock(&host->rrdctx.hub_queue.spinlock);
260
+
261
+ if(service_running(SERVICE_CONTEXT) && bundle) {
262
+ // we have a bundle to send messages
263
+
264
+ // update the version hash
265
+ contexts_updated_update_version_hash(bundle, rrdcontext_version_hash(host));
266
+
267
+ // send it
268
+ aclk_send_contexts_updated(bundle);
269
+ }
270
+ else if(bundle)
271
+ contexts_updated_delete(bundle);
272
+}