/src/fluent-bit/plugins/in_storage_backlog/sb.c
Line | Count | Source |
1 | | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* Fluent Bit |
4 | | * ========== |
5 | | * Copyright (C) 2015-2026 The Fluent Bit Authors |
6 | | * |
7 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | * you may not use this file except in compliance with the License. |
9 | | * You may obtain a copy of the License at |
10 | | * |
11 | | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | | * |
13 | | * Unless required by applicable law or agreed to in writing, software |
14 | | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | * See the License for the specific language governing permissions and |
17 | | * limitations under the License. |
18 | | */ |
19 | | |
20 | | #include <fluent-bit/flb_output.h> |
21 | | #include <fluent-bit/flb_input_plugin.h> |
22 | | #include <fluent-bit/flb_input_chunk.h> |
23 | | #include <fluent-bit/flb_storage.h> |
24 | | #include <fluent-bit/flb_utils.h> |
25 | | #include <chunkio/chunkio.h> |
26 | | #include <chunkio/cio_error.h> |
27 | | |
28 | | #include <sys/types.h> |
29 | | #include <sys/stat.h> |
30 | | |
31 | | #ifndef FLB_SYSTEM_WINDOWS |
32 | | #include <unistd.h> |
33 | | #endif |
34 | | |
35 | | struct sb_out_chunk { |
36 | | struct cio_chunk *chunk; |
37 | | struct cio_stream *stream; |
38 | | size_t size; |
39 | | struct mk_list _head; |
40 | | }; |
41 | | |
42 | | struct sb_out_queue { |
43 | | struct flb_output_instance *ins; |
44 | | struct mk_list chunks; /* head for every sb_out_chunk */ |
45 | | struct mk_list _head; |
46 | | }; |
47 | | |
48 | | struct flb_sb { |
49 | | int coll_fd; /* collector id */ |
50 | | size_t mem_limit; /* memory limit */ |
51 | | struct flb_input_instance *ins; /* input instance */ |
52 | | struct cio_ctx *cio; /* chunk i/o instance */ |
53 | | struct mk_list backlogs; /* list of all pending chunks segregated by output plugin */ |
54 | | flb_route_mask_element *dummy_routes_mask; /* dummy route mask used when segregating events */ |
55 | | }; |
56 | | |
57 | | |
58 | | static inline struct flb_sb *sb_get_context(struct flb_config *config); |
59 | | |
60 | | static struct sb_out_chunk *sb_allocate_chunk(struct cio_chunk *chunk, |
61 | | struct cio_stream *stream, |
62 | | size_t size); |
63 | | |
64 | | static void sb_destroy_chunk(struct sb_out_chunk *chunk); |
65 | | |
66 | | static void sb_destroy_backlog(struct sb_out_queue *backlog, struct flb_sb *context); |
67 | | |
68 | | static int sb_allocate_backlogs(struct flb_sb *ctx); |
69 | | |
70 | | static void sb_destroy_backlogs(struct flb_sb *ctx); |
71 | | |
72 | | static struct sb_out_queue *sb_find_segregated_backlog_by_output_plugin_instance( |
73 | | struct flb_output_instance *output_plugin, |
74 | | struct flb_sb *context); |
75 | | |
76 | | static void sb_remove_chunk_from_segregated_backlog(struct cio_chunk *target_chunk, |
77 | | struct sb_out_queue *backlog, |
78 | | int destroy); |
79 | | |
80 | | static void sb_remove_chunk_from_segregated_backlogs(struct cio_chunk *chunk, |
81 | | struct flb_sb *context); |
82 | | |
83 | | static int sb_append_chunk_to_segregated_backlog(struct cio_chunk *target_chunk, |
84 | | struct cio_stream *stream, |
85 | | size_t target_chunk_size, |
86 | | struct sb_out_queue *backlog); |
87 | | |
88 | | static int sb_append_chunk_to_segregated_backlogs(struct cio_chunk *target_chunk, |
89 | | struct cio_stream *stream, |
90 | | struct flb_sb *context); |
91 | | |
92 | | int sb_segregate_chunks(struct flb_config *config); |
93 | | |
94 | | int sb_release_output_queue_space(struct flb_output_instance *output_plugin, |
95 | | ssize_t *required_space); |
96 | | |
97 | | ssize_t sb_get_releasable_output_queue_space(struct flb_output_instance *output_plugin, |
98 | | size_t required_space); |
99 | | |
100 | | |
101 | | static inline struct flb_sb *sb_get_context(struct flb_config *config) |
102 | 25 | { |
103 | 25 | if (config == NULL) { |
104 | 0 | return NULL; |
105 | 0 | } |
106 | | |
107 | 25 | if (config->storage_input_plugin == NULL) { |
108 | 10 | return NULL; |
109 | 10 | } |
110 | | |
111 | 15 | return ((struct flb_input_instance *) config->storage_input_plugin)->context; |
112 | 25 | } |
113 | | |
114 | | static struct sb_out_chunk *sb_allocate_chunk(struct cio_chunk *chunk, |
115 | | struct cio_stream *stream, |
116 | | size_t size) |
117 | 0 | { |
118 | 0 | struct sb_out_chunk *result; |
119 | |
|
120 | 0 | result = (struct sb_out_chunk *) flb_calloc(1, sizeof(struct sb_out_chunk)); |
121 | |
|
122 | 0 | if (result != NULL) { |
123 | 0 | result->chunk = chunk; |
124 | 0 | result->stream = stream; |
125 | 0 | result->size = size; |
126 | 0 | } |
127 | |
|
128 | 0 | return result; |
129 | 0 | } |
130 | | |
131 | | static void sb_destroy_chunk(struct sb_out_chunk *chunk) |
132 | 0 | { |
133 | 0 | flb_free(chunk); |
134 | 0 | } |
135 | | |
136 | | static void sb_destroy_backlog(struct sb_out_queue *backlog, struct flb_sb *context) |
137 | 15 | { |
138 | 15 | struct mk_list *chunk_iterator_tmp; |
139 | 15 | struct mk_list *chunk_iterator; |
140 | 15 | struct sb_out_chunk *chunk; |
141 | | |
142 | 15 | mk_list_foreach_safe(chunk_iterator, chunk_iterator_tmp, &backlog->chunks) { |
143 | 0 | chunk = mk_list_entry(chunk_iterator, struct sb_out_chunk, _head); |
144 | |
|
145 | 0 | sb_remove_chunk_from_segregated_backlogs(chunk->chunk, context); |
146 | 0 | } |
147 | 15 | } |
148 | | |
149 | | static int sb_allocate_backlogs(struct flb_sb *context) |
150 | 15 | { |
151 | 15 | struct mk_list *output_plugin_iterator; |
152 | 15 | struct flb_output_instance *output_plugin; |
153 | 15 | struct sb_out_queue *backlog; |
154 | | |
155 | 15 | mk_list_foreach(output_plugin_iterator, &context->ins->config->outputs) { |
156 | 15 | output_plugin = mk_list_entry(output_plugin_iterator, |
157 | 15 | struct flb_output_instance, |
158 | 15 | _head); |
159 | | |
160 | 15 | backlog = (struct sb_out_queue *) \ |
161 | 15 | flb_calloc(1, sizeof(struct sb_out_queue)); |
162 | | |
163 | 15 | if (backlog == NULL) { |
164 | 0 | sb_destroy_backlogs(context); |
165 | |
|
166 | 0 | return -1; |
167 | 0 | } |
168 | | |
169 | 15 | backlog->ins = output_plugin; |
170 | | |
171 | 15 | mk_list_init(&backlog->chunks); |
172 | | |
173 | 15 | mk_list_add(&backlog->_head, &context->backlogs); |
174 | 15 | } |
175 | | |
176 | 15 | return 0; |
177 | 15 | } |
178 | | |
179 | | static void sb_destroy_backlogs(struct flb_sb *context) |
180 | 15 | { |
181 | 15 | struct mk_list *backlog_iterator_tmp; |
182 | 15 | struct mk_list *backlog_iterator; |
183 | 15 | struct sb_out_queue *backlog; |
184 | | |
185 | 15 | mk_list_foreach_safe(backlog_iterator, backlog_iterator_tmp, &context->backlogs) { |
186 | 15 | backlog = mk_list_entry(backlog_iterator, struct sb_out_queue, _head); |
187 | | |
188 | 15 | mk_list_del(&backlog->_head); |
189 | | |
190 | 15 | sb_destroy_backlog(backlog, context); |
191 | | |
192 | 15 | flb_free(backlog); |
193 | 15 | } |
194 | 15 | } |
195 | | |
196 | | static struct sb_out_queue *sb_find_segregated_backlog_by_output_plugin_instance( |
197 | | struct flb_output_instance *output_plugin, |
198 | | struct flb_sb *context) |
199 | 0 | { |
200 | 0 | struct mk_list *backlog_iterator; |
201 | 0 | struct sb_out_queue *backlog; |
202 | |
|
203 | 0 | mk_list_foreach(backlog_iterator, &context->backlogs) { |
204 | 0 | backlog = mk_list_entry(backlog_iterator, struct sb_out_queue, _head); |
205 | |
|
206 | 0 | if (output_plugin == backlog->ins) { |
207 | 0 | return backlog; |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | 0 | return NULL; |
212 | 0 | } |
213 | | |
214 | | static void sb_remove_chunk_from_segregated_backlog(struct cio_chunk *target_chunk, |
215 | | struct sb_out_queue *backlog, |
216 | | int destroy) |
217 | 0 | { |
218 | 0 | struct mk_list *chunk_iterator_tmp; |
219 | 0 | struct mk_list *chunk_iterator; |
220 | 0 | struct sb_out_chunk *chunk; |
221 | |
|
222 | 0 | mk_list_foreach_safe(chunk_iterator, chunk_iterator_tmp, &backlog->chunks) { |
223 | 0 | chunk = mk_list_entry(chunk_iterator, struct sb_out_chunk, _head); |
224 | |
|
225 | 0 | if (chunk->chunk == target_chunk) { |
226 | 0 | mk_list_del(&chunk->_head); |
227 | |
|
228 | 0 | if (chunk->size > backlog->ins->fs_backlog_chunks_size) { |
229 | 0 | flb_warn("[storage backlog] filesystem chunk accounting underflow for " |
230 | 0 | "output %s: current=%zu, subtract=%zu; resetting to zero", |
231 | 0 | flb_output_name(backlog->ins), |
232 | 0 | backlog->ins->fs_backlog_chunks_size, |
233 | 0 | chunk->size); |
234 | 0 | backlog->ins->fs_backlog_chunks_size = 0; |
235 | 0 | } |
236 | 0 | else { |
237 | 0 | backlog->ins->fs_backlog_chunks_size -= chunk->size; |
238 | 0 | } |
239 | |
|
240 | 0 | if (destroy) { |
241 | 0 | sb_destroy_chunk(chunk); |
242 | 0 | } |
243 | |
|
244 | 0 | break; |
245 | 0 | } |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | | static void sb_remove_chunk_from_segregated_backlogs(struct cio_chunk *target_chunk, |
250 | | struct flb_sb *context) |
251 | 0 | { |
252 | 0 | struct mk_list *backlog_iterator; |
253 | 0 | struct sb_out_queue *backlog; |
254 | |
|
255 | 0 | mk_list_foreach(backlog_iterator, &context->backlogs) { |
256 | 0 | backlog = mk_list_entry(backlog_iterator, struct sb_out_queue, _head); |
257 | |
|
258 | 0 | sb_remove_chunk_from_segregated_backlog(target_chunk, backlog, FLB_TRUE); |
259 | 0 | } |
260 | 0 | } |
261 | | |
262 | | static int sb_append_chunk_to_segregated_backlog(struct cio_chunk *target_chunk, |
263 | | struct cio_stream *stream, |
264 | | size_t target_chunk_size, |
265 | | struct sb_out_queue *backlog) |
266 | 0 | { |
267 | 0 | struct sb_out_chunk *chunk; |
268 | |
|
269 | 0 | chunk = sb_allocate_chunk(target_chunk, stream, target_chunk_size); |
270 | 0 | if (chunk == NULL) { |
271 | 0 | flb_errno(); |
272 | 0 | return -1; |
273 | 0 | } |
274 | | |
275 | 0 | mk_list_add(&chunk->_head, &backlog->chunks); |
276 | |
|
277 | 0 | if (target_chunk_size > SIZE_MAX - backlog->ins->fs_backlog_chunks_size) { |
278 | 0 | flb_warn("[storage backlog] filesystem chunk accounting overflow for output %s: " |
279 | 0 | "current=%zu, add=%zu; saturating at maximum", |
280 | 0 | flb_output_name(backlog->ins), |
281 | 0 | backlog->ins->fs_backlog_chunks_size, |
282 | 0 | target_chunk_size); |
283 | 0 | backlog->ins->fs_backlog_chunks_size = SIZE_MAX; |
284 | 0 | } |
285 | 0 | else { |
286 | 0 | backlog->ins->fs_backlog_chunks_size += target_chunk_size; |
287 | 0 | } |
288 | |
|
289 | 0 | return 0; |
290 | 0 | } |
291 | | |
292 | | static int sb_append_chunk_to_segregated_backlogs(struct cio_chunk *target_chunk, |
293 | | struct cio_stream *stream, |
294 | | struct flb_sb *context) |
295 | 0 | { |
296 | 0 | struct flb_input_chunk dummy_input_chunk; |
297 | 0 | struct mk_list *tmp; |
298 | 0 | struct mk_list *head; |
299 | 0 | size_t chunk_size; |
300 | 0 | struct sb_out_queue *backlog; |
301 | 0 | int tag_len; |
302 | 0 | const char * tag_buf; |
303 | 0 | int result; |
304 | 0 | size_t slots; |
305 | |
|
306 | 0 | memset(&dummy_input_chunk, 0, sizeof(struct flb_input_chunk)); |
307 | |
|
308 | 0 | slots = flb_routes_mask_get_slots(context->ins->config->router); |
309 | 0 | memset(context->dummy_routes_mask, |
310 | 0 | 0, |
311 | 0 | slots * sizeof(flb_route_mask_element)); |
312 | |
|
313 | 0 | dummy_input_chunk.in = context->ins; |
314 | 0 | dummy_input_chunk.chunk = target_chunk; |
315 | 0 | dummy_input_chunk.routes_mask = context->dummy_routes_mask; |
316 | |
|
317 | 0 | chunk_size = cio_chunk_get_real_size(target_chunk); |
318 | |
|
319 | 0 | if (chunk_size < 0) { |
320 | 0 | flb_warn("[storage backlog] could not get real size of chunk %s/%s", |
321 | 0 | stream->name, target_chunk->name); |
322 | |
|
323 | 0 | return -1; |
324 | 0 | } |
325 | | |
326 | 0 | result = flb_input_chunk_get_tag(&dummy_input_chunk, &tag_buf, &tag_len); |
327 | 0 | if (result == -1) { |
328 | 0 | flb_error("[storage backlog] could not retrieve chunk tag from %s/%s, " |
329 | 0 | "removing it from the queue", |
330 | 0 | stream->name, target_chunk->name); |
331 | |
|
332 | 0 | return -2; |
333 | 0 | } |
334 | | |
335 | 0 | result = flb_routes_mask_set_by_tag(dummy_input_chunk.routes_mask, tag_buf, tag_len, |
336 | 0 | context->ins); |
337 | 0 | if (result == 0) { |
338 | 0 | return -4; |
339 | 0 | } |
340 | | |
341 | 0 | mk_list_foreach_safe(head, tmp, &context->backlogs) { |
342 | 0 | backlog = mk_list_entry(head, struct sb_out_queue, _head); |
343 | 0 | if (flb_routes_mask_get_bit(dummy_input_chunk.routes_mask, |
344 | 0 | backlog->ins->id, |
345 | 0 | backlog->ins->config->router)) { |
346 | 0 | result = sb_append_chunk_to_segregated_backlog(target_chunk, stream, |
347 | 0 | chunk_size, backlog); |
348 | 0 | if (result) { |
349 | 0 | return -3; |
350 | 0 | } |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | 0 | return 0; |
355 | 0 | } |
356 | | |
357 | | static inline int sb_is_rejected_stream(struct flb_config *config, |
358 | | struct cio_stream *stream) |
359 | 30 | { |
360 | 30 | const char *rp; |
361 | | |
362 | 30 | if (!config || !stream || !stream->name) { |
363 | 0 | return FLB_FALSE; |
364 | 0 | } |
365 | | |
366 | 30 | if (config->storage_keep_rejected != FLB_TRUE) { |
367 | 30 | return FLB_FALSE; |
368 | 30 | } |
369 | | |
370 | 0 | rp = config->storage_rejected_path ? |
371 | 0 | config->storage_rejected_path : "rejected"; |
372 | |
|
373 | 0 | return strcmp(stream->name, rp) == 0; |
374 | 30 | } |
375 | | |
376 | | int sb_segregate_chunks(struct flb_config *config) |
377 | 25 | { |
378 | 25 | int ret; |
379 | 25 | size_t size; |
380 | 25 | struct mk_list *tmp; |
381 | 25 | struct mk_list *stream_iterator; |
382 | 25 | struct mk_list *chunk_iterator; |
383 | 25 | int chunk_error; |
384 | 25 | struct flb_sb *context; |
385 | 25 | struct cio_stream *stream; |
386 | 25 | struct cio_chunk *chunk; |
387 | | |
388 | 25 | context = sb_get_context(config); |
389 | | |
390 | 25 | if (context == NULL) { |
391 | 10 | return 0; |
392 | 10 | } |
393 | | |
394 | 15 | ret = sb_allocate_backlogs(context); |
395 | 15 | if (ret) { |
396 | 0 | return -2; |
397 | 0 | } |
398 | | |
399 | 30 | mk_list_foreach(stream_iterator, &context->cio->streams) { |
400 | 30 | stream = mk_list_entry(stream_iterator, struct cio_stream, _head); |
401 | | |
402 | | /* DLQ stream is not part of backlog. Just skip. */ |
403 | 30 | if (sb_is_rejected_stream(config, stream)) { |
404 | 0 | flb_debug("[storage backlog] skipping DLQ stream '%s'", stream->name); |
405 | 0 | continue; |
406 | 0 | } |
407 | | |
408 | 30 | mk_list_foreach_safe(chunk_iterator, tmp, &stream->chunks) { |
409 | 0 | chunk = mk_list_entry(chunk_iterator, struct cio_chunk, _head); |
410 | |
|
411 | 0 | if (!cio_chunk_is_up(chunk)) { |
412 | 0 | ret = cio_chunk_up_force(chunk); |
413 | 0 | if (ret == CIO_CORRUPTED) { |
414 | 0 | if (config->storage_del_bad_chunks) { |
415 | 0 | chunk_error = cio_error_get(chunk); |
416 | |
|
417 | 0 | if (chunk_error == CIO_ERR_BAD_CHECKSUM || |
418 | 0 | chunk_error == CIO_ERR_BAD_FILE_SIZE || |
419 | 0 | chunk_error == CIO_ERR_BAD_LAYOUT) |
420 | 0 | { |
421 | 0 | flb_plg_error(context->ins, "discarding irrecoverable chunk %s/%s", stream->name, chunk->name); |
422 | |
|
423 | 0 | cio_chunk_close(chunk, CIO_TRUE); |
424 | 0 | } |
425 | 0 | } |
426 | |
|
427 | 0 | continue; |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | 0 | if (!cio_chunk_is_up(chunk)) { |
432 | 0 | return -3; |
433 | 0 | } |
434 | | |
435 | | /* try to segregate a chunk */ |
436 | 0 | ret = sb_append_chunk_to_segregated_backlogs(chunk, stream, context); |
437 | 0 | if (ret) { |
438 | | /* |
439 | | * Leave chunks without a route on disk so a future configuration |
440 | | * can reconsider them. Closing the ChunkIO handle removes them |
441 | | * from this context's active chunk accounting without deleting |
442 | | * the underlying files. |
443 | | */ |
444 | 0 | if (ret == -4) { |
445 | 0 | flb_plg_info(context->ins, |
446 | 0 | "no matching route for %s/%s, keeping it on disk", |
447 | 0 | stream->name, chunk->name); |
448 | 0 | cio_chunk_close(chunk, CIO_FALSE); |
449 | 0 | continue; |
450 | 0 | } |
451 | | |
452 | | /* |
453 | | * if the chunk could not be segregated, just remove it from the |
454 | | * queue, delete it and continue. |
455 | | */ |
456 | | |
457 | | /* If the tag cannot be read it cannot be routed, let's remove it */ |
458 | 0 | if (ret == -2) { |
459 | 0 | cio_chunk_close(chunk, CIO_TRUE); |
460 | 0 | continue; |
461 | 0 | } |
462 | | |
463 | | /* |
464 | | * |
465 | | * if content size is zero, it's safe to 'delete it'. |
466 | | */ |
467 | 0 | size = cio_chunk_get_content_size(chunk); |
468 | 0 | if (size <= 0) { |
469 | 0 | cio_chunk_close(chunk, CIO_TRUE); |
470 | 0 | } |
471 | 0 | else { |
472 | 0 | cio_chunk_close(chunk, CIO_FALSE); |
473 | 0 | } |
474 | 0 | continue; |
475 | 0 | } |
476 | | |
477 | | /* lock the chunk */ |
478 | 0 | flb_plg_info(context->ins, "register %s/%s", stream->name, chunk->name); |
479 | |
|
480 | 0 | cio_chunk_lock(chunk); |
481 | 0 | cio_chunk_down(chunk); |
482 | 0 | } |
483 | 30 | } |
484 | | |
485 | 15 | return 0; |
486 | 15 | } |
487 | | |
488 | | ssize_t sb_get_releasable_output_queue_space(struct flb_output_instance *output_plugin, |
489 | | size_t required_space) |
490 | 0 | { |
491 | 0 | ssize_t releasable_space; |
492 | 0 | struct mk_list *chunk_iterator; |
493 | 0 | struct flb_sb *context; |
494 | 0 | struct sb_out_queue *backlog; |
495 | 0 | struct sb_out_chunk *chunk; |
496 | |
|
497 | 0 | context = sb_get_context(output_plugin->config); |
498 | |
|
499 | 0 | if (context == NULL) { |
500 | 0 | return 0; |
501 | 0 | } |
502 | | |
503 | 0 | backlog = sb_find_segregated_backlog_by_output_plugin_instance( |
504 | 0 | output_plugin, context); |
505 | |
|
506 | 0 | if (backlog == NULL) { |
507 | 0 | return 0; |
508 | 0 | } |
509 | | |
510 | 0 | releasable_space = 0; |
511 | |
|
512 | 0 | mk_list_foreach(chunk_iterator, &backlog->chunks) { |
513 | 0 | chunk = mk_list_entry(chunk_iterator, struct sb_out_chunk, _head); |
514 | |
|
515 | 0 | releasable_space += chunk->size; |
516 | |
|
517 | 0 | if (releasable_space >= required_space) { |
518 | 0 | break; |
519 | 0 | } |
520 | 0 | } |
521 | |
|
522 | 0 | return releasable_space; |
523 | 0 | } |
524 | | |
525 | | int sb_release_output_queue_space(struct flb_output_instance *output_plugin, |
526 | | ssize_t *required_space) |
527 | 0 | { |
528 | 0 | struct mk_list *chunk_iterator_tmp; |
529 | 0 | struct cio_chunk *underlying_chunk; |
530 | 0 | struct mk_list *chunk_iterator; |
531 | 0 | size_t released_space; |
532 | 0 | struct flb_sb *context; |
533 | 0 | struct sb_out_queue *backlog; |
534 | 0 | struct sb_out_chunk *chunk; |
535 | |
|
536 | 0 | context = sb_get_context(output_plugin->config); |
537 | |
|
538 | 0 | if (context == NULL) { |
539 | 0 | return -1; |
540 | 0 | } |
541 | | |
542 | 0 | backlog = sb_find_segregated_backlog_by_output_plugin_instance( |
543 | 0 | output_plugin, context); |
544 | |
|
545 | 0 | if (backlog == NULL) { |
546 | 0 | return -2; |
547 | 0 | } |
548 | | |
549 | 0 | released_space = 0; |
550 | |
|
551 | 0 | mk_list_foreach_safe(chunk_iterator, chunk_iterator_tmp, &backlog->chunks) { |
552 | 0 | chunk = mk_list_entry(chunk_iterator, struct sb_out_chunk, _head); |
553 | |
|
554 | 0 | released_space += chunk->size; |
555 | 0 | underlying_chunk = chunk->chunk; |
556 | |
|
557 | 0 | flb_warn("[storage backlog] chunk '%s' evicted from output queue to make room " |
558 | 0 | "under storage.total_limit_size: input=%s > output=%s " |
559 | 0 | "(out_id=%d), bytes=%zu, limit=%zu", |
560 | 0 | underlying_chunk->name, |
561 | 0 | chunk->stream->name, |
562 | 0 | flb_output_name(output_plugin), |
563 | 0 | output_plugin->id, |
564 | 0 | chunk->size, |
565 | 0 | output_plugin->total_limit_size); |
566 | |
|
567 | 0 | sb_remove_chunk_from_segregated_backlogs(underlying_chunk, context); |
568 | 0 | cio_chunk_close(underlying_chunk, FLB_TRUE); |
569 | |
|
570 | 0 | if (released_space >= *required_space) { |
571 | 0 | break; |
572 | 0 | } |
573 | 0 | } |
574 | |
|
575 | 0 | *required_space -= released_space; |
576 | |
|
577 | 0 | return 0; |
578 | 0 | } |
579 | | |
580 | | /* Collection callback */ |
581 | | static int cb_queue_chunks(struct flb_input_instance *in, |
582 | | struct flb_config *config, void *data) |
583 | 30 | { |
584 | 30 | size_t empty_output_queue_count; |
585 | 30 | struct mk_list *output_queue_iterator; |
586 | 30 | struct sb_out_queue *output_queue_instance; |
587 | 30 | struct sb_out_chunk *chunk_instance; |
588 | 30 | struct flb_sb *ctx; |
589 | 30 | struct flb_input_chunk *ic; |
590 | 30 | struct flb_input_chunk tmp_ic; |
591 | 30 | void *ch; |
592 | 30 | size_t total = 0; |
593 | 30 | ssize_t size; |
594 | 30 | int ret; |
595 | 30 | int event_type; |
596 | 30 | struct cio_chunk *underlying_chunk; |
597 | | |
598 | | /* Get context */ |
599 | 30 | ctx = (struct flb_sb *) data; |
600 | | |
601 | | /* Get the total number of bytes already enqueued */ |
602 | 30 | total = flb_input_chunk_total_size(in); |
603 | | |
604 | | /* If we already hitted our limit, just wait and re-check later */ |
605 | 30 | if (total >= ctx->mem_limit) { |
606 | 0 | return 0; |
607 | 0 | } |
608 | | |
609 | 30 | empty_output_queue_count = 0; |
610 | | |
611 | 60 | while (total < ctx->mem_limit && |
612 | 60 | empty_output_queue_count < mk_list_size(&ctx->backlogs)) { |
613 | 30 | empty_output_queue_count = 0; |
614 | | |
615 | 30 | mk_list_foreach(output_queue_iterator, &ctx->backlogs) { |
616 | 30 | output_queue_instance = mk_list_entry(output_queue_iterator, |
617 | 30 | struct sb_out_queue, |
618 | 30 | _head); |
619 | | |
620 | 30 | if (mk_list_is_empty(&output_queue_instance->chunks) != 0) { |
621 | 0 | chunk_instance = mk_list_entry_first(&output_queue_instance->chunks, |
622 | 0 | struct sb_out_chunk, |
623 | 0 | _head); |
624 | | |
625 | | /* Try to enqueue one chunk */ |
626 | | /* |
627 | | * All chunks on this backlog are 'file' based, always try to set |
628 | | * them up. We validate the status. |
629 | | */ |
630 | 0 | ret = cio_chunk_is_up(chunk_instance->chunk); |
631 | |
|
632 | 0 | if (ret == CIO_FALSE) { |
633 | 0 | ret = cio_chunk_up_force(chunk_instance->chunk); |
634 | |
|
635 | 0 | if (ret == CIO_CORRUPTED) { |
636 | 0 | flb_plg_error(ctx->ins, "removing corrupted chunk from the " |
637 | 0 | "queue %s:%s", |
638 | 0 | chunk_instance->stream->name, chunk_instance->chunk->name); |
639 | 0 | underlying_chunk = chunk_instance->chunk; |
640 | | |
641 | | /* |
642 | | * sb_remove_chunk_from_segregated_backlogs() releases chunk_instance, |
643 | | * so grab the pointer first and close the chunk afterwards. |
644 | | */ |
645 | 0 | sb_remove_chunk_from_segregated_backlogs(underlying_chunk, ctx); |
646 | 0 | cio_chunk_close(underlying_chunk, FLB_FALSE); |
647 | 0 | continue; |
648 | 0 | } |
649 | 0 | else if (ret == CIO_ERROR || ret == CIO_RETRY) { |
650 | 0 | continue; |
651 | 0 | } |
652 | 0 | } |
653 | | |
654 | | /* |
655 | | * Map the chunk file context into a temporary buffer since the |
656 | | * flb_input_chunk_get_event_type() interface needs an |
657 | | * struct fb_input_chunk argument. |
658 | | */ |
659 | 0 | tmp_ic.chunk = chunk_instance->chunk; |
660 | | |
661 | | /* Retrieve the event type: FLB_INPUT_LOGS, FLB_INPUT_METRICS of FLB_INPUT_TRACES */ |
662 | 0 | ret = flb_input_chunk_get_event_type(&tmp_ic); |
663 | 0 | if (ret == -1) { |
664 | 0 | flb_plg_error(ctx->ins, "removing chunk with wrong metadata " |
665 | 0 | "from the queue %s:%s", |
666 | 0 | chunk_instance->stream->name, |
667 | 0 | chunk_instance->chunk->name); |
668 | 0 | underlying_chunk = chunk_instance->chunk; |
669 | 0 | sb_remove_chunk_from_segregated_backlogs(underlying_chunk, ctx); |
670 | 0 | cio_chunk_close(underlying_chunk, FLB_TRUE); |
671 | 0 | continue; |
672 | 0 | } |
673 | 0 | event_type = ret; |
674 | | |
675 | | /* get the number of bytes being used by the chunk */ |
676 | 0 | size = cio_chunk_get_content_size(chunk_instance->chunk); |
677 | 0 | if (size <= 0) { |
678 | 0 | flb_plg_error(ctx->ins, "removing empty chunk from the " |
679 | 0 | "queue %s:%s", |
680 | 0 | chunk_instance->stream->name, chunk_instance->chunk->name); |
681 | 0 | underlying_chunk = chunk_instance->chunk; |
682 | 0 | sb_remove_chunk_from_segregated_backlogs(underlying_chunk, ctx); |
683 | 0 | cio_chunk_close(underlying_chunk, FLB_TRUE); |
684 | 0 | continue; |
685 | 0 | } |
686 | | |
687 | 0 | ch = chunk_instance->chunk; |
688 | | |
689 | | /* Associate this backlog chunk to this instance into the engine */ |
690 | 0 | ic = flb_input_chunk_map(in, event_type, ch); |
691 | 0 | if (!ic) { |
692 | 0 | flb_plg_error(ctx->ins, "removing chunk %s:%s from the queue", |
693 | 0 | chunk_instance->stream->name, chunk_instance->chunk->name); |
694 | 0 | cio_chunk_down(chunk_instance->chunk); |
695 | | |
696 | | /* |
697 | | * If the file cannot be mapped, just drop it. Failures are all |
698 | | * associated with data corruption. |
699 | | */ |
700 | 0 | underlying_chunk = chunk_instance->chunk; |
701 | 0 | sb_remove_chunk_from_segregated_backlogs(underlying_chunk, ctx); |
702 | 0 | cio_chunk_close(underlying_chunk, FLB_TRUE); |
703 | 0 | continue; |
704 | 0 | } |
705 | | |
706 | 0 | flb_plg_info(ctx->ins, "queueing %s:%s", |
707 | 0 | chunk_instance->stream->name, chunk_instance->chunk->name); |
708 | | |
709 | | /* We are removing this chunk reference from this specific backlog |
710 | | * queue but we need to leave it in the remainder queues. |
711 | | */ |
712 | 0 | sb_remove_chunk_from_segregated_backlogs(chunk_instance->chunk, ctx); |
713 | 0 | cio_chunk_down(ch); |
714 | | |
715 | | /* check our limits */ |
716 | 0 | total += size; |
717 | 0 | } |
718 | 30 | else { |
719 | 30 | empty_output_queue_count++; |
720 | 30 | } |
721 | 30 | } |
722 | 30 | } |
723 | | |
724 | 30 | return 0; |
725 | 30 | } |
726 | | |
727 | | /* Initialize plugin */ |
728 | | static int cb_sb_init(struct flb_input_instance *in, |
729 | | struct flb_config *config, void *data) |
730 | 15 | { |
731 | 15 | int ret; |
732 | 15 | char mem[32]; |
733 | 15 | struct flb_sb *ctx; |
734 | 15 | size_t slots; |
735 | | |
736 | 15 | ctx = flb_calloc(1, sizeof(struct flb_sb)); |
737 | | |
738 | 15 | if (!ctx) { |
739 | 0 | flb_errno(); |
740 | 0 | return -1; |
741 | 0 | } |
742 | | |
743 | 15 | slots = flb_routes_mask_get_slots(config->router); |
744 | 15 | ctx->dummy_routes_mask = flb_calloc(slots, |
745 | 15 | sizeof(flb_route_mask_element)); |
746 | | |
747 | 15 | if (ctx->dummy_routes_mask == NULL) { |
748 | 0 | flb_errno(); |
749 | 0 | flb_free(ctx); |
750 | |
|
751 | 0 | flb_error("[storage backlog] could not allocate route mask elements"); |
752 | |
|
753 | 0 | return -1; |
754 | 0 | } |
755 | | |
756 | 15 | ctx->cio = data; |
757 | 15 | ctx->ins = in; |
758 | 15 | ctx->mem_limit = flb_utils_size_to_bytes(config->storage_bl_mem_limit); |
759 | | |
760 | 15 | mk_list_init(&ctx->backlogs); |
761 | | |
762 | 15 | flb_utils_bytes_to_human_readable_size(ctx->mem_limit, mem, sizeof(mem) - 1); |
763 | 15 | flb_plg_info(ctx->ins, "queue memory limit: %s", mem); |
764 | | |
765 | | /* export plugin context */ |
766 | 15 | flb_input_set_context(in, ctx); |
767 | | |
768 | | /* Set a collector to trigger the callback to queue data every second */ |
769 | 15 | ret = flb_input_set_collector_time(in, cb_queue_chunks, 1, 0, config); |
770 | 15 | if (ret < 0) { |
771 | 0 | flb_plg_error(ctx->ins, "could not create collector"); |
772 | 0 | flb_free(ctx->dummy_routes_mask); |
773 | 0 | flb_free(ctx); |
774 | 0 | return -1; |
775 | 0 | } |
776 | 15 | ctx->coll_fd = ret; |
777 | | |
778 | 15 | return 0; |
779 | 15 | } |
780 | | |
781 | | static void cb_sb_pause(void *data, struct flb_config *config) |
782 | 30 | { |
783 | 30 | struct flb_sb *ctx = data; |
784 | 30 | flb_input_collector_pause(ctx->coll_fd, ctx->ins); |
785 | 30 | } |
786 | | |
787 | | static void cb_sb_resume(void *data, struct flb_config *config) |
788 | 0 | { |
789 | 0 | struct flb_sb *ctx = data; |
790 | 0 | flb_input_collector_resume(ctx->coll_fd, ctx->ins); |
791 | 0 | } |
792 | | |
793 | | static int cb_sb_exit(void *data, struct flb_config *config) |
794 | 15 | { |
795 | 15 | struct flb_sb *ctx = data; |
796 | | |
797 | 15 | flb_input_collector_pause(ctx->coll_fd, ctx->ins); |
798 | | |
799 | 15 | sb_destroy_backlogs(ctx); |
800 | | |
801 | 15 | if (ctx->dummy_routes_mask != NULL) { |
802 | 15 | flb_free(ctx->dummy_routes_mask); |
803 | 15 | } |
804 | | |
805 | 15 | flb_free(ctx); |
806 | | |
807 | 15 | return 0; |
808 | 15 | } |
809 | | |
810 | | /* Plugin reference */ |
811 | | struct flb_input_plugin in_storage_backlog_plugin = { |
812 | | .name = "storage_backlog", |
813 | | .description = "Storage Backlog", |
814 | | .cb_init = cb_sb_init, |
815 | | .cb_pre_run = NULL, |
816 | | .cb_collect = NULL, |
817 | | .cb_ingest = NULL, |
818 | | .cb_flush_buf = NULL, |
819 | | .cb_pause = cb_sb_pause, |
820 | | .cb_resume = cb_sb_resume, |
821 | | .cb_exit = cb_sb_exit, |
822 | | |
823 | | /* This plugin can only be configured and invoked by the Engine */ |
824 | | .flags = FLB_INPUT_PRIVATE |
825 | | }; |