2
#define NETDATA_RRD_INTERNALS
3
4
#include "rrdengine.h"
5
+#include "pdc.h"
6
7
rrdeng_stats_t global_io_errors = 0;
8
rrdeng_stats_t global_fs_errors = 0;
12
13
unsigned rrdeng_pages_per_extent = MAX_PAGES_PER_EXTENT;
14
14
-#if WORKER_UTILIZATION_MAX_JOB_TYPES < (RRDENG_MAX_OPCODE + 2)
15
+#if WORKER_UTILIZATION_MAX_JOB_TYPES < (RRDENG_OPCODE_MAX + 2)
16
#error Please increase WORKER_UTILIZATION_MAX_JOB_TYPES to at least (RRDENG_MAX_OPCODE + 2)
17
#endif
18
18
-void *dbengine_page_alloc() {
19
- void *page = NULL;
20
- if (unlikely(db_engine_use_malloc))
21
- page = mallocz(RRDENG_BLOCK_SIZE);
22
- else {
23
- page = netdata_mmap(NULL, RRDENG_BLOCK_SIZE, MAP_PRIVATE, enable_ksm);
24
- if(!page) fatal("Cannot allocate dbengine page cache page, with mmap()");
25
- }
26
- return page;
27
-}
28
-
29
-void dbengine_page_free(void *page) {
30
- if (unlikely(db_engine_use_malloc))
31
- freez(page);
32
- else
33
- netdata_munmap(page, RRDENG_BLOCK_SIZE);
34
-}
19
+struct rrdeng_main {
20
+ uv_thread_t thread;
21
+ uv_loop_t loop;
22
+ uv_async_t async;
23
+ uv_timer_t timer;
24
+ pid_t tid;
25
+
26
+ time_t last_buffers_cleanup_s;
27
+
28
+ bool flush_running;
29
+ bool evict_running;
30
+} rrdeng_main = {
31
+ .thread = 0,
32
+ .loop = {},
33
+ .async = {},
34
+ .timer = {},
35
+ .last_buffers_cleanup_s = 0,
36
+ .flush_running = false,
37
+ .evict_running = false,
38
+};
39
40
static void sanity_check(void)
41
{
38
- BUILD_BUG_ON(WORKER_UTILIZATION_MAX_JOB_TYPES < (RRDENG_MAX_OPCODE + 2));
42
+ BUILD_BUG_ON(WORKER_UTILIZATION_MAX_JOB_TYPES < (RRDENG_OPCODE_MAX + 2));
43
44
/* Magic numbers must fit in the super-blocks */
45
BUILD_BUG_ON(strlen(RRDENG_DF_MAGIC) > RRDENG_MAGIC_SZ);
58
BUILD_BUG_ON(MAX_PAGES_PER_EXTENT > 255);
59
60
/* extent cache count must fit in 32 bits */
57
- BUILD_BUG_ON(MAX_CACHED_EXTENTS > 32);
61
+// BUILD_BUG_ON(MAX_CACHED_EXTENTS > 32);
62
63
/* page info scratch space must be able to hold 2 32-bit integers */
64
BUILD_BUG_ON(sizeof(((struct rrdeng_page_info *)0)->scratch) < 2 * sizeof(uint32_t));
65
}
66
63
-/* always inserts into tail */
64
-static inline void xt_cache_replaceQ_insert(struct rrdengine_worker_config* wc,
65
- struct extent_cache_element *xt_cache_elem)
66
-{
67
- struct extent_cache *xt_cache = &wc->xt_cache;
67
+// ----------------------------------------------------------------------------
68
+// work request cache
69
69
- xt_cache_elem->prev = NULL;
70
- xt_cache_elem->next = NULL;
70
+typedef void (*work_cb)(struct rrdengine_instance *ctx, void *data, struct completion *completion, uv_work_t* req);
71
+typedef void (*after_work_cb)(struct rrdengine_instance *ctx, void *data, struct completion *completion, uv_work_t* req, int status);
72
72
- if (likely(NULL != xt_cache->replaceQ_tail)) {
73
- xt_cache_elem->prev = xt_cache->replaceQ_tail;
74
- xt_cache->replaceQ_tail->next = xt_cache_elem;
75
- }
76
- if (unlikely(NULL == xt_cache->replaceQ_head)) {
77
- xt_cache->replaceQ_head = xt_cache_elem;
73
+struct rrdeng_work {
74
+ uv_work_t req;
75
+
76
+ struct rrdengine_instance *ctx;
77
+ void *data;
78
+ struct completion *completion;
79
+
80
+ work_cb work_cb;
81
+ after_work_cb after_work_cb;
82
+ enum rrdeng_opcode opcode;
83
+
84
+ struct {
85
+ struct rrdeng_work *prev;
86
+ struct rrdeng_work *next;
87
+ } cache;
88
+};
89
+
90
+static struct {
91
+ struct {
92
+ SPINLOCK spinlock;
93
+ struct rrdeng_work *available_items;
94
+ size_t available;
95
+ } protected;
96
+
97
+ struct {
98
+ size_t allocated;
99
+ size_t dispatched;
100
+ size_t executing;
101
+ size_t pending_cb;
102
+ } atomics;
103
+} work_request_globals = {
104
+ .protected = {
105
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
106
+ .available_items = NULL,
107
+ .available = 0,
108
+ },
109
+ .atomics = {
110
+ .allocated = 0,
111
+ .dispatched = 0,
112
+ .executing = 0,
113
+ },
114
+};
115
+
116
+static inline bool work_request_full(void) {
117
+ return __atomic_load_n(&work_request_globals.atomics.dispatched, __ATOMIC_RELAXED) >= (size_t)(libuv_worker_threads - RESERVED_LIBUV_WORKER_THREADS);
118
+}
119
+
120
+static void work_request_cleanup(void) {
121
+ netdata_spinlock_lock(&work_request_globals.protected.spinlock);
122
+ while(work_request_globals.protected.available_items && work_request_globals.protected.available > (size_t)libuv_worker_threads) {
123
+ struct rrdeng_work *item = work_request_globals.protected.available_items;
124
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(work_request_globals.protected.available_items, item, cache.prev, cache.next);
125
+ freez(item);
126
+ work_request_globals.protected.available--;
127
+ __atomic_sub_fetch(&work_request_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
128
}
79
- xt_cache->replaceQ_tail = xt_cache_elem;
129
+ netdata_spinlock_unlock(&work_request_globals.protected.spinlock);
130
}
131
82
-static inline void xt_cache_replaceQ_delete(struct rrdengine_worker_config* wc,
83
- struct extent_cache_element *xt_cache_elem)
84
-{
85
- struct extent_cache *xt_cache = &wc->xt_cache;
86
- struct extent_cache_element *prev, *next;
132
+static inline void work_done(struct rrdeng_work *work_request) {
133
+ netdata_spinlock_lock(&work_request_globals.protected.spinlock);
134
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(work_request_globals.protected.available_items, work_request, cache.prev, cache.next);
135
+ work_request_globals.protected.available++;
136
+ netdata_spinlock_unlock(&work_request_globals.protected.spinlock);
137
+}
138
88
- prev = xt_cache_elem->prev;
89
- next = xt_cache_elem->next;
139
+void work_standard_worker(uv_work_t *req) {
140
+ __atomic_add_fetch(&work_request_globals.atomics.executing, 1, __ATOMIC_RELAXED);
141
91
- if (likely(NULL != prev)) {
92
- prev->next = next;
93
- }
94
- if (likely(NULL != next)) {
95
- next->prev = prev;
142
+ register_libuv_worker_jobs();
143
+ worker_is_busy(UV_EVENT_WORKER_INIT);
144
+
145
+ struct rrdeng_work *work_request = req->data;
146
+ work_request->work_cb(work_request->ctx, work_request->data, work_request->completion, req);
147
+ worker_is_idle();
148
+
149
+ __atomic_sub_fetch(&work_request_globals.atomics.dispatched, 1, __ATOMIC_RELAXED);
150
+ __atomic_sub_fetch(&work_request_globals.atomics.executing, 1, __ATOMIC_RELAXED);
151
+ __atomic_add_fetch(&work_request_globals.atomics.pending_cb, 1, __ATOMIC_RELAXED);
152
+
153
+ // signal the event loop a worker is available
154
+ fatal_assert(0 == uv_async_send(&rrdeng_main.async));
155
+}
156
+
157
+void after_work_standard_callback(uv_work_t* req, int status) {
158
+ struct rrdeng_work *work_request = req->data;
159
+
160
+ worker_is_busy(RRDENG_OPCODE_MAX + work_request->opcode);
161
+
162
+ if(work_request->after_work_cb)
163
+ work_request->after_work_cb(work_request->ctx, work_request->data, work_request->completion, req, status);
164
+
165
+ work_done(work_request);
166
+ __atomic_sub_fetch(&work_request_globals.atomics.pending_cb, 1, __ATOMIC_RELAXED);
167
+
168
+ worker_is_idle();
169
+}
170
+
171
+static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct completion *completion, enum rrdeng_opcode opcode, work_cb work_cb, after_work_cb after_work_cb) {
172
+ struct rrdeng_work *work_request = NULL;
173
+
174
+ internal_fatal(rrdeng_main.tid != gettid(), "work_dispatch() can only be run from the event loop thread");
175
+
176
+ netdata_spinlock_lock(&work_request_globals.protected.spinlock);
177
+
178
+ if(likely(work_request_globals.protected.available_items)) {
179
+ work_request = work_request_globals.protected.available_items;
180
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(work_request_globals.protected.available_items, work_request, cache.prev, cache.next);
181
+ work_request_globals.protected.available--;
182
}
97
- if (unlikely(xt_cache_elem == xt_cache->replaceQ_head)) {
98
- xt_cache->replaceQ_head = next;
183
+
184
+ netdata_spinlock_unlock(&work_request_globals.protected.spinlock);
185
+
186
+ if(unlikely(!work_request)) {
187
+ work_request = mallocz(sizeof(struct rrdeng_work));
188
+ __atomic_add_fetch(&work_request_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
189
}
100
- if (unlikely(xt_cache_elem == xt_cache->replaceQ_tail)) {
101
- xt_cache->replaceQ_tail = prev;
190
+
191
+ memset(work_request, 0, sizeof(struct rrdeng_work));
192
+ work_request->req.data = work_request;
193
+ work_request->ctx = ctx;
194
+ work_request->data = data;
195
+ work_request->completion = completion;
196
+ work_request->work_cb = work_cb;
197
+ work_request->after_work_cb = after_work_cb;
198
+ work_request->opcode = opcode;
199
+
200
+ if(uv_queue_work(&rrdeng_main.loop, &work_request->req, work_standard_worker, after_work_standard_callback)) {
201
+ internal_fatal(true, "DBENGINE: cannot queue work");
202
+ work_done(work_request);
203
+ return false;
204
}
103
- xt_cache_elem->prev = xt_cache_elem->next = NULL;
104
-}
205
106
-static inline void xt_cache_replaceQ_set_hot(struct rrdengine_worker_config* wc,
107
- struct extent_cache_element *xt_cache_elem)
108
-{
109
- xt_cache_replaceQ_delete(wc, xt_cache_elem);
110
- xt_cache_replaceQ_insert(wc, xt_cache_elem);
206
+ __atomic_add_fetch(&work_request_globals.atomics.dispatched, 1, __ATOMIC_RELAXED);
207
+
208
+ return true;
209
}
210
113
-/* Returns the index of the cached extent if it was successfully inserted in the extent cache, otherwise -1 */
114
-static int try_insert_into_xt_cache(struct rrdengine_worker_config* wc, struct extent_info *extent)
115
-{
116
- struct extent_cache *xt_cache = &wc->xt_cache;
117
- struct extent_cache_element *xt_cache_elem;
118
- unsigned idx;
119
- int ret;
211
+// ----------------------------------------------------------------------------
212
+// page descriptor cache
213
+
214
+static struct {
215
+ struct {
216
+ SPINLOCK spinlock;
217
+ struct page_descr_with_data *available_items;
218
+ size_t available;
219
+ } protected;
220
+
221
+ struct {
222
+ size_t allocated;
223
+ } atomics;
224
+} page_descriptor_globals = {
225
+ .protected = {
226
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
227
+ .available_items = NULL,
228
+ .available = 0,
229
+ },
230
+ .atomics = {
231
+ .allocated = 0,
232
+ },
233
+};
234
121
- ret = find_first_zero(xt_cache->allocation_bitmap);
122
- if (-1 == ret || ret >= MAX_CACHED_EXTENTS) {
123
- for (xt_cache_elem = xt_cache->replaceQ_head ; NULL != xt_cache_elem ; xt_cache_elem = xt_cache_elem->next) {
124
- idx = xt_cache_elem - xt_cache->extent_array;
125
- if (!check_bit(xt_cache->inflight_bitmap, idx)) {
126
- xt_cache_replaceQ_delete(wc, xt_cache_elem);
127
- break;
128
- }
129
- }
130
- if (NULL == xt_cache_elem)
131
- return -1;
132
- } else {
133
- idx = (unsigned)ret;
134
- xt_cache_elem = &xt_cache->extent_array[idx];
235
+static void page_descriptor_cleanup(void) {
236
+ netdata_spinlock_lock(&page_descriptor_globals.protected.spinlock);
237
+
238
+ while(page_descriptor_globals.protected.available_items && page_descriptor_globals.protected.available > MAX_PAGES_PER_EXTENT) {
239
+ struct page_descr_with_data *item = page_descriptor_globals.protected.available_items;
240
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(page_descriptor_globals.protected.available_items, item, cache.prev, cache.next);
241
+ freez(item);
242
+ page_descriptor_globals.protected.available--;
243
+ __atomic_sub_fetch(&page_descriptor_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
244
}
136
- xt_cache_elem->extent = extent;
137
- xt_cache_elem->fileno = extent->datafile->fileno;
138
- xt_cache_elem->inflight_io_descr = NULL;
139
- xt_cache_replaceQ_insert(wc, xt_cache_elem);
140
- modify_bit(&xt_cache->allocation_bitmap, idx, 1);
245
142
- return (int)idx;
246
+ netdata_spinlock_unlock(&page_descriptor_globals.protected.spinlock);
247
}
248
145
-/**
146
- * Returns 0 if the cached extent was found in the extent cache, 1 otherwise.
147
- * Sets *idx to point to the position of the extent inside the cache.
148
- **/
149
-static uint8_t lookup_in_xt_cache(struct rrdengine_worker_config* wc, struct extent_info *extent, unsigned *idx)
150
-{
151
- struct extent_cache *xt_cache = &wc->xt_cache;
152
- struct extent_cache_element *xt_cache_elem;
153
- unsigned i;
249
+struct page_descr_with_data *page_descriptor_get(void) {
250
+ struct page_descr_with_data *descr = NULL;
251
155
- for (i = 0 ; i < MAX_CACHED_EXTENTS ; ++i) {
156
- xt_cache_elem = &xt_cache->extent_array[i];
157
- if (check_bit(xt_cache->allocation_bitmap, i) && xt_cache_elem->extent == extent &&
158
- xt_cache_elem->fileno == extent->datafile->fileno) {
159
- *idx = i;
160
- return 0;
161
- }
252
+ netdata_spinlock_lock(&page_descriptor_globals.protected.spinlock);
253
+
254
+ if(likely(page_descriptor_globals.protected.available_items)) {
255
+ descr = page_descriptor_globals.protected.available_items;
256
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(page_descriptor_globals.protected.available_items, descr, cache.prev, cache.next);
257
+ page_descriptor_globals.protected.available--;
258
}
163
- return 1;
164
-}
259
166
-#if 0 /* disabled code */
167
-static void delete_from_xt_cache(struct rrdengine_worker_config* wc, unsigned idx)
168
-{
169
- struct extent_cache *xt_cache = &wc->xt_cache;
170
- struct extent_cache_element *xt_cache_elem;
171
-
172
- xt_cache_elem = &xt_cache->extent_array[idx];
173
- xt_cache_replaceQ_delete(wc, xt_cache_elem);
174
- xt_cache_elem->extent = NULL;
175
- modify_bit(&wc->xt_cache.allocation_bitmap, idx, 0); /* invalidate it */
176
- modify_bit(&wc->xt_cache.inflight_bitmap, idx, 0); /* not in-flight anymore */
260
+ netdata_spinlock_unlock(&page_descriptor_globals.protected.spinlock);
261
+
262
+ if(unlikely(!descr)) {
263
+ descr = mallocz(sizeof(struct page_descr_with_data));
264
+ __atomic_add_fetch(&page_descriptor_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
265
+ }
266
+
267
+ memset(descr, 0, sizeof(struct page_descr_with_data));
268
+ return descr;
269
}
178
-#endif
270
180
-void enqueue_inflight_read_to_xt_cache(struct rrdengine_worker_config* wc, unsigned idx,
181
- struct extent_io_descriptor *xt_io_descr)
182
-{
183
- struct extent_cache *xt_cache = &wc->xt_cache;
184
- struct extent_cache_element *xt_cache_elem;
185
- struct extent_io_descriptor *old_next;
186
-
187
- xt_cache_elem = &xt_cache->extent_array[idx];
188
- old_next = xt_cache_elem->inflight_io_descr->next;
189
- xt_cache_elem->inflight_io_descr->next = xt_io_descr;
190
- xt_io_descr->next = old_next;
271
+static inline void page_descriptor_release(struct page_descr_with_data *descr) {
272
+ if(unlikely(!descr)) return;
273
+
274
+ netdata_spinlock_lock(&page_descriptor_globals.protected.spinlock);
275
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(page_descriptor_globals.protected.available_items, descr, cache.prev, cache.next);
276
+ page_descriptor_globals.protected.available++;
277
+ netdata_spinlock_unlock(&page_descriptor_globals.protected.spinlock);
278
}
279
193
-void read_cached_extent_cb(struct rrdengine_worker_config* wc, unsigned idx, struct extent_io_descriptor *xt_io_descr)
194
-{
195
- unsigned i, j, page_offset;
196
- struct rrdengine_instance *ctx = wc->ctx;
197
- struct rrdeng_page_descr *descr;
198
- struct page_cache_descr *pg_cache_descr;
199
- void *page;
200
- struct extent_info *extent = xt_io_descr->descr_array[0]->extent;
201
-
202
- for (i = 0 ; i < xt_io_descr->descr_count; ++i) {
203
- page = dbengine_page_alloc();
204
- descr = xt_io_descr->descr_array[i];
205
- for (j = 0, page_offset = 0 ; j < extent->number_of_pages ; ++j) {
206
- /* care, we don't hold the descriptor mutex */
207
- if (!uuid_compare(*extent->pages[j]->id, *descr->id) &&
208
- extent->pages[j]->page_length == descr->page_length &&
209
- extent->pages[j]->start_time_ut == descr->start_time_ut &&
210
- extent->pages[j]->end_time_ut == descr->end_time_ut) {
211
- break;
212
- }
213
- page_offset += extent->pages[j]->page_length;
280
+// ----------------------------------------------------------------------------
281
+// extent io descriptor cache
282
+
283
+static struct {
284
+ struct {
285
+ SPINLOCK spinlock;
286
+ struct extent_io_descriptor *available_items;
287
+ size_t available;
288
+ } protected;
289
+
290
+ struct {
291
+ size_t allocated;
292
+ } atomics;
293
+
294
+} extent_io_descriptor_globals = {
295
+ .protected = {
296
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
297
+ .available_items = NULL,
298
+ .available = 0,
299
+ },
300
+ .atomics = {
301
+ .allocated = 0,
302
+ },
303
+};
304
215
- }
216
- /* care, we don't hold the descriptor mutex */
217
- (void) memcpy(page, wc->xt_cache.extent_array[idx].pages + page_offset, descr->page_length);
218
-
219
- rrdeng_page_descr_mutex_lock(ctx, descr);
220
- pg_cache_descr = descr->pg_cache_descr;
221
- pg_cache_descr->page = page;
222
- pg_cache_descr->flags |= RRD_PAGE_POPULATED;
223
- pg_cache_descr->flags &= ~RRD_PAGE_READ_PENDING;
224
- rrdeng_page_descr_mutex_unlock(ctx, descr);
225
- pg_cache_replaceQ_insert(ctx, descr);
226
- if (xt_io_descr->release_descr) {
227
- pg_cache_put(ctx, descr);
228
- } else {
229
- debug(D_RRDENGINE, "%s: Waking up waiters.", __func__);
230
- pg_cache_wake_up_waiters(ctx, descr);
231
- }
305
+static void extent_io_descriptor_cleanup(void) {
306
+ netdata_spinlock_lock(&extent_io_descriptor_globals.protected.spinlock);
307
+ while(extent_io_descriptor_globals.protected.available_items && extent_io_descriptor_globals.protected.available > (size_t)libuv_worker_threads) {
308
+ struct extent_io_descriptor *item = extent_io_descriptor_globals.protected.available_items;
309
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(extent_io_descriptor_globals.protected.available_items, item, cache.prev, cache.next);
310
+ freez(item);
311
+ extent_io_descriptor_globals.protected.available--;
312
+ __atomic_sub_fetch(&extent_io_descriptor_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
313
}
233
- if (xt_io_descr->completion)
234
- completion_mark_complete(xt_io_descr->completion);
235
- freez(xt_io_descr);
314
+ netdata_spinlock_unlock(&extent_io_descriptor_globals.protected.spinlock);
315
}
316
238
-static void fill_page_with_nulls(void *page, uint32_t page_length, uint8_t type) {
239
- switch(type) {
240
- case PAGE_METRICS: {
241
- storage_number n = pack_storage_number(NAN, SN_FLAG_NONE);
242
- storage_number *array = (storage_number *)page;
243
- size_t slots = page_length / sizeof(n);
244
- for(size_t i = 0; i < slots ; i++)
245
- array[i] = n;
246
- }
247
- break;
317
+static struct extent_io_descriptor *extent_io_descriptor_get(void) {
318
+ struct extent_io_descriptor *xt_io_descr = NULL;
319
249
- case PAGE_TIER: {
250
- storage_number_tier1_t n = {
251
- .min_value = NAN,
252
- .max_value = NAN,
253
- .sum_value = NAN,
254
- .count = 1,
255
- .anomaly_count = 0,
256
- };
257
- storage_number_tier1_t *array = (storage_number_tier1_t *)page;
258
- size_t slots = page_length / sizeof(n);
259
- for(size_t i = 0; i < slots ; i++)
260
- array[i] = n;
261
- }
262
- break;
320
+ netdata_spinlock_lock(&extent_io_descriptor_globals.protected.spinlock);
321
264
- default: {
265
- static bool logged = false;
266
- if(!logged) {
267
- error("DBENGINE: cannot fill page with nulls on unknown page type id %d", type);
268
- logged = true;
269
- }
270
- memset(page, 0, page_length);
271
- }
322
+ if(likely(extent_io_descriptor_globals.protected.available_items)) {
323
+ xt_io_descr = extent_io_descriptor_globals.protected.available_items;
324
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(extent_io_descriptor_globals.protected.available_items, xt_io_descr, cache.prev, cache.next);
325
+ extent_io_descriptor_globals.protected.available--;
326
}
327
+
328
+ netdata_spinlock_unlock(&extent_io_descriptor_globals.protected.spinlock);
329
+
330
+ if(unlikely(!xt_io_descr)) {
331
+ xt_io_descr = mallocz(sizeof(struct extent_io_descriptor));
332
+ __atomic_add_fetch(&extent_io_descriptor_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
333
+ }
334
+
335
+ memset(xt_io_descr, 0, sizeof(struct extent_io_descriptor));
336
+ return xt_io_descr;
337
}
338
275
-struct rrdeng_page_descr *get_descriptor(struct pg_cache_page_index *page_index, time_t start_time_s)
276
-{
277
- uv_rwlock_rdlock(&page_index->lock);
278
- Pvoid_t *PValue = JudyLGet(page_index->JudyL_array, start_time_s, PJE0);
279
- struct rrdeng_page_descr *descr = unlikely(NULL == PValue) ? NULL : *PValue;
280
- uv_rwlock_rdunlock(&page_index->lock);
281
- return descr;
282
-};
339
+static inline void extent_io_descriptor_release(struct extent_io_descriptor *xt_io_descr) {
340
+ if(unlikely(!xt_io_descr)) return;
341
284
-static void do_extent_processing (struct rrdengine_worker_config *wc, struct extent_io_descriptor *xt_io_descr, bool read_failed)
285
-{
286
- struct rrdengine_instance *ctx = wc->ctx;
287
- struct page_cache *pg_cache = &ctx->pg_cache;
288
- struct rrdeng_page_descr *descr;
289
- struct page_cache_descr *pg_cache_descr;
290
- int ret;
291
- unsigned i, j, count;
292
- void *page, *uncompressed_buf = NULL;
293
- uint32_t payload_length, payload_offset, page_offset, uncompressed_payload_length = 0;
294
- uint8_t have_read_error = 0;
295
- /* persistent structures */
296
- struct rrdeng_df_extent_header *header;
297
- struct rrdeng_df_extent_trailer *trailer;
298
- uLong crc;
342
+ netdata_spinlock_lock(&extent_io_descriptor_globals.protected.spinlock);
343
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(extent_io_descriptor_globals.protected.available_items, xt_io_descr, cache.prev, cache.next);
344
+ extent_io_descriptor_globals.protected.available++;
345
+ netdata_spinlock_unlock(&extent_io_descriptor_globals.protected.spinlock);
346
+}
347
300
- header = xt_io_descr->buf;
301
- payload_length = header->payload_length;
302
- count = header->number_of_pages;
303
- payload_offset = sizeof(*header) + sizeof(header->descr[0]) * count;
304
- trailer = xt_io_descr->buf + xt_io_descr->bytes - sizeof(*trailer);
348
+// ----------------------------------------------------------------------------
349
+// query handle cache
350
+
351
+static struct {
352
+ struct {
353
+ SPINLOCK spinlock;
354
+ struct rrdeng_query_handle *available_items;
355
+ size_t available;
356
+ } protected;
357
+
358
+ struct {
359
+ size_t allocated;
360
+ } atomics;
361
+} rrdeng_query_handle_globals = {
362
+ .protected = {
363
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
364
+ .available_items = NULL,
365
+ .available = 0,
366
+ },
367
+ .atomics = {
368
+ .allocated = 0,
369
+ },
370
+};
371
306
- if (unlikely(read_failed)) {
307
- struct rrdengine_datafile *datafile = xt_io_descr->descr_array[0]->extent->datafile;
372
+static void rrdeng_query_handle_cleanup(void) {
373
+ netdata_spinlock_lock(&rrdeng_query_handle_globals.protected.spinlock);
374
309
- ++ctx->stats.io_errors;
310
- rrd_stat_atomic_add(&global_io_errors, 1);
311
- have_read_error = 1;
312
- error("%s: uv_fs_read - extent at offset %"PRIu64"(%u) in datafile %u-%u.", __func__, xt_io_descr->pos,
313
- xt_io_descr->bytes, datafile->tier, datafile->fileno);
314
- goto after_crc_check;
315
- }
316
- crc = crc32(0L, Z_NULL, 0);
317
- crc = crc32(crc, xt_io_descr->buf, xt_io_descr->bytes - sizeof(*trailer));
318
- ret = crc32cmp(trailer->checksum, crc);
319
-#ifdef NETDATA_INTERNAL_CHECKS
320
- {
321
- struct rrdengine_datafile *datafile = xt_io_descr->descr_array[0]->extent->datafile;
322
- debug(D_RRDENGINE, "%s: Extent at offset %"PRIu64"(%u) was read from datafile %u-%u. CRC32 check: %s", __func__,
323
- xt_io_descr->pos, xt_io_descr->bytes, datafile->tier, datafile->fileno, ret ? "FAILED" : "SUCCEEDED");
375
+ while(rrdeng_query_handle_globals.protected.available_items && rrdeng_query_handle_globals.protected.available > 10) {
376
+ struct rrdeng_query_handle *item = rrdeng_query_handle_globals.protected.available_items;
377
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(rrdeng_query_handle_globals.protected.available_items, item, cache.prev, cache.next);
378
+ freez(item);
379
+ rrdeng_query_handle_globals.protected.available--;
380
+ __atomic_sub_fetch(&rrdeng_query_handle_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
381
}
325
-#endif
326
- if (unlikely(ret)) {
327
- struct rrdengine_datafile *datafile = xt_io_descr->descr_array[0]->extent->datafile;
382
329
- ++ctx->stats.io_errors;
330
- rrd_stat_atomic_add(&global_io_errors, 1);
331
- have_read_error = 1;
332
- error("%s: Extent at offset %"PRIu64"(%u) was read from datafile %u-%u. CRC32 check: FAILED", __func__,
333
- xt_io_descr->pos, xt_io_descr->bytes, datafile->tier, datafile->fileno);
383
+ netdata_spinlock_unlock(&rrdeng_query_handle_globals.protected.spinlock);
384
+}
385
+
386
+struct rrdeng_query_handle *rrdeng_query_handle_get(void) {
387
+ struct rrdeng_query_handle *handle = NULL;
388
+
389
+ netdata_spinlock_lock(&rrdeng_query_handle_globals.protected.spinlock);
390
+
391
+ if(likely(rrdeng_query_handle_globals.protected.available_items)) {
392
+ handle = rrdeng_query_handle_globals.protected.available_items;
393
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(rrdeng_query_handle_globals.protected.available_items, handle, cache.prev, cache.next);
394
+ rrdeng_query_handle_globals.protected.available--;
395
}
396
336
-after_crc_check:
337
- if (!have_read_error && RRD_NO_COMPRESSION != header->compression_algorithm) {
338
- uncompressed_payload_length = 0;
339
- for (i = 0 ; i < count ; ++i) {
340
- uncompressed_payload_length += header->descr[i].page_length;
341
- }
342
- uncompressed_buf = mallocz(uncompressed_payload_length);
343
- ret = LZ4_decompress_safe(xt_io_descr->buf + payload_offset, uncompressed_buf,
344
- payload_length, uncompressed_payload_length);
345
- ctx->stats.before_decompress_bytes += payload_length;
346
- ctx->stats.after_decompress_bytes += ret;
347
- debug(D_RRDENGINE, "LZ4 decompressed %u bytes to %d bytes.", payload_length, ret);
348
- /* care, we don't hold the descriptor mutex */
397
+ netdata_spinlock_unlock(&rrdeng_query_handle_globals.protected.spinlock);
398
+
399
+ if(unlikely(!handle)) {
400
+ handle = mallocz(sizeof(struct rrdeng_query_handle));
401
+ __atomic_add_fetch(&rrdeng_query_handle_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
402
}
350
- {
351
- uint8_t xt_is_cached = 0;
352
- unsigned xt_idx;
353
- struct extent_info *extent = xt_io_descr->descr_array[0]->extent;
354
-
355
- xt_is_cached = !lookup_in_xt_cache(wc, extent, &xt_idx);
356
- if (xt_is_cached && check_bit(wc->xt_cache.inflight_bitmap, xt_idx)) {
357
- struct extent_cache *xt_cache = &wc->xt_cache;
358
- struct extent_cache_element *xt_cache_elem = &xt_cache->extent_array[xt_idx];
359
- struct extent_io_descriptor *curr, *next;
360
-
361
- if (have_read_error) {
362
- memset(xt_cache_elem->pages, 0, sizeof(xt_cache_elem->pages));
363
- } else if (RRD_NO_COMPRESSION == header->compression_algorithm) {
364
- (void)memcpy(xt_cache_elem->pages, xt_io_descr->buf + payload_offset, payload_length);
365
- } else {
366
- (void)memcpy(xt_cache_elem->pages, uncompressed_buf, uncompressed_payload_length);
367
- }
368
- /* complete all connected in-flight read requests */
369
- for (curr = xt_cache_elem->inflight_io_descr->next ; curr ; curr = next) {
370
- next = curr->next;
371
- read_cached_extent_cb(wc, xt_idx, curr);
372
- }
373
- xt_cache_elem->inflight_io_descr = NULL;
374
- modify_bit(&xt_cache->inflight_bitmap, xt_idx, 0); /* not in-flight anymore */
375
- }
403
+
404
+ memset(handle, 0, sizeof(struct rrdeng_query_handle));
405
+ return handle;
406
+}
407
+
408
+void rrdeng_query_handle_release(struct rrdeng_query_handle *handle) {
409
+ if(unlikely(!handle)) return;
410
+
411
+ netdata_spinlock_lock(&rrdeng_query_handle_globals.protected.spinlock);
412
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(rrdeng_query_handle_globals.protected.available_items, handle, cache.prev, cache.next);
413
+ rrdeng_query_handle_globals.protected.available++;
414
+ netdata_spinlock_unlock(&rrdeng_query_handle_globals.protected.spinlock);
415
+}
416
+
417
+// ----------------------------------------------------------------------------
418
+// WAL cache
419
+
420
+static struct {
421
+ struct {
422
+ SPINLOCK spinlock;
423
+ WAL *available_items;
424
+ size_t available;
425
+ } protected;
426
+
427
+ struct {
428
+ size_t allocated;
429
+ } atomics;
430
+} wal_globals = {
431
+ .protected = {
432
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
433
+ .available_items = NULL,
434
+ .available = 0,
435
+ },
436
+ .atomics = {
437
+ .allocated = 0,
438
+ },
439
+};
440
+
441
+static void wal_cleanup(void) {
442
+ netdata_spinlock_lock(&wal_globals.protected.spinlock);
443
+
444
+ while(wal_globals.protected.available_items && wal_globals.protected.available > storage_tiers) {
445
+ WAL *wal = wal_globals.protected.available_items;
446
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(wal_globals.protected.available_items, wal, cache.prev, cache.next);
447
+ posix_memfree(wal->buf);
448
+ freez(wal);
449
+ wal_globals.protected.available--;
450
+ __atomic_sub_fetch(&wal_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
451
}
452
378
- uv_rwlock_rdlock(&pg_cache->metrics_index.lock);
379
- Pvoid_t *PValue = JudyHSGet(pg_cache->metrics_index.JudyHS_array, xt_io_descr->descr_array[0]->id, sizeof(uuid_t));
380
- struct pg_cache_page_index *page_index = likely( NULL != PValue) ? *PValue : NULL;
381
- uv_rwlock_rdunlock(&pg_cache->metrics_index.lock);
382
-
383
-
384
- for (i = 0, page_offset = 0; i < count; page_offset += header->descr[i++].page_length) {
385
- uint8_t is_prefetched_page;
386
- descr = NULL;
387
- for (j = 0 ; j < xt_io_descr->descr_count; ++j) {
388
- struct rrdeng_page_descr descrj;
389
-
390
- descrj = xt_io_descr->descr_read_array[j];
391
- /* care, we don't hold the descriptor mutex */
392
- if (!uuid_compare(*(uuid_t *) header->descr[i].uuid, *descrj.id) &&
393
- header->descr[i].page_length == descrj.page_length &&
394
- header->descr[i].start_time_ut == descrj.start_time_ut &&
395
- header->descr[i].end_time_ut == descrj.end_time_ut) {
396
- //descr = descrj;
397
- descr = get_descriptor(page_index, (time_t) (descrj.start_time_ut / USEC_PER_SEC));
398
- if (unlikely(!descr)) {
399
- error_limit_static_thread_var(erl, 1, 0);
400
- error_limit(&erl, "%s: Required descriptor is not in the page index anymore", __FUNCTION__);
401
- }
402
- break;
403
- }
404
- }
405
- is_prefetched_page = 0;
406
- if (!descr) { /* This extent page has not been requested. Try populating it for locality (best effort). */
407
- descr = pg_cache_lookup_unpopulated_and_lock(ctx, (uuid_t *)header->descr[i].uuid,
408
- header->descr[i].start_time_ut);
409
- if (!descr)
410
- continue; /* Failed to reserve a suitable page */
411
- is_prefetched_page = 1;
412
- }
413
- page = dbengine_page_alloc();
414
-
415
- /* care, we don't hold the descriptor mutex */
416
- if (have_read_error) {
417
- fill_page_with_nulls(page, descr->page_length, descr->type);
418
- } else if (RRD_NO_COMPRESSION == header->compression_algorithm) {
419
- (void) memcpy(page, xt_io_descr->buf + payload_offset + page_offset, descr->page_length);
420
- } else {
421
- (void) memcpy(page, uncompressed_buf + page_offset, descr->page_length);
422
- }
423
- rrdeng_page_descr_mutex_lock(ctx, descr);
424
- pg_cache_descr = descr->pg_cache_descr;
425
- pg_cache_descr->page = page;
426
- pg_cache_descr->flags |= RRD_PAGE_POPULATED;
427
- pg_cache_descr->flags &= ~RRD_PAGE_READ_PENDING;
428
- rrdeng_page_descr_mutex_unlock(ctx, descr);
429
- pg_cache_replaceQ_insert(ctx, descr);
430
- if (xt_io_descr->release_descr || is_prefetched_page) {
431
- pg_cache_put(ctx, descr);
432
- } else {
433
- debug(D_RRDENGINE, "%s: Waking up waiters.", __func__);
434
- pg_cache_wake_up_waiters(ctx, descr);
435
- }
453
+ netdata_spinlock_unlock(&wal_globals.protected.spinlock);
454
+}
455
+
456
+WAL *wal_get(struct rrdengine_instance *ctx, unsigned size) {
457
+ if(!size || size > RRDENG_BLOCK_SIZE)
458
+ fatal("DBENGINE: invalid WAL size requested");
459
+
460
+ WAL *wal = NULL;
461
+
462
+ netdata_spinlock_lock(&wal_globals.protected.spinlock);
463
+
464
+ if(likely(wal_globals.protected.available_items)) {
465
+ wal = wal_globals.protected.available_items;
466
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(wal_globals.protected.available_items, wal, cache.prev, cache.next);
467
+ wal_globals.protected.available--;
468
}
437
- if (!have_read_error && RRD_NO_COMPRESSION != header->compression_algorithm) {
438
- freez(uncompressed_buf);
469
+
470
+ uint64_t transaction_id = ctx->commit_log.transaction_id++;
471
+ netdata_spinlock_unlock(&wal_globals.protected.spinlock);
472
+
473
+ if(unlikely(!wal)) {
474
+ wal = mallocz(sizeof(WAL));
475
+ wal->buf_size = RRDENG_BLOCK_SIZE;
476
+ int ret = posix_memalign((void *)&wal->buf, RRDFILE_ALIGNMENT, wal->buf_size);
477
+ if (unlikely(ret))
478
+ fatal("DBENGINE: posix_memalign:%s", strerror(ret));
479
+ __atomic_add_fetch(&wal_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
480
}
440
- if (xt_io_descr->completion)
441
- completion_mark_complete(xt_io_descr->completion);
481
+
482
+ // these need to survive
483
+ unsigned buf_size = wal->buf_size;
484
+ void *buf = wal->buf;
485
+
486
+ memset(wal, 0, sizeof(WAL));
487
+
488
+ // put them back
489
+ wal->buf_size = buf_size;
490
+ wal->buf = buf;
491
+
492
+ memset(wal->buf, 0, wal->buf_size);
493
+
494
+ wal->transaction_id = transaction_id;
495
+ wal->size = size;
496
+
497
+ return wal;
498
}
499
444
-static void read_extent_cb(uv_fs_t *req)
445
-{
446
- struct rrdengine_worker_config *wc = req->loop->data;
447
- struct extent_io_descriptor *xt_io_descr;
500
+void wal_release(WAL *wal) {
501
+ if(unlikely(!wal)) return;
502
449
- xt_io_descr = req->data;
450
- do_extent_processing(wc, xt_io_descr, req->result < 0);
451
- uv_fs_req_cleanup(req);
452
- posix_memfree(xt_io_descr->buf);
453
- freez(xt_io_descr);
503
+ netdata_spinlock_lock(&wal_globals.protected.spinlock);
504
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(wal_globals.protected.available_items, wal, cache.prev, cache.next);
505
+ wal_globals.protected.available++;
506
+ netdata_spinlock_unlock(&wal_globals.protected.spinlock);
507
}
508
456
-static void read_mmap_extent_cb(uv_work_t *req, int status __maybe_unused)
457
-{
458
- struct rrdengine_worker_config *wc = req->loop->data;
459
- struct rrdengine_instance *ctx = wc->ctx;
460
- struct extent_io_descriptor *xt_io_descr;
461
- xt_io_descr = req->data;
509
+// ----------------------------------------------------------------------------
510
+// command queue cache
511
463
- if (likely(xt_io_descr->map_base)) {
464
- do_extent_processing(wc, xt_io_descr, false);
465
- munmap(xt_io_descr->map_base, xt_io_descr->map_length);
466
- freez(xt_io_descr);
467
- return;
468
- }
512
+struct rrdeng_cmd {
513
+ struct rrdengine_instance *ctx;
514
+ enum rrdeng_opcode opcode;
515
+ void *data;
516
+ struct completion *completion;
517
+ enum storage_priority priority;
518
+
519
+ struct {
520
+ struct rrdeng_cmd *prev;
521
+ struct rrdeng_cmd *next;
522
+ } cache;
523
+};
524
470
- // MMAP failed, so do uv_fs_read
471
- int ret = posix_memalign((void *)&xt_io_descr->buf, RRDFILE_ALIGNMENT, ALIGN_BYTES_CEILING(xt_io_descr->bytes));
472
- if (unlikely(ret)) {
473
- fatal("posix_memalign:%s", strerror(ret));
525
+static struct {
526
+ struct {
527
+ SPINLOCK spinlock;
528
+ struct rrdeng_cmd *available_items;
529
+ size_t available;
530
+
531
+ struct {
532
+ size_t allocated;
533
+ } atomics;
534
+ } cache;
535
+
536
+ struct {
537
+ SPINLOCK spinlock;
538
+ size_t waiting;
539
+ struct rrdeng_cmd *waiting_items_by_priority[STORAGE_PRIO_MAX_DONT_USE];
540
+ size_t executed_by_priority[STORAGE_PRIO_MAX_DONT_USE];
541
+ } queue;
542
+
543
+
544
+} rrdeng_cmd_globals = {
545
+ .cache = {
546
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
547
+ .available_items = NULL,
548
+ .available = 0,
549
+ .atomics = {
550
+ .allocated = 0,
551
+ },
552
+ },
553
+ .queue = {
554
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
555
+ .waiting = 0,
556
+ },
557
+};
558
+
559
+static void rrdeng_cmd_cleanup(void) {
560
+ netdata_spinlock_lock(&rrdeng_cmd_globals.cache.spinlock);
561
+ while(rrdeng_cmd_globals.cache.available_items && rrdeng_cmd_globals.cache.available > 100) {
562
+ struct rrdeng_cmd *item = rrdeng_cmd_globals.cache.available_items;
563
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(rrdeng_cmd_globals.cache.available_items, item, cache.prev, cache.next);
564
+ freez(item);
565
+ rrdeng_cmd_globals.cache.available--;
566
+ __atomic_sub_fetch(&rrdeng_cmd_globals.cache.atomics.allocated, 1, __ATOMIC_RELAXED);
567
}
475
- unsigned real_io_size = ALIGN_BYTES_CEILING( xt_io_descr->bytes);
476
- xt_io_descr->iov = uv_buf_init((void *)xt_io_descr->buf, real_io_size);
477
- xt_io_descr->req.data = xt_io_descr;
478
- ret = uv_fs_read(req->loop, &xt_io_descr->req, xt_io_descr->file, &xt_io_descr->iov, 1, (unsigned) xt_io_descr->pos, read_extent_cb);
479
- fatal_assert(-1 != ret);
480
- ctx->stats.io_read_bytes += real_io_size;
481
- ctx->stats.io_read_extent_bytes += real_io_size;
568
+ netdata_spinlock_unlock(&rrdeng_cmd_globals.cache.spinlock);
569
}
570
484
-static void do_mmap_read_extent(uv_work_t *req)
485
-{
486
- struct extent_io_descriptor *xt_io_descr = (struct extent_io_descriptor * )req->data;
487
- struct rrdengine_worker_config *wc = req->loop->data;
488
- struct rrdengine_instance *ctx = wc->ctx;
489
-
490
- off_t map_start = ALIGN_BYTES_FLOOR(xt_io_descr->pos);
491
- size_t length = ALIGN_BYTES_CEILING(xt_io_descr->pos + xt_io_descr->bytes) - map_start;
492
- unsigned real_io_size = xt_io_descr->bytes;
493
-
494
- void *data = mmap(NULL, length, PROT_READ, MAP_SHARED, xt_io_descr->file, map_start);
495
- if (likely(data != MAP_FAILED)) {
496
- xt_io_descr->map_base = data;
497
- xt_io_descr->map_length = length;
498
- xt_io_descr->buf = data + (xt_io_descr->pos - map_start);
499
- ctx->stats.io_read_bytes += real_io_size;
500
- ctx->stats.io_read_extent_bytes += real_io_size;
571
+void rrdeng_enq_cmd(struct rrdengine_instance *ctx, enum rrdeng_opcode opcode, void *data, struct completion *completion, STORAGE_PRIORITY priority) {
572
+ struct rrdeng_cmd *cmd = NULL;
573
+
574
+ if(unlikely(priority >= STORAGE_PRIO_MAX_DONT_USE))
575
+ priority = STORAGE_PRIORITY_NORMAL;
576
+
577
+ netdata_spinlock_lock(&rrdeng_cmd_globals.cache.spinlock);
578
+ if(likely(rrdeng_cmd_globals.cache.available_items)) {
579
+ cmd = rrdeng_cmd_globals.cache.available_items;
580
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(rrdeng_cmd_globals.cache.available_items, cmd, cache.prev, cache.next);
581
+ rrdeng_cmd_globals.cache.available--;
582
}
502
-}
583
+ netdata_spinlock_unlock(&rrdeng_cmd_globals.cache.spinlock);
584
504
-static void do_read_extent(struct rrdengine_worker_config* wc,
505
- struct rrdeng_page_descr **descr,
506
- unsigned count,
507
- uint8_t release_descr)
508
-{
509
- struct rrdengine_instance *ctx = wc->ctx;
510
- struct page_cache_descr *pg_cache_descr;
511
- int ret;
512
- unsigned i, size_bytes, pos;
513
- struct extent_io_descriptor *xt_io_descr;
514
- struct rrdengine_datafile *datafile;
515
- struct extent_info *extent = descr[0]->extent;
516
- uint8_t xt_is_cached = 0, xt_is_inflight = 0;
517
- unsigned xt_idx;
518
-
519
- datafile = extent->datafile;
520
- pos = extent->offset;
521
- size_bytes = extent->size;
522
-
523
- xt_io_descr = callocz(1, sizeof(*xt_io_descr));
524
- for (i = 0 ; i < count; ++i) {
525
- rrdeng_page_descr_mutex_lock(ctx, descr[i]);
526
- pg_cache_descr = descr[i]->pg_cache_descr;
527
- pg_cache_descr->flags |= RRD_PAGE_READ_PENDING;
528
- rrdeng_page_descr_mutex_unlock(ctx, descr[i]);
529
- xt_io_descr->descr_array[i] = descr[i];
530
- xt_io_descr->descr_read_array[i] = *(descr[i]);
585
+ if(unlikely(!cmd)) {
586
+ cmd = mallocz(sizeof(struct rrdeng_cmd));
587
+ __atomic_add_fetch(&rrdeng_cmd_globals.cache.atomics.allocated, 1, __ATOMIC_RELAXED);
588
}
532
- xt_io_descr->descr_count = count;
533
- xt_io_descr->file = datafile->file;
534
- xt_io_descr->bytes = size_bytes;
535
- xt_io_descr->pos = pos;
536
- xt_io_descr->req_worker.data = xt_io_descr;
537
- xt_io_descr->completion = NULL;
538
- xt_io_descr->release_descr = release_descr;
539
- xt_io_descr->buf = NULL;
540
-
541
- xt_is_cached = !lookup_in_xt_cache(wc, extent, &xt_idx);
542
- if (xt_is_cached) {
543
- xt_cache_replaceQ_set_hot(wc, &wc->xt_cache.extent_array[xt_idx]);
544
- xt_is_inflight = check_bit(wc->xt_cache.inflight_bitmap, xt_idx);
545
- if (xt_is_inflight) {
546
- enqueue_inflight_read_to_xt_cache(wc, xt_idx, xt_io_descr);
547
- return;
548
- }
549
- return read_cached_extent_cb(wc, xt_idx, xt_io_descr);
550
- } else {
551
- ret = try_insert_into_xt_cache(wc, extent);
552
- if (-1 != ret) {
553
- xt_idx = (unsigned)ret;
554
- modify_bit(&wc->xt_cache.inflight_bitmap, xt_idx, 1);
555
- wc->xt_cache.extent_array[xt_idx].inflight_io_descr = xt_io_descr;
589
+
590
+ memset(cmd, 0, sizeof(struct rrdeng_cmd));
591
+ cmd->ctx = ctx;
592
+ cmd->opcode = opcode;
593
+ cmd->data = data;
594
+ cmd->completion = completion;
595
+ cmd->priority = priority;
596
+
597
+ netdata_spinlock_lock(&rrdeng_cmd_globals.queue.spinlock);
598
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(rrdeng_cmd_globals.queue.waiting_items_by_priority[priority], cmd, cache.prev, cache.next);
599
+ rrdeng_cmd_globals.queue.waiting++;
600
+ netdata_spinlock_unlock(&rrdeng_cmd_globals.queue.spinlock);
601
+
602
+ fatal_assert(0 == uv_async_send(&rrdeng_main.async));
603
+}
604
+
605
+static inline bool rrdeng_cmd_has_waiting_opcodes_in_lower_priorities(STORAGE_PRIORITY priority, STORAGE_PRIORITY max_priority) {
606
+ for(; priority <= max_priority ; priority++)
607
+ if(rrdeng_cmd_globals.queue.waiting_items_by_priority[priority])
608
+ return true;
609
+
610
+ return false;
611
+}
612
+
613
+static inline struct rrdeng_cmd rrdeng_deq_cmd(void) {
614
+ struct rrdeng_cmd *cmd = NULL;
615
+
616
+ STORAGE_PRIORITY max_priority = work_request_full() ? STORAGE_PRIORITY_CRITICAL : STORAGE_PRIORITY_BEST_EFFORT;
617
+
618
+ // find an opcode to execute from the queue
619
+ netdata_spinlock_lock(&rrdeng_cmd_globals.queue.spinlock);
620
+ for(STORAGE_PRIORITY priority = STORAGE_PRIORITY_CRITICAL; priority <= max_priority ; priority++) {
621
+ cmd = rrdeng_cmd_globals.queue.waiting_items_by_priority[priority];
622
+ if(cmd) {
623
+
624
+ // avoid starvation of lower priorities
625
+ if(unlikely(priority > STORAGE_PRIORITY_CRITICAL &&
626
+ priority < STORAGE_PRIORITY_BEST_EFFORT &&
627
+ ++rrdeng_cmd_globals.queue.executed_by_priority[priority] % 50 == 0 &&
628
+ rrdeng_cmd_has_waiting_opcodes_in_lower_priorities(priority + 1, max_priority))) {
629
+ // let the others run 2% of the requests
630
+ cmd = NULL;
631
+ continue;
632
+ }
633
+
634
+ // remove it from the queue
635
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(rrdeng_cmd_globals.queue.waiting_items_by_priority[priority], cmd, cache.prev, cache.next);
636
+ rrdeng_cmd_globals.queue.waiting--;
637
+ break;
638
}
639
}
640
+ netdata_spinlock_unlock(&rrdeng_cmd_globals.queue.spinlock);
641
559
- ret = uv_queue_work(wc->loop, &xt_io_descr->req_worker, do_mmap_read_extent, read_mmap_extent_cb);
560
- fatal_assert(-1 != ret);
642
+ struct rrdeng_cmd ret;
643
+ if(cmd) {
644
+ // copy it, to return it
645
+ ret = *cmd;
646
562
- ++ctx->stats.io_read_requests;
563
- ++ctx->stats.io_read_extents;
564
- ctx->stats.pg_cache_backfills += count;
647
+ // put it in the cache
648
+ netdata_spinlock_lock(&rrdeng_cmd_globals.cache.spinlock);
649
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(rrdeng_cmd_globals.cache.available_items, cmd, cache.prev, cache.next);
650
+ rrdeng_cmd_globals.cache.available++;
651
+ netdata_spinlock_unlock(&rrdeng_cmd_globals.cache.spinlock);
652
+ }
653
+ else
654
+ ret = (struct rrdeng_cmd) {
655
+ .ctx = NULL,
656
+ .opcode = RRDENG_OPCODE_NOOP,
657
+ .priority = STORAGE_PRIORITY_BEST_EFFORT,
658
+ .completion = NULL,
659
+ .data = NULL,
660
+ };
661
+
662
+ return ret;
663
}
664
567
-static void commit_data_extent(struct rrdengine_worker_config* wc, struct extent_io_descriptor *xt_io_descr)
568
-{
569
- struct rrdengine_instance *ctx = wc->ctx;
665
+
666
+// ----------------------------------------------------------------------------
667
+
668
+void *dbengine_page_alloc(struct rrdengine_instance *ctx __maybe_unused, size_t size) {
669
+ void *page = mallocz(size);
670
+ return page;
671
+}
672
+
673
+void dbengine_page_free(void *page) {
674
+ freez(page);
675
+}
676
+
677
+static void commit_data_extent(struct rrdengine_instance *ctx, struct extent_io_descriptor *xt_io_descr) {
678
unsigned count, payload_length, descr_size, size_bytes;
679
void *buf;
680
/* persistent structures */
690
payload_length = sizeof(*jf_metric_data) + descr_size;
691
size_bytes = sizeof(*jf_header) + payload_length + sizeof(*jf_trailer);
692
585
- buf = wal_get_transaction_buffer(wc, size_bytes);
693
+ xt_io_descr->wal = wal_get(ctx, size_bytes);
694
+ buf = xt_io_descr->wal->buf;
695
696
jf_header = buf;
697
jf_header->type = STORE_DATA;
698
jf_header->reserved = 0;
590
- jf_header->id = ctx->commit_log.transaction_id++;
699
+ jf_header->id = xt_io_descr->wal->transaction_id;
700
jf_header->payload_length = payload_length;
701
702
jf_metric_data = buf + sizeof(*jf_header);
711
crc32set(jf_trailer->checksum, crc);
712
}
713
605
-static void do_commit_transaction(struct rrdengine_worker_config* wc, uint8_t type, void *data)
606
-{
607
- switch (type) {
608
- case STORE_DATA:
609
- commit_data_extent(wc, (struct extent_io_descriptor *)data);
610
- break;
611
- default:
612
- fatal_assert(type == STORE_DATA);
613
- break;
614
- }
615
-}
616
-
617
-static void after_invalidate_oldest_committed(struct rrdengine_worker_config* wc)
618
-{
619
- int error;
714
+static void after_extent_flushed_to_open(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
715
+ if(completion)
716
+ completion_mark_complete(completion);
717
621
- error = uv_thread_join(wc->now_invalidating_dirty_pages);
622
- if (error) {
623
- error("uv_thread_join(): %s", uv_strerror(error));
624
- }
625
- freez(wc->now_invalidating_dirty_pages);
626
- wc->now_invalidating_dirty_pages = NULL;
627
- wc->cleanup_thread_invalidating_dirty_pages = 0;
718
+ if(ctx_is_available_for_queries(ctx))
719
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_CRITICAL);
720
}
721
630
-static void invalidate_oldest_committed(void *arg)
631
-{
632
- struct rrdengine_instance *ctx = arg;
633
- struct rrdengine_worker_config *wc = &ctx->worker_config;
634
- struct page_cache *pg_cache = &ctx->pg_cache;
635
- int ret;
636
- struct rrdeng_page_descr *descr;
637
- struct page_cache_descr *pg_cache_descr;
638
- Pvoid_t *PValue;
639
- Word_t Index;
640
- unsigned nr_committed_pages;
641
-
642
- do {
643
- uv_rwlock_wrlock(&pg_cache->committed_page_index.lock);
644
- for (Index = 0,
645
- PValue = JudyLFirst(pg_cache->committed_page_index.JudyL_array, &Index, PJE0),
646
- descr = unlikely(NULL == PValue) ? NULL : *PValue;
722
+static void extent_flushed_to_open_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
723
+ worker_is_busy(UV_EVENT_FLUSHED_TO_OPEN);
724
648
- descr != NULL;
649
-
650
- PValue = JudyLNext(pg_cache->committed_page_index.JudyL_array, &Index, PJE0),
651
- descr = unlikely(NULL == PValue) ? NULL : *PValue) {
652
- fatal_assert(0 != descr->page_length);
725
+ uv_fs_t *uv_fs_request = data;
726
+ struct extent_io_descriptor *xt_io_descr = uv_fs_request->data;
727
+ struct page_descr_with_data *descr;
728
+ struct rrdengine_datafile *datafile;
729
+ unsigned i;
730
654
- rrdeng_page_descr_mutex_lock(ctx, descr);
655
- pg_cache_descr = descr->pg_cache_descr;
656
- if (!(pg_cache_descr->flags & RRD_PAGE_WRITE_PENDING) && pg_cache_try_get_unsafe(descr, 1)) {
657
- rrdeng_page_descr_mutex_unlock(ctx, descr);
731
+ if (uv_fs_request->result < 0) {
732
+ __atomic_add_fetch(&ctx->stats.io_errors, 1, __ATOMIC_RELAXED);
733
+ rrd_stat_atomic_add(&global_io_errors, 1);
734
+ error("DBENGINE: %s: uv_fs_write: %s", __func__, uv_strerror((int)uv_fs_request->result));
735
+ }
736
+ datafile = xt_io_descr->datafile;
737
659
- ret = JudyLDel(&pg_cache->committed_page_index.JudyL_array, Index, PJE0);
660
- fatal_assert(1 == ret);
661
- break;
662
- }
663
- rrdeng_page_descr_mutex_unlock(ctx, descr);
664
- }
665
- uv_rwlock_wrunlock(&pg_cache->committed_page_index.lock);
738
+ bool still_running = ctx_is_available_for_queries(ctx);
739
667
- if (!descr) {
668
- info("Failed to invalidate any dirty pages to relieve page cache pressure.");
740
+ for (i = 0 ; i < xt_io_descr->descr_count ; ++i) {
741
+ descr = xt_io_descr->descr_array[i];
742
670
- goto out;
671
- }
672
- pg_cache_punch_hole(ctx, descr, 1, 1, NULL);
673
-
674
- uv_rwlock_wrlock(&pg_cache->committed_page_index.lock);
675
- nr_committed_pages = --pg_cache->committed_page_index.nr_committed_pages;
676
- uv_rwlock_wrunlock(&pg_cache->committed_page_index.lock);
677
- rrd_stat_atomic_add(&ctx->stats.flushing_pressure_page_deletions, 1);
678
- rrd_stat_atomic_add(&global_flushing_pressure_page_deletions, 1);
679
-
680
- } while (nr_committed_pages >= pg_cache_committed_hard_limit(ctx));
681
-out:
682
- wc->cleanup_thread_invalidating_dirty_pages = 1;
683
- /* wake up event loop */
684
- fatal_assert(0 == uv_async_send(&wc->async));
685
-}
743
+ if (likely(still_running))
744
+ pgc_open_add_hot_page(
745
+ (Word_t)ctx, descr->metric_id,
746
+ (time_t) (descr->start_time_ut / USEC_PER_SEC),
747
+ (time_t) (descr->end_time_ut / USEC_PER_SEC),
748
+ descr->update_every_s,
749
+ datafile,
750
+ xt_io_descr->pos, xt_io_descr->bytes, descr->page_length);
751
687
-void rrdeng_invalidate_oldest_committed(struct rrdengine_worker_config* wc)
688
-{
689
- struct rrdengine_instance *ctx = wc->ctx;
690
- struct page_cache *pg_cache = &ctx->pg_cache;
691
- unsigned nr_committed_pages;
692
- int error;
752
+ page_descriptor_release(descr);
753
+ }
754
694
- if (unlikely(ctx->quiesce != NO_QUIESCE)) /* Shutting down */
695
- return;
755
+ uv_fs_req_cleanup(uv_fs_request);
756
+ posix_memfree(xt_io_descr->buf);
757
+ extent_io_descriptor_release(xt_io_descr);
758
697
- uv_rwlock_rdlock(&pg_cache->committed_page_index.lock);
698
- nr_committed_pages = pg_cache->committed_page_index.nr_committed_pages;
699
- uv_rwlock_rdunlock(&pg_cache->committed_page_index.lock);
759
+ netdata_spinlock_lock(&datafile->writers.spinlock);
760
+ datafile->writers.flushed_to_open_running--;
761
+ netdata_spinlock_unlock(&datafile->writers.spinlock);
762
701
- if (nr_committed_pages >= pg_cache_committed_hard_limit(ctx)) {
702
- /* delete the oldest page in memory */
703
- if (wc->now_invalidating_dirty_pages) {
704
- /* already deleting a page */
705
- return;
706
- }
707
- errno = 0;
708
- error("Failed to flush dirty buffers quickly enough in dbengine instance \"%s\". "
709
- "Metric data are being deleted, please reduce disk load or use a faster disk.", ctx->dbfiles_path);
710
-
711
- wc->now_invalidating_dirty_pages = mallocz(sizeof(*wc->now_invalidating_dirty_pages));
712
- wc->cleanup_thread_invalidating_dirty_pages = 0;
713
-
714
- error = uv_thread_create(wc->now_invalidating_dirty_pages, invalidate_oldest_committed, ctx);
715
- if (error) {
716
- error("uv_thread_create(): %s", uv_strerror(error));
717
- freez(wc->now_invalidating_dirty_pages);
718
- wc->now_invalidating_dirty_pages = NULL;
719
- }
720
- }
763
+ if(datafile->fileno != __atomic_load_n(&ctx->last_fileno, __ATOMIC_RELAXED) && still_running)
764
+ // we just finished a flushing on a datafile that is not the active one
765
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_FILE_INDEX, datafile, NULL, STORAGE_PRIORITY_CRITICAL);
766
}
767
723
-void flush_pages_cb(uv_fs_t* req)
724
-{
725
- struct rrdengine_worker_config* wc = req->loop->data;
726
- struct rrdengine_instance *ctx = wc->ctx;
727
- struct page_cache *pg_cache = &ctx->pg_cache;
728
- struct extent_io_descriptor *xt_io_descr;
729
- struct rrdeng_page_descr *descr;
730
- struct page_cache_descr *pg_cache_descr;
731
- unsigned i, count;
768
+// Main event loop callback
769
+static void extent_flush_io_callback(uv_fs_t *uv_fs_request) {
770
+ worker_is_busy(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSH_PAGES);
771
+ struct extent_io_descriptor *xt_io_descr = uv_fs_request->data;
772
+ struct rrdengine_datafile *datafile = xt_io_descr->datafile;
773
+ struct rrdengine_instance *ctx = datafile->ctx;
774
733
- xt_io_descr = req->data;
734
- if (req->result < 0) {
735
- ++ctx->stats.io_errors;
736
- rrd_stat_atomic_add(&global_io_errors, 1);
737
- error("%s: uv_fs_write: %s", __func__, uv_strerror((int)req->result));
738
- }
739
-#ifdef NETDATA_INTERNAL_CHECKS
740
- {
741
- struct rrdengine_datafile *datafile = xt_io_descr->descr_array[0]->extent->datafile;
742
- debug(D_RRDENGINE, "%s: Extent at offset %"PRIu64"(%u) was written to datafile %u-%u. Waking up waiters.",
743
- __func__, xt_io_descr->pos, xt_io_descr->bytes, datafile->tier, datafile->fileno);
744
- }
745
-#endif
746
- count = xt_io_descr->descr_count;
747
- for (i = 0 ; i < count ; ++i) {
748
- /* care, we don't hold the descriptor mutex */
749
- descr = xt_io_descr->descr_array[i];
775
+ wal_flush_transaction_buffer(ctx, xt_io_descr->datafile, xt_io_descr->wal, &rrdeng_main.loop);
776
751
- pg_cache_replaceQ_insert(ctx, descr);
777
+ netdata_spinlock_lock(&datafile->writers.spinlock);
778
+ datafile->writers.running--;
779
753
- rrdeng_page_descr_mutex_lock(ctx, descr);
754
- pg_cache_descr = descr->pg_cache_descr;
755
- pg_cache_descr->flags &= ~(RRD_PAGE_DIRTY | RRD_PAGE_WRITE_PENDING);
756
- /* wake up waiters, care no reference being held */
757
- pg_cache_wake_up_waiters_unsafe(descr);
758
- rrdeng_page_descr_mutex_unlock(ctx, descr);
759
- }
760
- if (xt_io_descr->completion)
761
- completion_mark_complete(xt_io_descr->completion);
762
- uv_fs_req_cleanup(req);
763
- posix_memfree(xt_io_descr->buf);
764
- freez(xt_io_descr);
780
+ datafile->writers.flushed_to_open_running++;
781
+ rrdeng_enq_cmd(xt_io_descr->ctx, RRDENG_OPCODE_FLUSHED_TO_OPEN, uv_fs_request, xt_io_descr->completion, STORAGE_PRIORITY_CRITICAL);
782
766
- uv_rwlock_wrlock(&pg_cache->committed_page_index.lock);
767
- pg_cache->committed_page_index.nr_committed_pages -= count;
768
- uv_rwlock_wrunlock(&pg_cache->committed_page_index.lock);
769
- wc->inflight_dirty_pages -= count;
783
+ netdata_spinlock_unlock(&datafile->writers.spinlock);
784
+
785
+ worker_is_idle();
786
}
787
788
/*
773
- * completion must be NULL or valid.
774
- * Returns 0 when no flushing can take place.
775
- * Returns datafile bytes to be written on successful flushing initiation.
789
+ * Take a page list in a judy array and write them
790
*/
777
-static int do_flush_pages(struct rrdengine_worker_config* wc, int force, struct completion *completion)
778
-{
779
- struct rrdengine_instance *ctx = wc->ctx;
780
- struct page_cache *pg_cache = &ctx->pg_cache;
791
+static unsigned do_flush_extent(struct rrdengine_instance *ctx, struct page_descr_with_data *base, struct completion *completion) {
792
int ret;
793
int compressed_size, max_compressed_size = 0;
794
unsigned i, count, size_bytes, pos, real_io_size;
795
uint32_t uncompressed_payload_length, payload_offset;
785
- struct rrdeng_page_descr *descr, *eligible_pages[MAX_PAGES_PER_EXTENT];
786
- struct page_cache_descr *pg_cache_descr;
796
+ struct page_descr_with_data *descr, *eligible_pages[MAX_PAGES_PER_EXTENT];
797
struct extent_io_descriptor *xt_io_descr;
798
+ struct extent_buffer *eb = NULL;
799
void *compressed_buf = NULL;
789
- Word_t descr_commit_idx_array[MAX_PAGES_PER_EXTENT];
790
- Pvoid_t *PValue;
800
Word_t Index;
801
uint8_t compression_algorithm = ctx->global_compress_alg;
793
- struct extent_info *extent;
802
struct rrdengine_datafile *datafile;
803
/* persistent structures */
804
struct rrdeng_df_extent_header *header;
805
struct rrdeng_df_extent_trailer *trailer;
806
uLong crc;
807
800
- if (force) {
801
- debug(D_RRDENGINE, "Asynchronous flushing of extent has been forced by page pressure.");
802
- }
803
- uv_rwlock_wrlock(&pg_cache->committed_page_index.lock);
804
- for (Index = 0, count = 0, uncompressed_payload_length = 0,
805
- PValue = JudyLFirst(pg_cache->committed_page_index.JudyL_array, &Index, PJE0),
806
- descr = unlikely(NULL == PValue) ? NULL : *PValue ;
807
-
808
- descr != NULL && count != rrdeng_pages_per_extent;
809
-
810
- PValue = JudyLNext(pg_cache->committed_page_index.JudyL_array, &Index, PJE0),
811
- descr = unlikely(NULL == PValue) ? NULL : *PValue) {
812
- uint8_t page_write_pending;
813
-
814
- fatal_assert(0 != descr->page_length);
815
- page_write_pending = 0;
816
-
817
- rrdeng_page_descr_mutex_lock(ctx, descr);
818
- pg_cache_descr = descr->pg_cache_descr;
819
- if (!(pg_cache_descr->flags & RRD_PAGE_WRITE_PENDING)) {
820
- page_write_pending = 1;
821
- /* care, no reference being held */
822
- pg_cache_descr->flags |= RRD_PAGE_WRITE_PENDING;
823
- uncompressed_payload_length += descr->page_length;
824
- descr_commit_idx_array[count] = Index;
825
- eligible_pages[count++] = descr;
826
- }
827
- rrdeng_page_descr_mutex_unlock(ctx, descr);
808
+ for(descr = base, Index = 0, count = 0, uncompressed_payload_length = 0;
809
+ descr && count != rrdeng_pages_per_extent;
810
+ descr = descr->link.next, Index++) {
811
+
812
+ uncompressed_payload_length += descr->page_length;
813
+ eligible_pages[count++] = descr;
814
829
- if (page_write_pending) {
830
- ret = JudyLDel(&pg_cache->committed_page_index.JudyL_array, Index, PJE0);
831
- fatal_assert(1 == ret);
832
- }
815
}
834
- uv_rwlock_wrunlock(&pg_cache->committed_page_index.lock);
816
817
if (!count) {
837
- debug(D_RRDENGINE, "%s: no pages eligible for flushing.", __func__);
818
if (completion)
819
completion_mark_complete(completion);
820
+
821
+ __atomic_sub_fetch(&ctx->worker_config.atomics.extents_currently_being_flushed, 1, __ATOMIC_RELAXED);
822
return 0;
823
}
842
- wc->inflight_dirty_pages += count;
824
844
- xt_io_descr = mallocz(sizeof(*xt_io_descr));
825
+ xt_io_descr = extent_io_descriptor_get();
826
+ xt_io_descr->ctx = ctx;
827
payload_offset = sizeof(*header) + count * sizeof(header->descr[0]);
828
switch (compression_algorithm) {
847
- case RRD_NO_COMPRESSION:
848
- size_bytes = payload_offset + uncompressed_payload_length + sizeof(*trailer);
849
- break;
850
- default: /* Compress */
851
- fatal_assert(uncompressed_payload_length < LZ4_MAX_INPUT_SIZE);
852
- max_compressed_size = LZ4_compressBound(uncompressed_payload_length);
853
- compressed_buf = mallocz(max_compressed_size);
854
- size_bytes = payload_offset + MAX(uncompressed_payload_length, (unsigned)max_compressed_size) + sizeof(*trailer);
855
- break;
829
+ case RRD_NO_COMPRESSION:
830
+ size_bytes = payload_offset + uncompressed_payload_length + sizeof(*trailer);
831
+ break;
832
+
833
+ default: /* Compress */
834
+ fatal_assert(uncompressed_payload_length < LZ4_MAX_INPUT_SIZE);
835
+ max_compressed_size = LZ4_compressBound(uncompressed_payload_length);
836
+ eb = extent_buffer_get(max_compressed_size);
837
+ compressed_buf = eb->data;
838
+ size_bytes = payload_offset + MAX(uncompressed_payload_length, (unsigned)max_compressed_size) + sizeof(*trailer);
839
+ break;
840
}
841
+
842
ret = posix_memalign((void *)&xt_io_descr->buf, RRDFILE_ALIGNMENT, ALIGN_BYTES_CEILING(size_bytes));
843
if (unlikely(ret)) {
859
- fatal("posix_memalign:%s", strerror(ret));
844
+ fatal("DBENGINE: posix_memalign:%s", strerror(ret));
845
/* freez(xt_io_descr);*/
846
}
847
memset(xt_io_descr->buf, 0, ALIGN_BYTES_CEILING(size_bytes));
863
- (void) memcpy(xt_io_descr->descr_array, eligible_pages, sizeof(struct rrdeng_page_descr *) * count);
848
+ (void) memcpy(xt_io_descr->descr_array, eligible_pages, sizeof(struct page_descr_with_data *) * count);
849
xt_io_descr->descr_count = count;
850
851
pos = 0;
854
header->number_of_pages = count;
855
pos += sizeof(*header);
856
872
- extent = mallocz(sizeof(*extent) + count * sizeof(extent->pages[0]));
873
- datafile = ctx->datafiles.last; /* TODO: check for exceeded size quota */
874
- extent->offset = datafile->pos;
875
- extent->number_of_pages = count;
876
- extent->datafile = datafile;
877
- extent->next = NULL;
878
-
857
for (i = 0 ; i < count ; ++i) {
880
- /* This is here for performance reasons */
881
- xt_io_descr->descr_commit_idx_array[i] = descr_commit_idx_array[i];
882
-
858
descr = xt_io_descr->descr_array[i];
859
header->descr[i].type = descr->type;
860
uuid_copy(*(uuid_t *)header->descr[i].uuid, *descr->id);
865
}
866
for (i = 0 ; i < count ; ++i) {
867
descr = xt_io_descr->descr_array[i];
893
- /* care, we don't hold the descriptor mutex */
894
- (void) memcpy(xt_io_descr->buf + pos, descr->pg_cache_descr->page, descr->page_length);
895
- descr->extent = extent;
896
- extent->pages[i] = descr;
897
-
868
+ (void) memcpy(xt_io_descr->buf + pos, descr->page, descr->page_length);
869
pos += descr->page_length;
870
}
900
- df_extent_insert(extent);
871
872
switch (compression_algorithm) {
903
- case RRD_NO_COMPRESSION:
904
- header->payload_length = uncompressed_payload_length;
905
- break;
906
- default: /* Compress */
907
- compressed_size = LZ4_compress_default(xt_io_descr->buf + payload_offset, compressed_buf,
873
+ case RRD_NO_COMPRESSION:
874
+ header->payload_length = uncompressed_payload_length;
875
+ break;
876
+ default: /* Compress */
877
+ compressed_size = LZ4_compress_default(xt_io_descr->buf + payload_offset, compressed_buf,
878
uncompressed_payload_length, max_compressed_size);
909
- ctx->stats.before_compress_bytes += uncompressed_payload_length;
910
- ctx->stats.after_compress_bytes += compressed_size;
911
- debug(D_RRDENGINE, "LZ4 compressed %"PRIu32" bytes to %d bytes.", uncompressed_payload_length, compressed_size);
912
- (void) memcpy(xt_io_descr->buf + payload_offset, compressed_buf, compressed_size);
913
- freez(compressed_buf);
914
- size_bytes = payload_offset + compressed_size + sizeof(*trailer);
915
- header->payload_length = compressed_size;
879
+ ctx->stats.before_compress_bytes += uncompressed_payload_length;
880
+ ctx->stats.after_compress_bytes += compressed_size;
881
+ debug(D_RRDENGINE, "LZ4 compressed %"PRIu32" bytes to %d bytes.", uncompressed_payload_length, compressed_size);
882
+ (void) memcpy(xt_io_descr->buf + payload_offset, compressed_buf, compressed_size);
883
+ extent_buffer_release(eb);
884
+ size_bytes = payload_offset + compressed_size + sizeof(*trailer);
885
+ header->payload_length = compressed_size;
886
break;
887
}
918
- extent->size = size_bytes;
888
+
889
+ // get the latest datafile
890
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
891
+ datafile = ctx->datafiles.first->prev;
892
+ netdata_spinlock_lock(&datafile->writers.spinlock);
893
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
894
+
895
+ if(ctx_is_available_for_queries(ctx) && datafile->pos > rrdeng_target_data_file_size(ctx)) {
896
+ static SPINLOCK sp = NETDATA_SPINLOCK_INITIALIZER;
897
+ netdata_spinlock_lock(&sp);
898
+ if(create_new_datafile_pair(ctx) == 0)
899
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_FILE_INDEX, datafile, NULL, STORAGE_PRIORITY_CRITICAL);
900
+ netdata_spinlock_unlock(&sp);
901
+
902
+ // unlock the old datafile
903
+ netdata_spinlock_unlock(&datafile->writers.spinlock);
904
+
905
+ // get the new datafile
906
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
907
+ datafile = ctx->datafiles.first->prev;
908
+ netdata_spinlock_lock(&datafile->writers.spinlock);
909
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
910
+ }
911
+
912
+ datafile->writers.running++;
913
+
914
+ xt_io_descr->datafile = datafile;
915
xt_io_descr->bytes = size_bytes;
916
xt_io_descr->pos = datafile->pos;
921
- xt_io_descr->req.data = xt_io_descr;
917
+ xt_io_descr->uv_fs_request.data = xt_io_descr;
918
xt_io_descr->completion = completion;
919
920
trailer = xt_io_descr->buf + size_bytes - sizeof(*trailer);
924
925
real_io_size = ALIGN_BYTES_CEILING(size_bytes);
926
xt_io_descr->iov = uv_buf_init((void *)xt_io_descr->buf, real_io_size);
931
- ret = uv_fs_write(wc->loop, &xt_io_descr->req, datafile->file, &xt_io_descr->iov, 1, datafile->pos, flush_pages_cb);
932
- fatal_assert(-1 != ret);
927
+
928
ctx->stats.io_write_bytes += real_io_size;
929
++ctx->stats.io_write_requests;
930
ctx->stats.io_write_extent_bytes += real_io_size;
931
++ctx->stats.io_write_extents;
937
- do_commit_transaction(wc, STORE_DATA, xt_io_descr);
938
- datafile->pos += ALIGN_BYTES_CEILING(size_bytes);
939
- ctx->disk_space += ALIGN_BYTES_CEILING(size_bytes);
940
- rrdeng_test_quota(wc);
932
+ commit_data_extent(ctx, xt_io_descr);
933
+ datafile->pos += real_io_size;
934
+ ctx->disk_space += real_io_size;
935
+ ctx->last_flush_fileno = datafile->fileno;
936
+
937
+ ret = uv_fs_write(&rrdeng_main.loop, &xt_io_descr->uv_fs_request, datafile->file, &xt_io_descr->iov,
938
+ 1, xt_io_descr->pos, extent_flush_io_callback);
939
+
940
+ fatal_assert(-1 != ret);
941
+
942
+ netdata_spinlock_unlock(&datafile->writers.spinlock);
943
942
- return ALIGN_BYTES_CEILING(size_bytes);
944
+ return real_io_size;
945
}
946
945
-static void after_delete_old_data(struct rrdengine_worker_config* wc)
947
+static void after_database_rotate(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
948
+ ctx->worker_config.now_deleting_files = false;
949
+}
950
+
951
+struct uuid_first_time_s {
952
+ uuid_t *uuid;
953
+ time_t first_time_s;
954
+ time_t last_time_s;
955
+ METRIC *metric;
956
+};
957
+
958
+static int journal_metric_uuid_compare(const void *key, const void *metric)
959
{
947
- struct rrdengine_instance *ctx = wc->ctx;
948
- struct rrdengine_datafile *datafile;
949
- struct rrdengine_journalfile *journalfile;
950
- unsigned deleted_bytes, journalfile_bytes, datafile_bytes;
951
- int ret, error;
960
+ return uuid_compare(*(uuid_t *) key, ((struct journal_metric_list *) metric)->uuid);
961
+}
962
+
963
+void find_uuid_first_time(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, Pvoid_t metric_first_time_JudyL)
964
+{
965
+ if (unlikely(!datafile))
966
+ return;
967
+
968
+ unsigned v2_count = 0;
969
+ unsigned journalfile_count = 0;
970
+ uv_rwlock_rdlock(&ctx->datafiles.rwlock);
971
+ while (datafile) {
972
+ struct journal_v2_header *journal_header = (struct journal_v2_header *) GET_JOURNAL_DATA(datafile->journalfile);
973
+ if (!journal_header || !datafile->users.available) {
974
+ datafile = datafile->next;
975
+ continue;
976
+ }
977
+
978
+ time_t journal_start_time_s = (time_t) (journal_header->start_time_ut / USEC_PER_SEC);
979
+ size_t journal_metric_count = (size_t)journal_header->metric_count;
980
+ struct journal_metric_list *uuid_list = (struct journal_metric_list *)((uint8_t *) journal_header + journal_header->metric_offset);
981
+
982
+ Word_t index = 0;
983
+ bool first_then_next = true;
984
+ Pvoid_t *PValue;
985
+ while ((PValue = JudyLFirstThenNext(metric_first_time_JudyL, &index, &first_then_next))) {
986
+ struct uuid_first_time_s *uuid_first_t_entry = *PValue;
987
+
988
+ struct journal_metric_list *uuid_entry = bsearch(uuid_first_t_entry->uuid,uuid_list,journal_metric_count,sizeof(*uuid_list), journal_metric_uuid_compare);
989
+
990
+ if (unlikely(!uuid_entry))
991
+ continue;
992
+
993
+ time_t first_time_s = uuid_entry->delta_start_s + journal_start_time_s;
994
+ time_t last_time_s = uuid_entry->delta_end_s + journal_start_time_s;
995
+ uuid_first_t_entry->first_time_s = MIN(uuid_first_t_entry->first_time_s , first_time_s);
996
+ uuid_first_t_entry->last_time_s = MAX(uuid_first_t_entry->last_time_s , last_time_s);
997
+ v2_count++;
998
+ }
999
+ journalfile_count++;
1000
+ datafile = datafile->next;
1001
+ }
1002
+ uv_rwlock_rdunlock(&ctx->datafiles.rwlock);
1003
+
1004
+ // Let's scan the open cache for almost exact match
1005
+ bool first_then_next = true;
1006
+ Pvoid_t *PValue;
1007
+ Word_t index = 0;
1008
+ unsigned open_cache_count = 0;
1009
+ while ((PValue = JudyLFirstThenNext(metric_first_time_JudyL, &index, &first_then_next))) {
1010
+ struct uuid_first_time_s *uuid_first_t_entry = *PValue;
1011
+
1012
+ PGC_PAGE *page = pgc_page_get_and_acquire(
1013
+ open_cache, (Word_t)ctx,
1014
+ (Word_t)uuid_first_t_entry->metric, uuid_first_t_entry->last_time_s,
1015
+ PGC_SEARCH_CLOSEST);
1016
+
1017
+ if (page) {
1018
+ time_t first_time_s = pgc_page_start_time_s(page);
1019
+ time_t last_time_s = pgc_page_end_time_s(page);
1020
+ uuid_first_t_entry->first_time_s = MIN(uuid_first_t_entry->first_time_s, first_time_s);
1021
+ uuid_first_t_entry->last_time_s = MAX(uuid_first_t_entry->last_time_s, last_time_s);
1022
+ pgc_page_release(open_cache, page);
1023
+ open_cache_count++;
1024
+ }
1025
+ }
1026
+ info("DBENGINE: processed %u journalfiles and matched %u metric pages in v2 files and %u in open cache", journalfile_count,
1027
+ v2_count, open_cache_count);
1028
+}
1029
+
1030
+static void update_metrics_first_time_s(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile_to_delete, struct rrdengine_datafile *first_datafile_remaining, bool worker) {
1031
+ if(worker)
1032
+ worker_is_busy(UV_EVENT_ANALYZE_V2);
1033
+
1034
+ struct rrdengine_journalfile *journal_file = datafile_to_delete->journalfile;
1035
+ struct journal_v2_header *journal_header = (struct journal_v2_header *)GET_JOURNAL_DATA(journal_file);
1036
+ struct journal_metric_list *uuid_list = (struct journal_metric_list *)((uint8_t *) journal_header + journal_header->metric_offset);
1037
+
1038
+ Pvoid_t metric_first_time_JudyL = (Pvoid_t) NULL;
1039
+ Pvoid_t *PValue;
1040
+
1041
+ unsigned count = 0;
1042
+ struct uuid_first_time_s *uuid_first_t_entry;
1043
+ for (uint32_t index = 0; index < journal_header->metric_count; ++index) {
1044
+ METRIC *metric = mrg_metric_get_and_acquire(main_mrg, &uuid_list[index].uuid, (Word_t) ctx);
1045
+ if (!metric)
1046
+ continue;
1047
+
1048
+ PValue = JudyLIns(&metric_first_time_JudyL, (Word_t) index, PJE0);
1049
+ fatal_assert(NULL != PValue);
1050
+ if (!*PValue) {
1051
+ uuid_first_t_entry = mallocz(sizeof(*uuid_first_t_entry));
1052
+ uuid_first_t_entry->metric = metric;
1053
+ uuid_first_t_entry->first_time_s = mrg_metric_get_first_time_s(main_mrg, metric);
1054
+ uuid_first_t_entry->last_time_s = mrg_metric_get_latest_time_s(main_mrg, metric);
1055
+ uuid_first_t_entry->uuid = mrg_metric_uuid(main_mrg, metric);
1056
+ *PValue = uuid_first_t_entry;
1057
+ count++;
1058
+ }
1059
+ }
1060
+
1061
+ info("DBENGINE: recalculating retention for %u metrics", count);
1062
+
1063
+ // Update the first time / last time for all metrics we plan to delete
1064
+
1065
+ if(worker)
1066
+ worker_is_busy(UV_EVENT_RETENTION_V2);
1067
+
1068
+ find_uuid_first_time(ctx, first_datafile_remaining, metric_first_time_JudyL);
1069
+
1070
+ if(worker)
1071
+ worker_is_busy(UV_EVENT_RETENTION_UPDATE);
1072
+
1073
+ info("DBENGINE: updating metric registry retention for %u metrics", count);
1074
+
1075
+ Word_t index = 0;
1076
+ bool first_then_next = true;
1077
+ while ((PValue = JudyLFirstThenNext(metric_first_time_JudyL, &index, &first_then_next))) {
1078
+ uuid_first_t_entry = *PValue;
1079
+ mrg_metric_set_first_time_s(main_mrg, uuid_first_t_entry->metric, uuid_first_t_entry->first_time_s);
1080
+ mrg_metric_release(main_mrg, uuid_first_t_entry->metric);
1081
+ freez(uuid_first_t_entry);
1082
+ }
1083
+
1084
+ JudyLFreeArray(&metric_first_time_JudyL, PJE0);
1085
+
1086
+ if(worker)
1087
+ worker_is_idle();
1088
+}
1089
+
1090
+static void datafile_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile, bool worker) {
1091
+ if(worker)
1092
+ worker_is_busy(UV_EVENT_DATAFILE_ACQUIRE);
1093
+
1094
+ bool datafile_got_for_deletion = datafile_acquire_for_deletion(datafile);
1095
+
1096
+ if (ctx_is_available_for_queries(ctx))
1097
+ update_metrics_first_time_s(ctx, datafile, datafile->next, worker);
1098
+
1099
+ while (!datafile_got_for_deletion) {
1100
+ if(worker)
1101
+ worker_is_busy(UV_EVENT_DATAFILE_ACQUIRE);
1102
+
1103
+ datafile_got_for_deletion = datafile_acquire_for_deletion(datafile);
1104
+
1105
+ if (!datafile_got_for_deletion) {
1106
+ info("DBENGINE: waiting for data file '%s/"
1107
+ DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1108
+ "' to be available for deletion, "
1109
+ "it is in use currently by %u users.",
1110
+ ctx->dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno, datafile->users.lockers);
1111
+
1112
+ sleep_usec(1 * USEC_PER_SEC);
1113
+ }
1114
+ }
1115
+
1116
+ info("DBENGINE: deleting data file '%s/"
1117
+ DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION
1118
+ "'.",
1119
+ ctx->dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1120
+
1121
+ if(worker)
1122
+ worker_is_busy(UV_EVENT_DATAFILE_DELETE);
1123
+
1124
+ struct rrdengine_journalfile *journal_file;
1125
+ unsigned deleted_bytes, journal_file_bytes, datafile_bytes;
1126
+ int ret;
1127
char path[RRDENG_PATH_MAX];
1128
954
- datafile = ctx->datafiles.first;
955
- journalfile = datafile->journalfile;
1129
+ uv_rwlock_wrlock(&ctx->datafiles.rwlock);
1130
+
1131
+ journal_file = datafile->journalfile;
1132
datafile_bytes = datafile->pos;
957
- journalfile_bytes = journalfile->pos;
958
- deleted_bytes = 0;
1133
+ journal_file_bytes = journal_file->pos;
1134
+ deleted_bytes = GET_JOURNAL_DATA_SIZE(journal_file);
1135
960
- info("Deleting data and journal file pair.");
961
- datafile_list_delete(ctx, datafile);
962
- ret = destroy_journal_file(journalfile, datafile);
1136
+ info("DBENGINE: deleting data and journal files to maintain disk quota");
1137
+ datafile_list_delete_unsafe(ctx, datafile);
1138
+ ret = destroy_journal_file_unsafe(journal_file, datafile);
1139
if (!ret) {
1140
generate_journalfilepath(datafile, path, sizeof(path));
965
- info("Deleted journal file \"%s\".", path);
966
- deleted_bytes += journalfile_bytes;
1141
+ info("DBENGINE: deleted journal file \"%s\".", path);
1142
+ generate_journalfilepath_v2(datafile, path, sizeof(path));
1143
+ info("DBENGINE: deleted journal file \"%s\".", path);
1144
+ deleted_bytes += journal_file_bytes;
1145
}
968
- ret = destroy_data_file(datafile);
1146
+ ret = destroy_data_file_unsafe(datafile);
1147
if (!ret) {
1148
generate_datafilepath(datafile, path, sizeof(path));
971
- info("Deleted data file \"%s\".", path);
1149
+ info("DBENGINE: deleted data file \"%s\".", path);
1150
deleted_bytes += datafile_bytes;
1151
}
974
- freez(journalfile);
1152
+ freez(journal_file);
1153
freez(datafile);
1154
1155
ctx->disk_space -= deleted_bytes;
978
- info("Reclaimed %u bytes of disk space.", deleted_bytes);
979
-
980
- error = uv_thread_join(wc->now_deleting_files);
981
- if (error) {
982
- error("uv_thread_join(): %s", uv_strerror(error));
983
- }
984
- freez(wc->now_deleting_files);
985
- /* unfreeze command processing */
986
- wc->now_deleting_files = NULL;
1156
+ info("DBENGINE: reclaimed %u bytes of disk space.", deleted_bytes);
1157
+ uv_rwlock_wrunlock(&ctx->datafiles.rwlock);
1158
988
- wc->cleanup_thread_deleting_files = 0;
1159
rrdcontext_db_rotation();
1160
+}
1161
991
- /* interrupt event loop */
992
- uv_stop(wc->loop);
1162
+static void database_rotate_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1163
+ datafile_delete(ctx, ctx->datafiles.first, true);
1164
}
1165
995
-static void delete_old_data(void *arg)
996
-{
997
- struct rrdengine_instance *ctx = arg;
998
- struct rrdengine_worker_config* wc = &ctx->worker_config;
999
- struct rrdengine_datafile *datafile;
1000
- struct extent_info *extent, *next;
1001
- struct rrdeng_page_descr *descr;
1002
- unsigned count, i;
1003
- uint8_t can_delete_metric;
1004
- uuid_t metric_id;
1005
-
1006
- /* Safe to use since it will be deleted after we are done */
1007
- datafile = ctx->datafiles.first;
1008
-
1009
- for (extent = datafile->extents.first ; extent != NULL ; extent = next) {
1010
- count = extent->number_of_pages;
1011
- for (i = 0 ; i < count ; ++i) {
1012
- descr = extent->pages[i];
1013
- can_delete_metric = pg_cache_punch_hole(ctx, descr, 0, 0, &metric_id);
1014
- if (unlikely(can_delete_metric)) {
1015
- /*
1016
- * If the metric is empty, has no active writers and if the metadata log has been initialized then
1017
- * attempt to delete the corresponding netdata dimension.
1018
- */
1019
- metaqueue_delete_dimension_uuid(&metric_id);
1020
- }
1021
- }
1022
- next = extent->next;
1023
- freez(extent);
1024
- }
1025
- wc->cleanup_thread_deleting_files = 1;
1026
- /* wake up event loop */
1027
- fatal_assert(0 == uv_async_send(&wc->async));
1166
+static void after_flush_all_hot_and_dirty_pages_of_section(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1167
+ ;
1168
}
1169
1030
-void rrdeng_test_quota(struct rrdengine_worker_config* wc)
1031
-{
1032
- struct rrdengine_instance *ctx = wc->ctx;
1033
- struct rrdengine_datafile *datafile;
1034
- unsigned current_size, target_size;
1035
- uint8_t out_of_space, only_one_datafile;
1036
- int ret, error;
1037
-
1038
- out_of_space = 0;
1039
- /* Do not allow the pinned pages to exceed the disk space quota to avoid deadlocks */
1040
- if (unlikely(ctx->disk_space > MAX(ctx->max_disk_space, 2 * ctx->metric_API_max_producers * RRDENG_BLOCK_SIZE))) {
1041
- out_of_space = 1;
1042
- }
1043
- datafile = ctx->datafiles.last;
1044
- current_size = datafile->pos;
1045
- target_size = ctx->max_disk_space / TARGET_DATAFILES;
1046
- target_size = MIN(target_size, MAX_DATAFILE_SIZE);
1047
- target_size = MAX(target_size, MIN_DATAFILE_SIZE);
1048
- only_one_datafile = (datafile == ctx->datafiles.first) ? 1 : 0;
1049
- if (unlikely(current_size >= target_size || (out_of_space && only_one_datafile))) {
1050
- /* Finalize data and journal file and create a new pair */
1051
- wal_flush_transaction_buffer(wc);
1052
- ret = create_new_datafile_pair(ctx, 1, ctx->last_fileno + 1);
1053
- if (likely(!ret)) {
1054
- ++ctx->last_fileno;
1055
- }
1056
- }
1057
- if (unlikely(out_of_space && NO_QUIESCE == ctx->quiesce)) {
1058
- /* delete old data */
1059
- if (wc->now_deleting_files) {
1060
- /* already deleting data */
1061
- return;
1062
- }
1063
- if (NULL == ctx->datafiles.first->next) {
1064
- error("Cannot delete data file \"%s/"DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION"\""
1065
- " to reclaim space, there are no other file pairs left.",
1066
- ctx->dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1067
- return;
1068
- }
1069
- info("Deleting data file \"%s/"DATAFILE_PREFIX RRDENG_FILE_NUMBER_PRINT_TMPL DATAFILE_EXTENSION"\".",
1070
- ctx->dbfiles_path, ctx->datafiles.first->tier, ctx->datafiles.first->fileno);
1071
- wc->now_deleting_files = mallocz(sizeof(*wc->now_deleting_files));
1072
- wc->cleanup_thread_deleting_files = 0;
1073
-
1074
- error = uv_thread_create(wc->now_deleting_files, delete_old_data, ctx);
1075
- if (error) {
1076
- error("uv_thread_create(): %s", uv_strerror(error));
1077
- freez(wc->now_deleting_files);
1078
- wc->now_deleting_files = NULL;
1079
- }
1080
- }
1170
+static void flush_all_hot_and_dirty_pages_of_section_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1171
+ pgc_flush_all_hot_and_dirty_pages(main_cache, (Word_t)ctx);
1172
+ completion_mark_complete(&ctx->quiesce_completion);
1173
}
1174
1083
-static inline int rrdeng_threads_alive(struct rrdengine_worker_config* wc)
1084
-{
1085
- if (wc->now_invalidating_dirty_pages || wc->now_deleting_files) {
1086
- return 1;
1087
- }
1088
- return 0;
1175
+static void after_ctx_shutdown(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1176
+ ;
1177
}
1178
1091
-static void rrdeng_cleanup_finished_threads(struct rrdengine_worker_config* wc)
1092
-{
1093
- struct rrdengine_instance *ctx = wc->ctx;
1179
+static void ctx_shutdown_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1180
+ completion_wait_for(&ctx->quiesce_completion);
1181
+ completion_destroy(&ctx->quiesce_completion);
1182
1095
- if (unlikely(wc->cleanup_thread_invalidating_dirty_pages)) {
1096
- after_invalidate_oldest_committed(wc);
1097
- }
1098
- if (unlikely(wc->cleanup_thread_deleting_files)) {
1099
- after_delete_old_data(wc);
1100
- }
1101
- if (unlikely(SET_QUIESCE == ctx->quiesce && !rrdeng_threads_alive(wc))) {
1102
- ctx->quiesce = QUIESCED;
1103
- completion_mark_complete(&ctx->rrdengine_completion);
1104
- }
1183
+ while(__atomic_load_n(&ctx->worker_config.atomics.extents_currently_being_flushed, __ATOMIC_RELAXED) ||
1184
+ __atomic_load_n(&ctx->inflight_queries, __ATOMIC_RELAXED))
1185
+ sleep_usec(1 * USEC_PER_MS);
1186
+
1187
+ completion_mark_complete(completion);
1188
}
1189
1107
-/* return 0 on success */
1108
-int init_rrd_files(struct rrdengine_instance *ctx)
1109
-{
1110
- int ret = init_data_files(ctx);
1111
-
1112
- BUFFER *wb = buffer_create(1000);
1113
- size_t all_errors = 0;
1114
- usec_t now = now_realtime_usec();
1115
-
1116
- if(ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter) {
1117
- buffer_sprintf(wb, "%s%zu pages had start time > end time (latest: %llu secs ago)"
1118
- , (all_errors)?", ":""
1119
- , ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter
1120
- , (now - ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].latest_end_time_ut) / USEC_PER_SEC
1121
- );
1122
- all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter;
1123
- }
1190
+static void cache_flush_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1191
+ if (!main_cache)
1192
+ return;
1193
1125
- if(ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter) {
1126
- buffer_sprintf(wb, "%s%zu pages had start time = end time with more than 1 entries (latest: %llu secs ago)"
1127
- , (all_errors)?", ":""
1128
- , ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter
1129
- , (now - ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].latest_end_time_ut) / USEC_PER_SEC
1130
- );
1131
- all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter;
1132
- }
1194
+ worker_is_busy(UV_EVENT_FLUSH_MAIN);
1195
+ pgc_flush_pages(main_cache, 0);
1196
+}
1197
1134
- if(ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter) {
1135
- buffer_sprintf(wb, "%s%zu pages had zero points (latest: %llu secs ago)"
1136
- , (all_errors)?", ":""
1137
- , ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter
1138
- , (now - ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].latest_end_time_ut) / USEC_PER_SEC
1139
- );
1140
- all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter;
1141
- }
1198
+static void cache_evict_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1199
+ if (!main_cache)
1200
+ return;
1201
1143
- if(ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter) {
1144
- buffer_sprintf(wb, "%s%zu pages had update every == 0 with entries > 1 (latest: %llu secs ago)"
1145
- , (all_errors)?", ":""
1146
- , ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter
1147
- , (now - ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].latest_end_time_ut) / USEC_PER_SEC
1148
- );
1149
- all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter;
1150
- }
1202
+ worker_is_busy(UV_EVENT_EVICT_MAIN);
1203
+ pgc_evict_pages(main_cache, 0, 0);
1204
+}
1205
1152
- if(ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter) {
1153
- buffer_sprintf(wb, "%s%zu pages had a different number of points compared to their timestamps (latest: %llu secs ago; these page have been loaded)"
1154
- , (all_errors)?", ":""
1155
- , ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter
1156
- , (now - ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].latest_end_time_ut) / USEC_PER_SEC
1157
- );
1158
- all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter;
1159
- }
1206
+static void after_prep_query(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1207
+ ;
1208
+}
1209
1161
- if(ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter) {
1162
- buffer_sprintf(wb, "%s%zu extents have been dropped because they didn't have any valid pages"
1163
- , (all_errors)?", ":""
1164
- , ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter
1165
- );
1166
- all_errors += ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter;
1167
- }
1210
+static void query_prep_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *req __maybe_unused) {
1211
+ worker_is_busy(UV_EVENT_PREP_QUERY);
1212
+ PDC *pdc = data;
1213
+ rrdeng_prep_query(pdc);
1214
+}
1215
1169
- if(all_errors)
1170
- info("DBENGINE: tier %d: %s", ctx->tier, buffer_tostring(wb));
1216
+unsigned rrdeng_target_data_file_size(struct rrdengine_instance *ctx) {
1217
+ unsigned target_size = ctx->max_disk_space / TARGET_DATAFILES;
1218
+ target_size = MIN(target_size, MAX_DATAFILE_SIZE);
1219
+ target_size = MAX(target_size, MIN_DATAFILE_SIZE);
1220
+ return target_size;
1221
+}
1222
1172
- buffer_free(wb);
1173
- return ret;
1223
+/* return 0 on success */
1224
+int init_rrd_files(struct rrdengine_instance *ctx)
1225
+{
1226
+ return init_data_files(ctx);
1227
}
1228
1229
void finalize_rrd_files(struct rrdengine_instance *ctx)
1231
return finalize_data_files(ctx);
1232
}
1233
1181
-void rrdeng_init_cmd_queue(struct rrdengine_worker_config* wc)
1234
+void async_cb(uv_async_t *handle)
1235
{
1183
- wc->cmd_queue.head = wc->cmd_queue.tail = 0;
1184
- wc->queue_size = 0;
1185
- fatal_assert(0 == uv_cond_init(&wc->cmd_cond));
1186
- fatal_assert(0 == uv_mutex_init(&wc->cmd_mutex));
1236
+ uv_stop(handle->loop);
1237
+ uv_update_time(handle->loop);
1238
+ debug(D_RRDENGINE, "%s called, active=%d.", __func__, uv_is_active((uv_handle_t *)handle));
1239
}
1240
1189
-void rrdeng_enq_cmd(struct rrdengine_worker_config* wc, struct rrdeng_cmd *cmd)
1190
-{
1191
- unsigned queue_size;
1241
+#define TIMER_PERIOD_MS (1000)
1242
1193
- /* wait for free space in queue */
1194
- uv_mutex_lock(&wc->cmd_mutex);
1195
- while ((queue_size = wc->queue_size) == RRDENG_CMD_Q_MAX_SIZE) {
1196
- uv_cond_wait(&wc->cmd_cond, &wc->cmd_mutex);
1197
- }
1198
- fatal_assert(queue_size < RRDENG_CMD_Q_MAX_SIZE);
1199
- /* enqueue command */
1200
- wc->cmd_queue.cmd_array[wc->cmd_queue.tail] = *cmd;
1201
- wc->cmd_queue.tail = wc->cmd_queue.tail != RRDENG_CMD_Q_MAX_SIZE - 1 ?
1202
- wc->cmd_queue.tail + 1 : 0;
1203
- wc->queue_size = queue_size + 1;
1204
- uv_mutex_unlock(&wc->cmd_mutex);
1205
-
1206
- /* wake up event loop */
1207
- fatal_assert(0 == uv_async_send(&wc->async));
1243
+
1244
+static void extent_read_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1245
+ EPDL *epdl = data;
1246
+ epdl_find_extent_and_populate_pages(ctx, epdl, true);
1247
}
1248
1210
-struct rrdeng_cmd rrdeng_deq_cmd(struct rrdengine_worker_config* wc)
1211
-{
1212
- struct rrdeng_cmd ret;
1213
- unsigned queue_size;
1214
-
1215
- uv_mutex_lock(&wc->cmd_mutex);
1216
- queue_size = wc->queue_size;
1217
- if (queue_size == 0) {
1218
- ret.opcode = RRDENG_NOOP;
1219
- } else {
1220
- /* dequeue command */
1221
- ret = wc->cmd_queue.cmd_array[wc->cmd_queue.head];
1222
- if (queue_size == 1) {
1223
- wc->cmd_queue.head = wc->cmd_queue.tail = 0;
1224
- } else {
1225
- wc->cmd_queue.head = wc->cmd_queue.head != RRDENG_CMD_Q_MAX_SIZE - 1 ?
1226
- wc->cmd_queue.head + 1 : 0;
1249
+static void epdl_populate_pages_asynchronously(struct rrdengine_instance *ctx, EPDL *epdl, STORAGE_PRIORITY priority) {
1250
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_EXTENT_READ, epdl, NULL, priority);
1251
+}
1252
+
1253
+void pdc_route_asynchronously(struct rrdengine_instance *ctx, struct page_details_control *pdc) {
1254
+ pdc_to_epdl_router(ctx, pdc, epdl_populate_pages_asynchronously, epdl_populate_pages_asynchronously);
1255
+}
1256
+
1257
+void epdl_populate_pages_synchronously(struct rrdengine_instance *ctx, EPDL *epdl, enum storage_priority priority __maybe_unused) {
1258
+ epdl_find_extent_and_populate_pages(ctx, epdl, false);
1259
+}
1260
+
1261
+void pdc_route_synchronously(struct rrdengine_instance *ctx, struct page_details_control *pdc) {
1262
+ pdc_to_epdl_router(ctx, pdc, epdl_populate_pages_synchronously, epdl_populate_pages_synchronously);
1263
+}
1264
+
1265
+#define MAX_RETRIES_TO_START_INDEX (100)
1266
+static void journal_v2_indexing_tp_worker(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t *uv_work_req __maybe_unused) {
1267
+ unsigned count = 0;
1268
+ worker_is_busy(UV_EVENT_JOURNAL_INDEX_WAIT);
1269
+
1270
+ while (ctx->worker_config.now_deleting_files && count++ < MAX_RETRIES_TO_START_INDEX)
1271
+ sleep_usec(100 * USEC_PER_MS);
1272
+
1273
+ if (count == MAX_RETRIES_TO_START_INDEX) {
1274
+ worker_is_idle();
1275
+ return;
1276
+ }
1277
+
1278
+ struct rrdengine_datafile *datafile = ctx->datafiles.first;
1279
+ worker_is_busy(UV_EVENT_JOURNAL_INDEX);
1280
+ count = 0;
1281
+ while (datafile && datafile->fileno != ctx->last_fileno && datafile->fileno != ctx->last_flush_fileno) {
1282
+
1283
+ netdata_spinlock_lock(&datafile->writers.spinlock);
1284
+ bool available = (datafile->writers.running || datafile->writers.flushed_to_open_running) ? false : true;
1285
+ netdata_spinlock_unlock(&datafile->writers.spinlock);
1286
+
1287
+ if(!available)
1288
+ continue;
1289
+
1290
+ if (unlikely(!GET_JOURNAL_DATA(datafile->journalfile))) {
1291
+ info("DBENGINE: journal file %u is ready to be indexed", datafile->fileno);
1292
+ pgc_open_cache_to_journal_v2(open_cache, (Word_t) ctx, (int) datafile->fileno, ctx->page_type, do_migrate_to_v2_callback, (void *) datafile->journalfile);
1293
+ count++;
1294
}
1228
- wc->queue_size = queue_size - 1;
1295
1230
- /* wake up producers */
1231
- uv_cond_signal(&wc->cmd_cond);
1296
+ datafile = datafile->next;
1297
+
1298
+ if (unlikely(!ctx_is_available_for_queries(ctx)))
1299
+ break;
1300
}
1233
- uv_mutex_unlock(&wc->cmd_mutex);
1301
1235
- return ret;
1302
+ errno = 0;
1303
+ internal_error(count, "DBENGINE: journal indexing done; %u files processed", count);
1304
+
1305
+ worker_is_idle();
1306
}
1307
1238
-static void load_configuration_dynamic(void)
1239
-{
1240
- unsigned read_num = (unsigned)config_get_number(CONFIG_SECTION_DB, "dbengine pages per extent", MAX_PAGES_PER_EXTENT);
1241
- if (read_num > 0 && read_num <= MAX_PAGES_PER_EXTENT)
1242
- rrdeng_pages_per_extent = read_num;
1243
- else {
1244
- error("Invalid dbengine pages per extent %u given. Using %u.", read_num, rrdeng_pages_per_extent);
1245
- config_set_number(CONFIG_SECTION_DB, "dbengine pages per extent", rrdeng_pages_per_extent);
1246
- }
1308
+static void after_do_cache_flush(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1309
+ rrdeng_main.flush_running = false;
1310
}
1311
1249
-void async_cb(uv_async_t *handle)
1250
-{
1251
- uv_stop(handle->loop);
1252
- uv_update_time(handle->loop);
1253
- debug(D_RRDENGINE, "%s called, active=%d.", __func__, uv_is_active((uv_handle_t *)handle));
1312
+static void after_do_cache_evict(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1313
+ rrdeng_main.evict_running = false;
1314
}
1315
1256
-/* Flushes dirty pages when timer expires */
1257
-#define TIMER_PERIOD_MS (1000)
1316
+static void after_extent_read(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1317
+ ;
1318
+}
1319
1259
-void timer_cb(uv_timer_t* handle)
1260
-{
1261
- worker_is_busy(RRDENG_MAX_OPCODE + 1);
1320
+static void after_journal_v2_indexing(struct rrdengine_instance *ctx __maybe_unused, void *data __maybe_unused, struct completion *completion __maybe_unused, uv_work_t* req __maybe_unused, int status __maybe_unused) {
1321
+ ctx->worker_config.migration_to_v2_running = false;
1322
+ rrdeng_enq_cmd(ctx, RRDENG_OPCODE_DATABASE_ROTATE, NULL, NULL, STORAGE_PRIORITY_CRITICAL);
1323
+}
1324
1263
- struct rrdengine_worker_config* wc = handle->data;
1264
- struct rrdengine_instance *ctx = wc->ctx;
1325
+struct rrdeng_buffer_sizes rrdeng_get_buffer_sizes(void) {
1326
+ return (struct rrdeng_buffer_sizes) {
1327
+ .opcodes = __atomic_load_n(&rrdeng_cmd_globals.cache.atomics.allocated, __ATOMIC_RELAXED) * sizeof(struct rrdeng_cmd),
1328
+ .handles = __atomic_load_n(&rrdeng_query_handle_globals.atomics.allocated, __ATOMIC_RELAXED) * sizeof(struct rrdeng_query_handle),
1329
+ .descriptors = __atomic_load_n(&page_descriptor_globals.atomics.allocated, __ATOMIC_RELAXED) * sizeof(struct page_descr_with_data),
1330
+ .wal = __atomic_load_n(&wal_globals.atomics.allocated, __ATOMIC_RELAXED) * (sizeof(WAL) + RRDENG_BLOCK_SIZE),
1331
+ .workers = __atomic_load_n(&work_request_globals.atomics.allocated, __ATOMIC_RELAXED) * sizeof(struct rrdeng_work),
1332
+ .pdc = pdc_cache_size(),
1333
+ .xt_io = __atomic_load_n(&extent_io_descriptor_globals.atomics.allocated, __ATOMIC_RELAXED) * sizeof(struct extent_io_descriptor),
1334
+ .xt_buf = extent_buffer_cache_size(),
1335
+ .epdl = epdl_cache_size(),
1336
+ .deol = deol_cache_size(),
1337
+ .pd = pd_cache_size(),
1338
+#ifdef PDC_USE_JULYL
1339
+ .julyl = julyl_cache_size(),
1340
+#endif
1341
+ };
1342
+}
1343
1344
+void timer_cb(uv_timer_t* handle) {
1345
+ worker_is_busy(RRDENG_TIMER_CB);
1346
uv_stop(handle->loop);
1347
uv_update_time(handle->loop);
1268
- rrdeng_test_quota(wc);
1269
- debug(D_RRDENGINE, "%s: timeout reached.", __func__);
1270
- if (likely(!wc->now_deleting_files && !wc->now_invalidating_dirty_pages)) {
1271
- /* There is free space so we can write to disk and we are not actively deleting dirty buffers */
1272
- struct page_cache *pg_cache = &ctx->pg_cache;
1273
- unsigned long total_bytes, bytes_written, nr_committed_pages, bytes_to_write = 0, producers, low_watermark,
1274
- high_watermark;
1275
-
1276
- uv_rwlock_rdlock(&pg_cache->committed_page_index.lock);
1277
- nr_committed_pages = pg_cache->committed_page_index.nr_committed_pages;
1278
- uv_rwlock_rdunlock(&pg_cache->committed_page_index.lock);
1279
- producers = ctx->metric_API_max_producers;
1280
- /* are flushable pages more than 25% of the maximum page cache size */
1281
- high_watermark = (ctx->max_cache_pages * 25LLU) / 100;
1282
- low_watermark = (ctx->max_cache_pages * 5LLU) / 100; /* 5%, must be smaller than high_watermark */
1283
-
1284
- /* Flush more pages only if disk can keep up */
1285
- if (wc->inflight_dirty_pages < high_watermark + producers) {
1286
- if (nr_committed_pages > producers &&
1287
- /* committed to be written pages are more than the produced number */
1288
- nr_committed_pages - producers > high_watermark) {
1289
- /* Flushing speed must increase to stop page cache from filling with dirty pages */
1290
- bytes_to_write = (nr_committed_pages - producers - low_watermark) * RRDENG_BLOCK_SIZE;
1291
- }
1292
- bytes_to_write = MAX(DATAFILE_IDEAL_IO_SIZE, bytes_to_write);
1348
1294
- debug(D_RRDENGINE, "Flushing pages to disk.");
1295
- for (total_bytes = bytes_written = do_flush_pages(wc, 0, NULL);
1296
- bytes_written && (total_bytes < bytes_to_write);
1297
- total_bytes += bytes_written) {
1298
- bytes_written = do_flush_pages(wc, 0, NULL);
1299
- }
1300
- }
1301
- }
1302
- load_configuration_dynamic();
1303
-#ifdef NETDATA_INTERNAL_CHECKS
1304
- {
1305
- char buf[4096];
1306
- debug(D_RRDENGINE, "%s", get_rrdeng_statistics(wc->ctx, buf, sizeof(buf)));
1307
- }
1349
+ worker_set_metric(RRDENG_OPCODES_WAITING, (NETDATA_DOUBLE)rrdeng_cmd_globals.queue.waiting);
1350
+ worker_set_metric(RRDENG_WORKS_DISPATCHED, (NETDATA_DOUBLE)__atomic_load_n(&work_request_globals.atomics.dispatched, __ATOMIC_RELAXED));
1351
+ worker_set_metric(RRDENG_WORKS_EXECUTING, (NETDATA_DOUBLE)__atomic_load_n(&work_request_globals.atomics.executing, __ATOMIC_RELAXED));
1352
+
1353
+ rrdeng_enq_cmd(NULL, RRDENG_OPCODE_FLUSH_INIT, NULL, NULL, STORAGE_PRIORITY_CRITICAL);
1354
+ rrdeng_enq_cmd(NULL, RRDENG_OPCODE_EVICT_INIT, NULL, NULL, STORAGE_PRIORITY_CRITICAL);
1355
+
1356
+ time_t now_s = now_monotonic_sec();
1357
+ if(now_s - rrdeng_main.last_buffers_cleanup_s > 600) {
1358
+ rrdeng_main.last_buffers_cleanup_s = now_s;
1359
+
1360
+ work_request_cleanup();
1361
+ page_descriptor_cleanup();
1362
+ extent_io_descriptor_cleanup();
1363
+ rrdeng_cmd_cleanup();
1364
+ pdc_cleanup();
1365
+ page_details_cleanup();
1366
+ rrdeng_query_handle_cleanup();
1367
+ wal_cleanup();
1368
+ extent_buffer_cleanup();
1369
+ epdl_cleanup();
1370
+ deol_cleanup();
1371
+#ifdef PDC_USE_JULYL
1372
+ julyl_cleanup();
1373
#endif
1374
+ }
1375
1376
worker_is_idle();
1377
}
1378
1313
-#define MAX_CMD_BATCH_SIZE (256)
1379
+bool rrdeng_dbengine_spawn(struct rrdengine_instance *ctx) {
1380
+ static bool spawned = false;
1381
1315
-void rrdeng_worker(void* arg)
1316
-{
1317
- worker_register("DBENGINE");
1318
- worker_register_job_name(RRDENG_NOOP, "noop");
1319
- worker_register_job_name(RRDENG_READ_PAGE, "page read");
1320
- worker_register_job_name(RRDENG_READ_EXTENT, "extent read");
1321
- worker_register_job_name(RRDENG_COMMIT_PAGE, "commit");
1322
- worker_register_job_name(RRDENG_FLUSH_PAGES, "flush");
1323
- worker_register_job_name(RRDENG_SHUTDOWN, "shutdown");
1324
- worker_register_job_name(RRDENG_INVALIDATE_OLDEST_MEMORY_PAGE, "page lru");
1325
- worker_register_job_name(RRDENG_QUIESCE, "quiesce");
1326
- worker_register_job_name(RRDENG_MAX_OPCODE, "cleanup");
1327
- worker_register_job_name(RRDENG_MAX_OPCODE + 1, "timer");
1328
-
1329
- struct rrdengine_worker_config* wc = arg;
1330
- struct rrdengine_instance *ctx = wc->ctx;
1331
- uv_loop_t* loop;
1332
- int shutdown, ret;
1333
- enum rrdeng_opcode opcode;
1334
- uv_timer_t timer_req;
1335
- struct rrdeng_cmd cmd;
1336
- unsigned cmd_batch_size;
1382
+ if(!spawned) {
1383
+ int ret;
1384
1338
- rrdeng_init_cmd_queue(wc);
1385
+ ret = uv_loop_init(&rrdeng_main.loop);
1386
+ if (ret) {
1387
+ error("DBENGINE: uv_loop_init(): %s", uv_strerror(ret));
1388
+ return false;
1389
+ }
1390
+ rrdeng_main.loop.data = &rrdeng_main;
1391
1340
- loop = wc->loop = mallocz(sizeof(uv_loop_t));
1341
- ret = uv_loop_init(loop);
1342
- if (ret) {
1343
- error("uv_loop_init(): %s", uv_strerror(ret));
1344
- goto error_after_loop_init;
1345
- }
1346
- loop->data = wc;
1392
+ ret = uv_async_init(&rrdeng_main.loop, &rrdeng_main.async, async_cb);
1393
+ if (ret) {
1394
+ error("DBENGINE: uv_async_init(): %s", uv_strerror(ret));
1395
+ fatal_assert(0 == uv_loop_close(&rrdeng_main.loop));
1396
+ return false;
1397
+ }
1398
+ rrdeng_main.async.data = &rrdeng_main;
1399
+
1400
+ ret = uv_timer_init(&rrdeng_main.loop, &rrdeng_main.timer);
1401
+ if (ret) {
1402
+ error("DBENGINE: uv_timer_init(): %s", uv_strerror(ret));
1403
+ uv_close((uv_handle_t *)&rrdeng_main.async, NULL);
1404
+ fatal_assert(0 == uv_loop_close(&rrdeng_main.loop));
1405
+ return false;
1406
+ }
1407
+ rrdeng_main.timer.data = &rrdeng_main;
1408
1348
- ret = uv_async_init(wc->loop, &wc->async, async_cb);
1349
- if (ret) {
1350
- error("uv_async_init(): %s", uv_strerror(ret));
1351
- goto error_after_async_init;
1409
+ fatal_assert(0 == uv_thread_create(&rrdeng_main.thread, dbengine_event_loop, &rrdeng_main));
1410
+ spawned = true;
1411
}
1353
- wc->async.data = wc;
1412
1355
- wc->now_deleting_files = NULL;
1356
- wc->cleanup_thread_deleting_files = 0;
1413
+ ctx->worker_config.now_deleting_files = false;
1414
+ ctx->worker_config.migration_to_v2_running = false;
1415
+ ctx->worker_config.atomics.extents_currently_being_flushed = 0;
1416
1358
- wc->now_invalidating_dirty_pages = NULL;
1359
- wc->cleanup_thread_invalidating_dirty_pages = 0;
1360
- wc->inflight_dirty_pages = 0;
1417
+ return true;
1418
+}
1419
1362
- /* dirty page flushing timer */
1363
- ret = uv_timer_init(loop, &timer_req);
1364
- if (ret) {
1365
- error("uv_timer_init(): %s", uv_strerror(ret));
1366
- goto error_after_timer_init;
1367
- }
1368
- timer_req.data = wc;
1420
+void dbengine_event_loop(void* arg) {
1421
+ sanity_check();
1422
+ uv_thread_set_name_np(pthread_self(), "DBENGINE");
1423
1370
- wc->error = 0;
1371
- /* wake up initialization thread */
1372
- completion_mark_complete(&ctx->rrdengine_completion);
1424
+ worker_register("DBENGINE");
1425
1374
- fatal_assert(0 == uv_timer_start(&timer_req, timer_cb, TIMER_PERIOD_MS, TIMER_PERIOD_MS));
1375
- shutdown = 0;
1376
- int set_name = 0;
1377
- while (likely(shutdown == 0 || rrdeng_threads_alive(wc))) {
1426
+ // opcode jobs
1427
+ worker_register_job_name(RRDENG_OPCODE_NOOP, "noop");
1428
+
1429
+ worker_register_job_name(RRDENG_OPCODE_EXTENT_READ, "extent read");
1430
+ worker_register_job_name(RRDENG_OPCODE_PREP_QUERY, "prep query");
1431
+ worker_register_job_name(RRDENG_OPCODE_FLUSH_PAGES, "flush pages");
1432
+ worker_register_job_name(RRDENG_OPCODE_FLUSHED_TO_OPEN, "flushed to open");
1433
+ worker_register_job_name(RRDENG_OPCODE_FLUSH_INIT, "flush init");
1434
+ worker_register_job_name(RRDENG_OPCODE_EVICT_INIT, "evict init");
1435
+ //worker_register_job_name(RRDENG_OPCODE_DATAFILE_CREATE, "datafile create");
1436
+ worker_register_job_name(RRDENG_OPCODE_JOURNAL_FILE_INDEX, "journal file index");
1437
+ worker_register_job_name(RRDENG_OPCODE_DATABASE_ROTATE, "db rotate");
1438
+ worker_register_job_name(RRDENG_OPCODE_CTX_SHUTDOWN, "ctx shutdown");
1439
+ worker_register_job_name(RRDENG_OPCODE_CTX_QUIESCE, "ctx quiesce");
1440
+
1441
+ worker_register_job_name(RRDENG_OPCODE_MAX, "get opcode");
1442
+
1443
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_EXTENT_READ, "extent read cb");
1444
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_PREP_QUERY, "prep query cb");
1445
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSH_PAGES, "flush pages cb");
1446
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSHED_TO_OPEN, "flushed to open cb");
1447
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_FLUSH_INIT, "flush init cb");
1448
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_EVICT_INIT, "evict init cb");
1449
+ //worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_DATAFILE_CREATE, "datafile create cb");
1450
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_JOURNAL_FILE_INDEX, "journal file index cb");
1451
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_DATABASE_ROTATE, "db rotate cb");
1452
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_CTX_SHUTDOWN, "ctx shutdown cb");
1453
+ worker_register_job_name(RRDENG_OPCODE_MAX + RRDENG_OPCODE_CTX_QUIESCE, "ctx quiesce cb");
1454
+
1455
+ // special jobs
1456
+ worker_register_job_name(RRDENG_TIMER_CB, "timer");
1457
+ worker_register_job_name(RRDENG_FLUSH_TRANSACTION_BUFFER_CB, "transaction buffer flush cb");
1458
+
1459
+ worker_register_job_custom_metric(RRDENG_OPCODES_WAITING, "opcodes waiting", "opcodes", WORKER_METRIC_ABSOLUTE);
1460
+ worker_register_job_custom_metric(RRDENG_WORKS_DISPATCHED, "works dispatched", "works", WORKER_METRIC_ABSOLUTE);
1461
+ worker_register_job_custom_metric(RRDENG_WORKS_EXECUTING, "works executing", "works", WORKER_METRIC_ABSOLUTE);
1462
+
1463
+ extent_buffer_init();
1464
+
1465
+ struct rrdeng_main *main = arg;
1466
+ enum rrdeng_opcode opcode;
1467
+ struct rrdeng_cmd cmd;
1468
+ main->tid = gettid();
1469
+
1470
+ fatal_assert(0 == uv_timer_start(&main->timer, timer_cb, TIMER_PERIOD_MS, TIMER_PERIOD_MS));
1471
+
1472
+ bool shutdown = false;
1473
+ while (likely(!shutdown)) {
1474
worker_is_idle();
1379
- uv_run(loop, UV_RUN_DEFAULT);
1380
- worker_is_busy(RRDENG_MAX_OPCODE);
1381
- rrdeng_cleanup_finished_threads(wc);
1475
+ uv_run(&main->loop, UV_RUN_DEFAULT);
1476
1477
/* wait for commands */
1384
- cmd_batch_size = 0;
1478
do {
1386
- /*
1387
- * Avoid starving the loop when there are too many commands coming in.
1388
- * timer_cb will interrupt the loop again to allow serving more commands.
1389
- */
1390
- if (unlikely(cmd_batch_size >= MAX_CMD_BATCH_SIZE))
1391
- break;
1392
-
1393
- cmd = rrdeng_deq_cmd(wc);
1479
+ worker_is_busy(RRDENG_OPCODE_MAX);
1480
+ cmd = rrdeng_deq_cmd();
1481
opcode = cmd.opcode;
1395
- ++cmd_batch_size;
1482
1397
- if(likely(opcode != RRDENG_NOOP))
1398
- worker_is_busy(opcode);
1483
+ worker_is_busy(opcode);
1484
1485
switch (opcode) {
1401
- case RRDENG_NOOP:
1402
- /* the command queue was empty, do nothing */
1403
- break;
1404
- case RRDENG_SHUTDOWN:
1405
- shutdown = 1;
1406
- break;
1407
- case RRDENG_QUIESCE:
1408
- ctx->drop_metrics_under_page_cache_pressure = 0;
1409
- ctx->quiesce = SET_QUIESCE;
1410
- fatal_assert(0 == uv_timer_stop(&timer_req));
1411
- uv_close((uv_handle_t *)&timer_req, NULL);
1412
- while (do_flush_pages(wc, 1, NULL)) {
1413
- ; /* Force flushing of all committed pages. */
1486
+ case RRDENG_OPCODE_EXTENT_READ: {
1487
+ struct rrdengine_instance *ctx = cmd.ctx;
1488
+ EPDL *epdl = cmd.data;
1489
+ work_dispatch(ctx, epdl, NULL, opcode, extent_read_tp_worker, after_extent_read);
1490
+ break;
1491
}
1415
- wal_flush_transaction_buffer(wc);
1416
- if (!rrdeng_threads_alive(wc)) {
1417
- ctx->quiesce = QUIESCED;
1418
- completion_mark_complete(&ctx->rrdengine_completion);
1492
+
1493
+ case RRDENG_OPCODE_PREP_QUERY: {
1494
+ struct rrdengine_instance *ctx = cmd.ctx;
1495
+ PDC *pdc = cmd.data;
1496
+ work_dispatch(ctx, pdc, NULL, opcode, query_prep_tp_worker, after_prep_query);
1497
+ break;
1498
}
1420
- break;
1421
- case RRDENG_READ_PAGE:
1422
- do_read_extent(wc, &cmd.read_page.page_cache_descr, 1, 0);
1423
- break;
1424
- case RRDENG_READ_EXTENT:
1425
- do_read_extent(wc, cmd.read_extent.page_cache_descr, cmd.read_extent.page_count, 1);
1426
- if (unlikely(!set_name)) {
1427
- set_name = 1;
1428
- uv_thread_set_name_np(ctx->worker_config.thread, "DBENGINE");
1499
+
1500
+ case RRDENG_OPCODE_FLUSH_PAGES: {
1501
+ struct rrdengine_instance *ctx = cmd.ctx;
1502
+ struct page_descr_with_data *base = cmd.data;
1503
+ struct completion *completion = cmd.completion; // optional
1504
+ // for the datafile and the journalfile
1505
+ do_flush_extent(ctx, base, completion);
1506
+ break;
1507
}
1430
- break;
1431
- case RRDENG_COMMIT_PAGE:
1432
- do_commit_transaction(wc, STORE_DATA, NULL);
1433
- break;
1434
- case RRDENG_FLUSH_PAGES: {
1435
- if (wc->now_invalidating_dirty_pages) {
1436
- /* Do not flush if the disk cannot keep up */
1437
- completion_mark_complete(cmd.completion);
1438
- } else {
1439
- (void)do_flush_pages(wc, 1, cmd.completion);
1508
+
1509
+ case RRDENG_OPCODE_FLUSHED_TO_OPEN: {
1510
+ struct rrdengine_instance *ctx = cmd.ctx;
1511
+ uv_fs_t *uv_fs_request = cmd.data;
1512
+ struct extent_io_descriptor *xt_io_descr = uv_fs_request->data;
1513
+ struct completion *completion = xt_io_descr->completion;
1514
+ work_dispatch(ctx, uv_fs_request, completion, opcode, extent_flushed_to_open_tp_worker, after_extent_flushed_to_open);
1515
+ break;
1516
+ }
1517
+
1518
+ case RRDENG_OPCODE_FLUSH_INIT: {
1519
+ if(!rrdeng_main.flush_running) {
1520
+
1521
+ rrdeng_main.flush_running = true;
1522
+ if(!work_dispatch(NULL, NULL, NULL, opcode, cache_flush_tp_worker, after_do_cache_flush))
1523
+ rrdeng_main.flush_running = false;
1524
+
1525
+ }
1526
+ break;
1527
+ }
1528
+
1529
+ case RRDENG_OPCODE_EVICT_INIT: {
1530
+ if(!rrdeng_main.evict_running) {
1531
+
1532
+ rrdeng_main.evict_running = true;
1533
+ if (!work_dispatch(NULL, NULL, NULL, opcode, cache_evict_tp_worker, after_do_cache_evict))
1534
+ rrdeng_main.evict_running = false;
1535
+
1536
+ }
1537
+ break;
1538
+ }
1539
+
1540
+// case RRDENG_OPCODE_DATAFILE_CREATE: {
1541
+// struct rrdengine_instance *ctx = cmd.ctx;
1542
+// struct rrdengine_datafile *datafile = ctx->datafiles.first->prev;
1543
+// if(datafile->pos > rrdeng_target_data_file_size(ctx) &&
1544
+// create_new_datafile_pair(ctx, 1, ctx->last_fileno + 1) == 0) {
1545
+// ++ctx->last_fileno;
1546
+// rrdeng_enq_cmd(ctx, RRDENG_OPCODE_JOURNAL_FILE_INDEX, datafile, NULL, STORAGE_PRIORITY_CRITICAL);
1547
+// }
1548
+// break;
1549
+// }
1550
+
1551
+ case RRDENG_OPCODE_JOURNAL_FILE_INDEX: {
1552
+ struct rrdengine_instance *ctx = cmd.ctx;
1553
+ struct rrdengine_datafile *datafile = cmd.data;
1554
+ if(!ctx->worker_config.migration_to_v2_running) {
1555
+
1556
+ ctx->worker_config.migration_to_v2_running = true;
1557
+ if (!work_dispatch(ctx, datafile, NULL, opcode, journal_v2_indexing_tp_worker, after_journal_v2_indexing))
1558
+ ctx->worker_config.migration_to_v2_running = false;
1559
+
1560
+ }
1561
+ break;
1562
+ }
1563
+
1564
+ case RRDENG_OPCODE_DATABASE_ROTATE: {
1565
+ struct rrdengine_instance *ctx = cmd.ctx;
1566
+ if (!ctx->worker_config.now_deleting_files &&
1567
+ ctx->datafiles.first->next != NULL &&
1568
+ ctx->datafiles.first->next->next != NULL &&
1569
+ ctx->disk_space > MAX(ctx->max_disk_space, 2 * ctx->metric_API_max_producers * RRDENG_BLOCK_SIZE)) {
1570
+
1571
+ ctx->worker_config.now_deleting_files = true;
1572
+ if(!work_dispatch(ctx, NULL, NULL, opcode, database_rotate_tp_worker, after_database_rotate))
1573
+ ctx->worker_config.now_deleting_files = false;
1574
+
1575
+ }
1576
+ break;
1577
+ }
1578
+
1579
+ case RRDENG_OPCODE_CTX_QUIESCE: {
1580
+ // a ctx will shutdown shortly
1581
+ struct rrdengine_instance *ctx = cmd.ctx;
1582
+ __atomic_store_n(&ctx->quiesce, SET_QUIESCE, __ATOMIC_RELEASE);
1583
+ work_dispatch(ctx, NULL, NULL, opcode,
1584
+ flush_all_hot_and_dirty_pages_of_section_tp_worker,
1585
+ after_flush_all_hot_and_dirty_pages_of_section);
1586
+ break;
1587
+ }
1588
+
1589
+ case RRDENG_OPCODE_CTX_SHUTDOWN: {
1590
+ // a ctx is shutting down
1591
+ struct rrdengine_instance *ctx = cmd.ctx;
1592
+ struct completion *completion = cmd.completion;
1593
+ work_dispatch(ctx, NULL, completion, opcode, ctx_shutdown_tp_worker, after_ctx_shutdown);
1594
+ break;
1595
+ }
1596
+
1597
+ case RRDENG_OPCODE_NOOP: {
1598
+ /* the command queue was empty, do nothing */
1599
+ break;
1600
+ }
1601
+
1602
+ // not opcodes
1603
+ case RRDENG_OPCODE_MAX:
1604
+ default: {
1605
+ internal_fatal(true, "DBENGINE: unknown opcode");
1606
+ break;
1607
}
1441
- break;
1442
- case RRDENG_INVALIDATE_OLDEST_MEMORY_PAGE:
1443
- rrdeng_invalidate_oldest_committed(wc);
1444
- break;
1445
- }
1446
- default:
1447
- debug(D_RRDENGINE, "%s: default.", __func__);
1448
- break;
1608
}
1450
- } while (opcode != RRDENG_NOOP);
1609
+
1610
+ } while (opcode != RRDENG_OPCODE_NOOP);
1611
}
1612
1613
/* cleanup operations of the event loop */
1454
- info("Shutting down RRD engine event loop for tier %d", ctx->tier);
1614
+ info("DBENGINE: shutting down dbengine thread");
1615
1616
/*
1617
* uv_async_send after uv_close does not seem to crash in linux at the moment,
1618
* it is however undocumented behaviour and we need to be aware if this becomes
1619
* an issue in the future.
1620
*/
1461
- uv_close((uv_handle_t *)&wc->async, NULL);
1462
-
1463
- while (do_flush_pages(wc, 1, NULL)) {
1464
- ; /* Force flushing of all committed pages. */
1465
- }
1466
- wal_flush_transaction_buffer(wc);
1467
- uv_run(loop, UV_RUN_DEFAULT);
1468
-
1469
- info("Shutting down RRD engine event loop for tier %d complete", ctx->tier);
1470
- /* TODO: don't let the API block by waiting to enqueue commands */
1471
- uv_cond_destroy(&wc->cmd_cond);
1472
-/* uv_mutex_destroy(&wc->cmd_mutex); */
1473
- fatal_assert(0 == uv_loop_close(loop));
1474
- freez(loop);
1475
-
1621
+ uv_close((uv_handle_t *)&main->async, NULL);
1622
+ uv_timer_stop(&main->timer);
1623
+ uv_close((uv_handle_t *)&main->timer, NULL);
1624
+ uv_run(&main->loop, UV_RUN_DEFAULT);
1625
+ uv_loop_close(&main->loop);
1626
worker_unregister();
1477
- return;
1478
-
1479
-error_after_timer_init:
1480
- uv_close((uv_handle_t *)&wc->async, NULL);
1481
-error_after_async_init:
1482
- fatal_assert(0 == uv_loop_close(loop));
1483
-error_after_loop_init:
1484
- freez(loop);
1485
-
1486
- wc->error = UV_EAGAIN;
1487
- /* wake up initialization thread */
1488
- completion_mark_complete(&ctx->rrdengine_completion);
1489
- worker_unregister();
1490
-}
1491
-
1492
-/* C entry point for development purposes
1493
- * make "LDFLAGS=-errdengine_main"
1494
- */
1495
-void rrdengine_main(void)
1496
-{
1497
- int ret;
1498
- struct rrdengine_instance *ctx;
1499
-
1500
- sanity_check();
1501
- ret = rrdeng_init(NULL, &ctx, "/tmp", RRDENG_MIN_PAGE_CACHE_SIZE_MB, RRDENG_MIN_DISK_SPACE_MB, 0);
1502
- if (ret) {
1503
- exit(ret);
1504
- }
1505
- rrdeng_exit(ctx);
1506
- fprintf(stderr, "Hello world!");
1507
- exit(0);
1627
}