Coverage Report

Created: 2026-11-12 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
};