Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e5b2b1d
utils: add strict time conversion helper
cosmo0920 Sep 10, 2026
9c21a69
tests: internal: cover strict time conversion
cosmo0920 Sep 10, 2026
0ed789b
output_throttle: add portable output cooldown gate
cosmo0920 Sep 10, 2026
2c5c529
task: track deferred route ownership
cosmo0920 Sep 10, 2026
21dbd13
output: manage throttled dispatch and queue ownership
cosmo0920 Sep 10, 2026
f1619a6
output_thread: recover deferred dispatch results safely
cosmo0920 Sep 10, 2026
8e208ea
engine_dispatch: defer throttled routes and resolve failures
cosmo0920 Sep 10, 2026
1f46ebc
engine: resume throttled work without spending retry attempts
cosmo0920 Sep 10, 2026
2edfc25
tests: internal: cover dispatch failure ownership on all platforms
cosmo0920 Sep 10, 2026
aed2217
tests: internal: cover output cooldown and dispatch recovery
cosmo0920 Sep 10, 2026
b8148a5
tests: internal: cover worker shutdown signaling failures
cosmo0920 Sep 10, 2026
f92e3da
tests: runtime: cover throttle retries and route serialization
cosmo0920 Sep 10, 2026
88a28ef
benchmarks: measure output throttle gate overhead
cosmo0920 Sep 10, 2026
1958ac1
http_retry_after: parse response delay hints safely
cosmo0920 Sep 10, 2026
79ff0bd
tests: internal: cover retry delay parsing and malformed headers
cosmo0920 Sep 10, 2026
64aa5c9
tests: internal: fuzz HTTP retry delay parsing
cosmo0920 Sep 10, 2026
0a7a0f3
out_http: classify throttled responses and honor retry delays
cosmo0920 Sep 10, 2026
ee49414
tests: integration: cover HTTP throttle timing and body requests
cosmo0920 Sep 10, 2026
c74ea01
search_bulk: expose item throttle evidence
cosmo0920 Sep 10, 2026
acb0366
out_es: adapt to bulk throttle evidence interface
cosmo0920 Sep 17, 2026
ec8c172
out_opensearch: adapt to bulk throttle evidence interface
cosmo0920 Sep 17, 2026
7c3c7d2
tests: internal: adapt bulk response calls to throttle evidence
cosmo0920 Sep 17, 2026
2687006
tests: internal: cover search bulk throttle evidence
cosmo0920 Sep 10, 2026
0061088
out_es: honor bulk throttle responses and retry delay hints
cosmo0920 Sep 10, 2026
1975157
out_es: document throttle response audit
cosmo0920 Sep 10, 2026
1b7da65
out_opensearch: honor bulk throttle responses and retry delay hints
cosmo0920 Sep 10, 2026
26757ea
out_opensearch: document throttle response audit
cosmo0920 Sep 10, 2026
f7b1c50
tests: integration: cover search bulk throttle headers and trailers
cosmo0920 Sep 10, 2026
f5de0b5
out_opentelemetry: honor HTTP and gRPC throttle signals
cosmo0920 Sep 10, 2026
5971703
out_opentelemetry: document throttle response audit
cosmo0920 Sep 10, 2026
b450417
tests: integration: cover OTLP retry hints and unpadded metadata
cosmo0920 Sep 10, 2026
b3deb15
tests: integration: expose Valgrind failure contexts
cosmo0920 Sep 10, 2026
c594a6a
output_thread: retain dispatch ownership across wake failures
cosmo0920 Sep 10, 2026
6dfa519
tests: internal: cover queued worker dispatch recovery
cosmo0920 Sep 10, 2026
2958d11
tests: integration: verify successive body-key throttle cooldowns
cosmo0920 Sep 10, 2026
84fadf2
engine: schedule deferred routes after serialized output completion
cosmo0920 Sep 10, 2026
b136609
output: wait for completion before resuming deferred routes
cosmo0920 Sep 10, 2026
c7ddd45
task: avoid accessing destroyed outputs during cleanup
cosmo0920 Sep 10, 2026
573c49f
tests: internal: cover task cleanup after output destruction
cosmo0920 Sep 10, 2026
1e83a44
tests: runtime: bound throttle tests and stop engines gracefully
cosmo0920 Sep 10, 2026
155095b
tests: integration: reserve distinct service monitoring ports
cosmo0920 Sep 10, 2026
6062163
out_opentelemetry: preserve split metric throttling
cosmo0920 Sep 10, 2026
ebd1ebf
tests: integration: cover split metric throttling and shutdown
cosmo0920 Sep 10, 2026
6b9d2e6
tests: runtime: wait for dummy records before stopping
cosmo0920 Sep 11, 2026
cc0d5e4
tests: runtime: stop stackdriver gracefully on macos
cosmo0920 Sep 11, 2026
100313f
tests: internal: bound input chunk shutdown on macos
cosmo0920 Sep 11, 2026
e5007e9
workflows: bound macos tests and retain failure logs
cosmo0920 Sep 11, 2026
cb1ef0d
lib: finish cooperative shutdown on macos
cosmo0920 Sep 11, 2026
3516a33
tests: runtime: verify stop waits for engine cleanup
cosmo0920 Sep 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/scripts/ctest-wrapper/ctest
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

