Skip to content

Commit d1abb54

Browse files
cosmo0920edsiper
authored andcommitted
out_s3: Provide options for inactive chunks' behavior
Signed-off-by: Hiroshi Hatake <hiroshi@chronosphere.io>
1 parent ba1123c commit d1abb54

4 files changed

Lines changed: 173 additions & 13 deletions

File tree

plugins/out_s3/s3.c

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ static struct multipart_upload *create_upload(struct flb_s3 *ctx,
8282
time_t file_first_log_time);
8383

8484
static void remove_from_queue(struct upload_queue *entry);
85+
static void s3_chunk_retry_exhausted_cleanup(struct flb_s3 *ctx,
86+
struct s3_file *chunk_file);
87+
static int s3_get_retry_exhausted_action(const char *value);
8588

8689
static int blob_initialize_authorization_endpoint_upstream(struct flb_s3 *context);
8790

@@ -97,21 +100,21 @@ static struct flb_aws_header *get_content_encoding_header(int compression_type)
97100
.val = "gzip",
98101
.val_len = 4,
99102
};
100-
103+
101104
static struct flb_aws_header zstd_header = {
102105
.key = "Content-Encoding",
103106
.key_len = 16,
104107
.val = "zstd",
105108
.val_len = 4,
106109
};
107-
110+
108111
static struct flb_aws_header snappy_header = {
109112
.key = "Content-Encoding",
110113
.key_len = 16,
111114
.val = "snappy",
112115
.val_len = 6,
113116
};
114-
117+
115118
switch (compression_type) {
116119
case FLB_AWS_COMPRESS_GZIP:
117120
return &gzip_header;
@@ -196,7 +199,7 @@ int create_headers(struct flb_s3 *ctx, char *body_md5,
196199
if (ctx->content_type != NULL) {
197200
headers_len++;
198201
}
199-
if (ctx->compression == FLB_AWS_COMPRESS_GZIP ||
202+
if (ctx->compression == FLB_AWS_COMPRESS_GZIP ||
200203
ctx->compression == FLB_AWS_COMPRESS_ZSTD ||
201204
ctx->compression == FLB_AWS_COMPRESS_SNAPPY) {
202205
headers_len++;
@@ -228,7 +231,7 @@ int create_headers(struct flb_s3 *ctx, char *body_md5,
228231
s3_headers[n].val_len = strlen(ctx->content_type);
229232
n++;
230233
}
231-
if (ctx->compression == FLB_AWS_COMPRESS_GZIP ||
234+
if (ctx->compression == FLB_AWS_COMPRESS_GZIP ||
232235
ctx->compression == FLB_AWS_COMPRESS_ZSTD ||
233236
ctx->compression == FLB_AWS_COMPRESS_SNAPPY) {
234237
encoding_header = get_content_encoding_header(ctx->compression);
@@ -628,6 +631,7 @@ static int cb_s3_init(struct flb_output_instance *ins,
628631
struct flb_config *config, void *data)
629632
{
630633
int ret;
634+
int action;
631635
flb_sds_t tmp_sds;
632636
char *role_arn = NULL;
633637
char *session_name;
@@ -681,6 +685,15 @@ static int cb_s3_init(struct flb_output_instance *ins,
681685
return -1;
682686
}
683687

688+
action = s3_get_retry_exhausted_action(ctx->retry_exhausted_action_str);
689+
if (action == -1) {
690+
flb_plg_error(ctx->ins,
691+
"invalid retry_exhausted_action value '%s'",
692+
ctx->retry_exhausted_action_str ? ctx->retry_exhausted_action_str : "(null)");
693+
return -1;
694+
}
695+
ctx->retry_exhausted_action = action;
696+
684697
/* the check against -1 is works here because size_t is unsigned
685698
* and (int) -1 == unsigned max value
686699
* Fluent Bit uses -1 (which becomes max value) to indicate undefined
@@ -834,7 +847,7 @@ static int cb_s3_init(struct flb_output_instance *ins,
834847
"total_file_size is less than 10 MB, will use PutObject API");
835848
ctx->use_put_object = FLB_TRUE;
836849
}
837-
850+
838851
if (ctx->use_put_object == FLB_FALSE) {
839852
/* upload_chunk_size */
840853
if (ctx->upload_chunk_size <= 0) {
@@ -1446,6 +1459,9 @@ static int put_all_chunks(struct flb_s3 *ctx)
14461459
if (fs_stream == ctx->stream_upload) {
14471460
continue;
14481461
}
1462+
if (fs_stream == ctx->stream_quarantine) {
1463+
continue;
1464+
}
14491465
/* skip metadata stream */
14501466
if (fs_stream == ctx->stream_metadata) {
14511467
continue;
@@ -1464,8 +1480,7 @@ static int put_all_chunks(struct flb_s3 *ctx)
14641480
flb_plg_warn(ctx->ins,
14651481
"Chunk for tag %s failed to send %d/%d times, will not retry",
14661482
(char *) fsf->meta_buf, chunk->failures, ctx->ins->retry_limit);
1467-
flb_free(chunk);
1468-
flb_fstore_file_inactive(ctx->fs, fsf);
1483+
s3_chunk_retry_exhausted_cleanup(ctx, chunk);
14691484
continue;
14701485
}
14711486

@@ -1946,6 +1961,72 @@ static int buffer_chunk(void *out_context, struct s3_file *upload_file,
19461961
return 0;
19471962
}
19481963

1964+
/*
1965+
* Terminal retry exhaustion must permanently remove local buffer files.
1966+
* Unlike inactive state (recoverable/restart-resumable), terminal cleanup
1967+
* must release store_dir_limit_size accounting and delete on-disk state.
1968+
*/
1969+
static void s3_chunk_retry_exhausted_cleanup(struct flb_s3 *ctx,
1970+
struct s3_file *chunk_file)
1971+
{
1972+
size_t reclaim_size;
1973+
int ret;
1974+
1975+
if (chunk_file == NULL) {
1976+
return;
1977+
}
1978+
1979+
if (ctx->retry_exhausted_action == S3_RETRY_EXHAUSTED_QUARANTINE) {
1980+
reclaim_size = chunk_file->size;
1981+
1982+
if (ctx->quarantine_dir_limit_size > 0 &&
1983+
ctx->quarantine_buffer_size + reclaim_size > ctx->quarantine_dir_limit_size) {
1984+
flb_plg_warn(ctx->ins,
1985+
"quarantine limit reached, deleting retry-exhausted chunk");
1986+
s3_store_file_delete(ctx, chunk_file);
1987+
return;
1988+
}
1989+
1990+
ret = s3_store_file_quarantine(ctx, chunk_file);
1991+
if (ret < 0) {
1992+
flb_plg_error(ctx->ins,
1993+
"could not quarantine, deleting retry-exhausted chunk");
1994+
s3_store_file_delete(ctx, chunk_file);
1995+
return;
1996+
}
1997+
1998+
if (ctx->current_buffer_size >= reclaim_size) {
1999+
ctx->current_buffer_size -= reclaim_size;
2000+
}
2001+
else {
2002+
ctx->current_buffer_size = 0;
2003+
}
2004+
ctx->quarantine_buffer_size += reclaim_size;
2005+
2006+
flb_plg_warn(ctx->ins,
2007+
"retry-exhausted chunk moved to quarantine");
2008+
return;
2009+
}
2010+
2011+
s3_store_file_delete(ctx, chunk_file);
2012+
}
2013+
2014+
static int s3_get_retry_exhausted_action(const char *value)
2015+
{
2016+
if (value == NULL) {
2017+
return S3_RETRY_EXHAUSTED_QUARANTINE;
2018+
}
2019+
2020+
if (strcasecmp(value, "delete") == 0) {
2021+
return S3_RETRY_EXHAUSTED_DELETE;
2022+
}
2023+
if (strcasecmp(value, "quarantine") == 0) {
2024+
return S3_RETRY_EXHAUSTED_QUARANTINE;
2025+
}
2026+
2027+
return -1;
2028+
}
2029+
19492030
/* Uploads all chunk files in queue synchronously */
19502031
static void s3_upload_queue(struct flb_config *config, void *out_context)
19512032
{
@@ -1998,7 +2079,7 @@ static void s3_upload_queue(struct flb_config *config, void *out_context)
19982079
if (upload_contents->retry_counter > ctx->ins->retry_limit) {
19992080
flb_plg_warn(ctx->ins, "Chunk file failed to send %d times, will not "
20002081
"retry", upload_contents->retry_counter);
2001-
s3_store_file_inactive(ctx, upload_contents->upload_file);
2082+
s3_chunk_retry_exhausted_cleanup(ctx, upload_contents->upload_file);
20022083
if (upload_contents->m_upload_file) {
20032084
mk_list_del(&upload_contents->m_upload_file->_head);
20042085
}
@@ -3403,8 +3484,7 @@ static void cb_s3_upload(struct flb_config *config, void *data)
34033484
flb_plg_warn(ctx->ins,
34043485
"Chunk for tag %s failed to send %d/%d times, will not retry",
34053486
(char *) fsf->meta_buf, chunk->failures, ctx->ins->retry_limit);
3406-
flb_free(chunk);
3407-
flb_fstore_file_inactive(ctx->fs, fsf);
3487+
s3_chunk_retry_exhausted_cleanup(ctx, chunk);
34083488
continue;
34093489
}
34103490
}
@@ -3937,7 +4017,7 @@ static void cb_s3_flush(struct flb_event_chunk *event_chunk,
39374017
if (upload_file != NULL && upload_file->failures > ctx->ins->retry_limit) {
39384018
flb_plg_warn(ctx->ins, "File with tag %s failed to send %d/%d times, will not retry",
39394019
event_chunk->tag, upload_file->failures, ctx->ins->retry_limit);
3940-
s3_store_file_inactive(ctx, upload_file);
4020+
s3_chunk_retry_exhausted_cleanup(ctx, upload_file);
39414021
upload_file = NULL;
39424022
}
39434023

@@ -4177,6 +4257,13 @@ static struct flb_config_map config_map[] = {
41774257
"the `store_dir` to limit disk usage. If the limit is reached, "
41784258
"data will be discarded. Default is 0 which means unlimited."
41794259
},
4260+
{
4261+
FLB_CONFIG_MAP_SIZE, "quarantine_dir_limit_size", "0",
4262+
0, FLB_TRUE, offsetof(struct flb_s3, quarantine_dir_limit_size),
4263+
"Limit size for retry-exhausted quarantined chunks. Applies when "
4264+
"retry_exhausted_action is set to 'quarantine'. If limit is reached, "
4265+
"retry-exhausted chunks are deleted. Default is 0 (unlimited)."
4266+
},
41804267

41814268
{
41824269
FLB_CONFIG_MAP_STR, "s3_key_format", "/fluent-bit-logs/$TAG/%Y/%m/%d/%H/%M/%S",
@@ -4229,6 +4316,13 @@ static struct flb_config_map config_map[] = {
42294316
"received chunk to be swapped with a later chunk, resulting in data shuffling. "
42304317
"This feature prevents this shuffling by using a queue logic for uploads."
42314318
},
4319+
{
4320+
FLB_CONFIG_MAP_STR, "retry_exhausted_action", "quarantine",
4321+
0, FLB_TRUE, offsetof(struct flb_s3, retry_exhausted_action_str),
4322+
"Action for chunks that exceeded retry_limit. Supported values are "
4323+
"'delete' (remove permanently) and 'quarantine' (move to quarantine and "
4324+
"remove from active buffer accounting)."
4325+
},
42324326

42334327
{
42344328
FLB_CONFIG_MAP_STR, "log_key", NULL,

plugins/out_s3/s3.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
#define DEFAULT_UPLOAD_TIMEOUT 3600
5050

5151
#define MAX_UPLOAD_ERRORS 5
52+
#define S3_RETRY_EXHAUSTED_DELETE 0
53+
#define S3_RETRY_EXHAUSTED_QUARANTINE 1
5254

5355
/*
5456
* If we see repeated errors on an upload/chunk, we will discard it
@@ -113,6 +115,7 @@ struct flb_s3 {
113115
char *sts_endpoint;
114116
char *canned_acl;
115117
char *content_type;
118+
char *retry_exhausted_action_str;
116119
char *storage_class;
117120
char *log_key;
118121
char *external_id;
@@ -122,10 +125,12 @@ struct flb_s3 {
122125
int use_put_object;
123126
int send_content_md5;
124127
int static_file_path;
128+
int retry_exhausted_action;
125129
int compression;
126130
int port;
127131
int insecure;
128132
size_t store_dir_limit_size;
133+
size_t quarantine_dir_limit_size;
129134

130135
struct flb_blob_db blob_db;
131136
flb_sds_t blob_database_file;
@@ -143,6 +148,7 @@ struct flb_s3 {
143148

144149
/* track the total amount of buffered data */
145150
size_t current_buffer_size;
151+
size_t quarantine_buffer_size;
146152

147153
struct flb_aws_provider *provider;
148154
struct flb_aws_provider *base_provider;
@@ -164,6 +170,7 @@ struct flb_s3 {
164170
struct flb_fstore *fs;
165171
struct flb_fstore_stream *stream_active; /* default active stream */
166172
struct flb_fstore_stream *stream_upload; /* multipart upload stream */
173+
struct flb_fstore_stream *stream_quarantine; /* retry-exhausted stream */
167174
struct flb_fstore_stream *stream_metadata; /* s3 metadata stream */
168175

169176
/*

plugins/out_s3/s3_store.c

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ static int set_files_context(struct flb_s3 *ctx)
215215
struct flb_fstore_stream *fs_stream;
216216
struct flb_fstore_file *fsf;
217217
struct s3_file *s3_file;
218+
ssize_t file_size;
218219

219220
mk_list_foreach(head, &ctx->fs->streams) {
220221
fs_stream = mk_list_entry(head, struct flb_fstore_stream, _head);
@@ -246,6 +247,11 @@ static int set_files_context(struct flb_s3 *ctx)
246247
s3_file->first_log_time = time(NULL);
247248
s3_file->create_time = time(NULL);
248249

250+
file_size = cio_chunk_get_real_size(fsf->chunk);
251+
if (file_size > 0) {
252+
s3_file->size = (size_t) file_size;
253+
}
254+
249255
/* Use fstore opaque 'data' reference to keep our context */
250256
fsf->data = s3_file;
251257
}
@@ -321,6 +327,16 @@ int s3_store_init(struct flb_s3 *ctx)
321327
}
322328
ctx->stream_upload = fs_stream;
323329

330+
/* Terminal quarantine stream */
331+
fs_stream = flb_fstore_stream_create(ctx->fs, "quarantine");
332+
if (!fs_stream) {
333+
flb_plg_error(ctx->ins, "could not initialize quarantine stream");
334+
flb_fstore_destroy(fs);
335+
ctx->fs = NULL;
336+
return -1;
337+
}
338+
ctx->stream_quarantine = fs_stream;
339+
324340
set_files_context(ctx);
325341
return 0;
326342
}
@@ -376,7 +392,8 @@ int s3_store_has_data(struct flb_s3 *ctx)
376392
mk_list_foreach(head, &ctx->fs->streams) {
377393
/* skip multi upload stream */
378394
fs_stream = mk_list_entry(head, struct flb_fstore_stream, _head);
379-
if (fs_stream == ctx->stream_upload) {
395+
if (fs_stream == ctx->stream_upload ||
396+
fs_stream == ctx->stream_quarantine) {
380397
continue;
381398
}
382399

@@ -413,6 +430,47 @@ int s3_store_file_inactive(struct flb_s3 *ctx, struct s3_file *s3_file)
413430
return ret;
414431
}
415432

433+
int s3_store_file_quarantine(struct flb_s3 *ctx, struct s3_file *s3_file)
434+
{
435+
struct flb_fstore_file *fsf;
436+
struct flb_fstore_file *qfsf;
437+
void *buf;
438+
size_t size;
439+
int ret;
440+
441+
fsf = s3_file->fsf;
442+
ret = flb_fstore_file_content_copy(ctx->fs, fsf, &buf, &size);
443+
if (ret < 0) {
444+
return -1;
445+
}
446+
447+
qfsf = flb_fstore_file_create(ctx->fs, ctx->stream_quarantine, fsf->name, size);
448+
if (qfsf == NULL) {
449+
flb_free(buf);
450+
return -1;
451+
}
452+
453+
if (fsf->meta_buf != NULL && fsf->meta_size > 0) {
454+
ret = flb_fstore_file_meta_set(ctx->fs, qfsf, fsf->meta_buf, fsf->meta_size);
455+
if (ret < 0) {
456+
flb_free(buf);
457+
flb_fstore_file_delete(ctx->fs, qfsf);
458+
return -1;
459+
}
460+
}
461+
462+
ret = flb_fstore_file_append(qfsf, buf, size);
463+
flb_free(buf);
464+
if (ret < 0) {
465+
flb_fstore_file_delete(ctx->fs, qfsf);
466+
return -1;
467+
}
468+
469+
flb_fstore_file_delete(ctx->fs, fsf);
470+
flb_free(s3_file);
471+
return 0;
472+
}
473+
416474
int s3_store_file_delete(struct flb_s3 *ctx, struct s3_file *s3_file)
417475
{
418476
struct flb_fstore_file *fsf;

plugins/out_s3/s3_store.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ int s3_store_has_data(struct flb_s3 *ctx);
4545
int s3_store_has_uploads(struct flb_s3 *ctx);
4646

4747
int s3_store_file_inactive(struct flb_s3 *ctx, struct s3_file *s3_file);
48+
int s3_store_file_quarantine(struct flb_s3 *ctx, struct s3_file *s3_file);
4849
struct s3_file *s3_store_file_get(struct flb_s3 *ctx, const char *tag,
4950
int tag_len);
5051
int s3_store_file_delete(struct flb_s3 *ctx, struct s3_file *s3_file);

0 commit comments

Comments
 (0)