set -u

if [ -z "${FLB_CTEST_REAL_PATH:-}" ]; then
echo "FLB_CTEST_REAL_PATH must name the real ctest executable" >&2
exit 2
fi

wrapper_path="$(cd "$(dirname "$0")" && pwd -P)/$(basename "$0")"
ctest_path="$(cd "$(dirname "$FLB_CTEST_REAL_PATH")" && pwd -P)/$(basename "$FLB_CTEST_REAL_PATH")"

if [ "$ctest_path" = "$wrapper_path" ]; then
echo "FLB_CTEST_REAL_PATH resolves to the ctest timeout wrapper" >&2
exit 2
fi

timeout_seconds="${FLB_CTEST_DEFAULT_TIMEOUT_SECONDS:-300}"

case "$timeout_seconds" in
''|*[!0-9]*|0)
echo "FLB_CTEST_DEFAULT_TIMEOUT_SECONDS must be a positive integer" >&2
exit 2
;;
esac

exec "$FLB_CTEST_REAL_PATH" "$@" --timeout "$timeout_seconds"
91 changes: 91 additions & 0 deletions .github/scripts/tests/test_ctest_timeout_wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/bin/bash

set -eu

script_dir="$(cd "$(dirname "$0")" && pwd -P)"
wrapper="$script_dir/../ctest-wrapper/ctest"
temporary_dir="$(mktemp -d "${TMPDIR:-/tmp}/flb-ctest-wrapper.XXXXXX")"

cleanup()
{
rm -rf "$temporary_dir"
}

trap cleanup EXIT

fake_ctest="$temporary_dir/fake ctest"
arguments_file="$temporary_dir/arguments"

cat > "$fake_ctest" <<'EOF'
#!/bin/bash
printf '%s\n' "$@" > "$FLB_CTEST_ARGUMENTS_FILE"
exit "${FLB_CTEST_FAKE_EXIT_CODE:-0}"
EOF
chmod +x "$fake_ctest"

FLB_CTEST_REAL_PATH="$fake_ctest" \
FLB_CTEST_ARGUMENTS_FILE="$arguments_file" \
FLB_CTEST_DEFAULT_TIMEOUT_SECONDS=17 \
"$wrapper" --test-dir "directory with spaces" --output-on-failure

expected_arguments="$temporary_dir/expected-arguments"
cat > "$expected_arguments" <<'EOF'
--test-dir
directory with spaces
--output-on-failure
--timeout
17
EOF
cmp "$expected_arguments" "$arguments_file"

set +e
FLB_CTEST_REAL_PATH="$fake_ctest" \
FLB_CTEST_ARGUMENTS_FILE="$arguments_file" \
FLB_CTEST_FAKE_EXIT_CODE=42 \
"$wrapper"
wrapper_status=$?
set -e

if [ "$wrapper_status" -ne 42 ]; then
echo "wrapper returned $wrapper_status instead of the ctest status 42" >&2
exit 1
fi

timeout_project="$temporary_dir/default-timeout"
mkdir "$timeout_project"
cat > "$timeout_project/CTestTestfile.cmake" <<'EOF'
add_test(slow-test /bin/sleep 2)
EOF

set +e
FLB_CTEST_REAL_PATH="$(command -v ctest)" \
FLB_CTEST_DEFAULT_TIMEOUT_SECONDS=1 \
"$wrapper" --test-dir "$timeout_project" --output-on-failure
timeout_status=$?
set -e

if [ "$timeout_status" -eq 0 ]; then
echo "ctest unexpectedly passed a test longer than the default timeout" >&2
exit 1
fi

property_project="$temporary_dir/explicit-timeout"
mkdir "$property_project"
cat > "$property_project/CTestTestfile.cmake" <<'EOF'
add_test(slow-test /bin/sleep 2)
set_tests_properties(slow-test PROPERTIES TIMEOUT 3)
EOF

FLB_CTEST_REAL_PATH="$(command -v ctest)" \
FLB_CTEST_DEFAULT_TIMEOUT_SECONDS=1 \
"$wrapper" --test-dir "$property_project" --output-on-failure

set +e
FLB_CTEST_REAL_PATH="$wrapper" "$wrapper"
recursion_status=$?
set -e

if [ "$recursion_status" -ne 2 ]; then
echo "wrapper did not reject itself as the real ctest executable" >&2
exit 1
fi
12 changes: 12 additions & 0 deletions .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,24 @@ jobs:
echo "CC = $CC, CXX = $CXX, FLB_OPT = $FLB_OPT"
brew update
brew install bison flex openssl || true
export FLB_CTEST_REAL_PATH="$(command -v ctest)"
export PATH="$GITHUB_WORKSPACE/.github/scripts/ctest-wrapper:$PATH"
ci/scripts/run-unit-tests.sh
env:
CC: gcc
CXX: g++
FLB_OPT: ${{ matrix.flb_option }}

- name: Upload CTest failure diagnostics
if: ${{ failure() || cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: macos-ctest-logs-${{ matrix.flb_option }}
path: |
build/Testing/Temporary/LastTest.log
build/Testing/Temporary/LastTest.log.tmp
if-no-files-found: warn

run-aarch64-unit-tests:
runs-on: ${{(github.repository == 'fluent/fluent-bit') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
permissions:
Expand Down
6 changes: 6 additions & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ target_link_libraries(flb-bench-processor_sampling
fluent-bit-static
${CMAKE_THREAD_LIBS_INIT}
)

add_executable(flb-bench-output_throttle flb-bench-output_throttle.c)
target_link_libraries(flb-bench-output_throttle
fluent-bit-static
${CMAKE_THREAD_LIBS_INIT}
)
169 changes: 169 additions & 0 deletions benchmarks/flb-bench-output_throttle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <cfl/cfl_time.h>
#include <fluent-bit/flb_output_throttle.h>

#define DEFAULT_ITERATIONS 1000000
#define CONTENTION_THREADS 4

struct benchmark_worker {
uint64_t iterations;
uint64_t admitted;
struct flb_output_throttle *gate;
};

static void *run_admissions(void *data)
{
uint64_t index;
uint64_t generation;
struct benchmark_worker *worker;

worker = data;
for (index = 0; index < worker->iterations; index++) {
worker->admitted += flb_output_throttle_admit(
worker->gate,
flb_output_throttle_now_ms(),
&generation);
}

return NULL;
}

static double elapsed_seconds(uint64_t start, uint64_t end)
{
return (double) (end - start) / 1000000000.0;
}

static void report(const char *name, uint64_t operations, double seconds)
{
printf("%-30s %12" PRIu64 " ops %10.3f Mops/s\n",
name, operations, (double) operations / seconds / 1000000.0);
}

static double run_parallel(struct flb_output_throttle *gate,
uint64_t iterations)
{
int index;
int created;
uint64_t start;
uint64_t end;
pthread_t threads[CONTENTION_THREADS];
struct benchmark_worker workers[CONTENTION_THREADS];

created = 0;
start = cfl_time_now();
for (index = 0; index < CONTENTION_THREADS; index++) {
workers[index].iterations = iterations;
workers[index].admitted = 0;
workers[index].gate = gate;
if (pthread_create(&threads[index], NULL,
run_admissions, &workers[index]) != 0) {
break;
}
created++;
}
for (index = 0; index < created; index++) {
pthread_join(threads[index], NULL);
}
end = cfl_time_now();

if (created != CONTENTION_THREADS) {
return -1.0;
}
return elapsed_seconds(start, end);
}

static void report_overhead(const char *name, double baseline, double candidate)
{
printf("%-30s %10.2f%%\n", name,
(candidate / baseline - 1.0) * 100.0);
}

int main(int argc, char **argv)
{
uint64_t iterations;
uint64_t start;
uint64_t end;
uint64_t generation;
double disabled_seconds;
double enabled_seconds;
double disabled_parallel_seconds;
double enabled_parallel_seconds;
volatile uint64_t baseline;
struct benchmark_worker workers[CONTENTION_THREADS];
struct flb_output_throttle disabled_gate;
struct flb_output_throttle enabled_gate;

iterations = DEFAULT_ITERATIONS;
if (argc > 1) {
iterations = strtoull(argv[1], NULL, 10);
}
if (iterations == 0) {
fprintf(stderr, "iterations must be greater than zero\n");
return EXIT_FAILURE;
}

if (flb_output_throttle_init(&disabled_gate,
FLB_FALSE, 1000, 60000) != 0) {
fprintf(stderr, "could not initialize throttle gates\n");
return EXIT_FAILURE;
}
if (flb_output_throttle_init(&enabled_gate,
FLB_TRUE, 1000, 60000) != 0) {
fprintf(stderr, "could not initialize throttle gates\n");
flb_output_throttle_destroy(&disabled_gate);
return EXIT_FAILURE;
}

baseline = 0;
start = cfl_time_now();
for (generation = 0; generation < iterations; generation++) {
baseline += flb_output_throttle_now_ms() != UINT64_MAX;
}
end = cfl_time_now();
report("clock-only baseline", iterations, elapsed_seconds(start, end));

workers[0].iterations = iterations;
workers[0].admitted = 0;
workers[0].gate = &disabled_gate;
start = cfl_time_now();
run_admissions(&workers[0]);
end = cfl_time_now();
disabled_seconds = elapsed_seconds(start, end);
report("disabled gate", iterations, disabled_seconds);

workers[0].admitted = 0;
workers[0].gate = &enabled_gate;
start = cfl_time_now();
run_admissions(&workers[0]);
end = cfl_time_now();
enabled_seconds = elapsed_seconds(start, end);
report("enabled ready gate", iterations, enabled_seconds);
report_overhead("enabled vs disabled overhead",
disabled_seconds, enabled_seconds);

disabled_parallel_seconds = run_parallel(&disabled_gate, iterations);
enabled_parallel_seconds = run_parallel(&enabled_gate, iterations);
if (disabled_parallel_seconds < 0 || enabled_parallel_seconds < 0) {
fprintf(stderr, "could not create benchmark threads\n");
flb_output_throttle_destroy(&enabled_gate);
flb_output_throttle_destroy(&disabled_gate);
return EXIT_FAILURE;
}
report("disabled gate, 4 threads", iterations * CONTENTION_THREADS,
disabled_parallel_seconds);
report("enabled ready gate, 4 threads",
iterations * CONTENTION_THREADS, enabled_parallel_seconds);
report_overhead("4-thread enabled overhead",
disabled_parallel_seconds, enabled_parallel_seconds);

flb_output_throttle_destroy(&enabled_gate);
flb_output_throttle_destroy(&disabled_gate);
return baseline == 0;
}
46 changes: 46 additions & 0 deletions include/fluent-bit/flb_http_retry_after.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/* Fluent Bit
* ==========
* Copyright (C) 2015-2026 The Fluent Bit Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef FLB_HTTP_RETRY_AFTER_H
#define FLB_HTTP_RETRY_AFTER_H

#include <fluent-bit/flb_macros.h>

#include <stddef.h>
#include <stdint.h>

enum flb_retry_after_status {
FLB_RETRY_AFTER_ABSENT = 0,
FLB_RETRY_AFTER_VALID,
FLB_RETRY_AFTER_INVALID,
FLB_RETRY_AFTER_SATURATED
};

FLB_EXPORT int flb_http_retry_after_parse(const char *value,
size_t length,
int64_t received_wall_time_ms,
uint64_t *delay_ms);

FLB_EXPORT int flb_http_retry_after_parse_headers(const char *headers,
size_t length,
int64_t received_wall_time_ms,
uint64_t *delay_ms,
size_t *invalid_count);

#endif
1 change: 1 addition & 0 deletions include/fluent-bit/flb_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#define FLB_ERROR 0
#define FLB_OK 1
#define FLB_RETRY 2
#define FLB_THROTTLE 3

/* ala-printf format check */
#if defined(__GNUC__) || defined(__clang__)
Expand Down
Loading
Loading