Skip to content

chunkio: add optional file-slot pool to bound dentry/inode churn - #117

Open
dpavlov2024 wants to merge 1 commit into
fluent:masterfrom
dpavlov2024:file-slot-pool
Open

dpavlov2024 wants to merge 1 commit into
fluent:masterfrom
dpavlov2024:file-slot-pool

Conversation

@dpavlov2024

@dpavlov2024 dpavlov2024 commented Oct 13, 2026 •

Copy link
Copy Markdown

Refs fluent/fluent-bit#12504 (as requested there, the implementation is proposed here in chunkio first; a fluent-bit PR will follow).

Opt-in per-stream file-slot pool for filesystem storage. Recycles a bounded set of slot files instead of creating and unlinking a unique file per flush, which stops the unbounded dentry/inode slab growth on cgroup v2. Cited by the fluent-bit PR; evidence in the comments.

Signed-off-by: Dmitri Pavlov <dpavlov@perforce.com>
@dpavlov2024

Copy link
Copy Markdown
Author

Test pool_slot_name_parse...
Test pool_disabled_legacy...
Test pool_reuse_bounds_files...
Test pool_backlog_fallback...
Test pool_recovery...
Test pool_recovery_legacy_name...
Test pool_corrupt_slot...
Test pool_option_clamp...
Test pool_option_invalid...
Test pool_single_slot...
Test pool_mem_stream_ignores_option...
Test pool_delete_by_name...
Test pool_counter_balance...
Test pool_truncate_failure_keeps_slot...
Test pool_shrunk_pool_cleanup...
Test pool_crash_after_truncate...
Test pool_crash_mid_write...
Test pool_lock_threads...
Test pool_threaded_chunks...
Test pool_threaded_max_chunks...
SUCCESS: All unit tests have passed.

@dpavlov2024

Copy link
Copy Markdown
Author

100% tests passed, 0 tests failed out of 6

Total Test time (real) = 0.47 sec

@dpavlov2024

Copy link
Copy Markdown
Author

Integration results - pool fix on master (5.2.0 line), 2026-10-12

(unit/sanitizer section refreshed 2026-10-13 after review hardening)

Image: fluent-bit-pool:5.2.0 (master a92d414, dev version 5.1.4) with
pool-fix-5.2.0.patch applied. Host: lab Ubuntu server (kernel 6.x).

Pipeline: the 100-stream tail rig - 100 tail log files at 15 MB/s -> filesystem
chunks -> Splunk HEC mock, storage.max_chunks_up: 32.

A/B via [SERVICE] storage.file_pool_size (all paths on XFS)

Storage, logs and tail DB all on an XFS loopback to remove unrelated ext4
metadata churn:

setting slab_reclaimable 300 s slope chunk files
storage.file_pool_size unset +1446 KB ~17.4 MB/h unique names, churning
storage.file_pool_size 2048 +196 KB ~2.4 MB/h 100% slot-*.flb, reused

Startup log reflects the setting:

[storage] ver=1.5.4, type=memory+filesystem, sync=normal, checksum=off,
max_chunks_up=32, file_pool_size=2048

Restart recovery (option on)

  1. Ran with the option off + HEC delay to build a backlog: 36 pending
    stock-named chunk files.
  2. Stopped the container, restarted with storage.file_pool_size 2048:
    • could not retrieve chunk tag errors: 0
    • all 36 legacy chunks delivered and unlinked (unique files 36 -> 0)
    • 36 slot files remain (the reusable pool)

Opt-out after opt-in

  1. Ran with the pool on, then stopped with 35 slot chunks pending.
  2. Restarted with the option unset (stock behaviour): 0 tag errors, HEC event
    counter kept increasing (chunks recovered/delivered), no errors.

Unit / sanitizers

  • chunkio ctest: 6/6 suites; fs_pool: 20/20 tests, including the
    review additions pool_counter_balance (max_chunks_up limit, failed
    down-chunk read, failed corrupt-slot open and pool-disabled delete-by-name)
    and pool_threaded_max_chunks (8 threads racing the atomic up-slot
    reservation with max_chunks_up 4, 20 rounds). Full run in
    fs_pool-tests.txt.
  • Stress: cio-test-fs_pool 0 failures / 3000 runs.
  • ASan/UBSan build of fs_pool: clean; TSan (-fsanitize=thread): clean.
  • Valgrind --leak-check=full: 0 definitely/indirectly lost, 0 errors
    (see valgrind-summary.txt).
  • Full matrix (clang 18, UBSan full ctest, -Wall -Wextra, ASan caveat):
    thread-safety-notes.md.

Notes

  • On ext4-stored chunk/log/tail-db paths a residual ~10-30 MB/h remains from
    ext4 block metadata churn (ftruncate/WAL writes), independent of this
    change; with those paths on XFS the patched profile is near-flat (~2.4 MB/h
    with no linear chunk-file component, the same figure as the 5.1.2 VM A/B:
    +2.3 vs +533 MB/h).
  • The option is per chunkio context (all filesystem streams), default 0
    (disabled), clamped to 4096.

@dpavlov2024

Copy link
Copy Markdown
Author

definitely lost: 0 bytes in 0 blocks
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
indirectly lost: 0 bytes in 0 blocks

@dpavlov2024

Copy link
Copy Markdown
Author

Stress and sanitizer results

Run 2026-10-13 against the review-hardened tree (20 fs_pool tests, Linux).

  • cio-test-fs_pool stress: 0 failures / 3000 runs, including the threaded
    chunks test and pool_threaded_max_chunks (8 threads racing the atomic
    reservation for 4 up-slots, 20 rounds per run).
  • ctest (GCC): 6/6 suites; clang 18 build + ctest: 6/6.
  • AddressSanitizer (fs_pool): clean. Full ASan ctest is 5/6: cio-test-fs
    trips LeakSanitizer on a pre-existing stock test bug - tests/fs.c
    test_metadata_unsigned_underflow overwrites content_buf with a second
    cio_chunk_get_content_copy() at fs.c:1094 without freeing the first buffer
    allocated at :1072; with detect_leaks=0 the same binary passes 12/12. The
    same leak reproduces on stock chunkio (pre-patch), so it is not caused by
    this change.
  • UBSan (-fsanitize=undefined -fno-sanitize-recover): full ctest 6/6,
    no reports.
  • ThreadSanitizer (-fsanitize=thread, fs_pool): 0 warnings (needs ASLR
    disabled in this container: setarch $(uname -m) -R).
  • Windows: the _WIN32 reservation path is covered concurrently by
    pool_threaded_max_chunks_win (8 CreateThread workers, max_chunks_up 4,
    20 rounds); Windows fs_pool is 17/17 (windows-results.md).
  • Valgrind --leak-check=full: 0 definitely/indirectly lost, 0 errors.
  • -Wall -Wextra (GCC 13 / clang 18): no warnings from the changed or added
    code; the remainder are pre-existing header/stock warnings
    (include/chunkio/cio_file_st.h, uid/gid and checksum sign-compares).

Note: the chunkio fs tests use the fixed directory /tmp/cio-fs-pool-test/ -
run test binaries serially, never in parallel, or they collide.

@dpavlov2024

Copy link
Copy Markdown
Author

Windows (MSVC) test results

Host: Windows Server 2022, Visual Studio 2022 Build Tools (MSVC x64),
CMake 3.30.5. Source: this PR, Visual Studio 17 2022 generator, Release.
Run 2026-10-13 against the review-hardened tree.

All chunkio suites pass (exit codes):

context=0  stream=0  fs=0  metadata=0  fs_windows=0  fs_pool=0
cio-test-memfs: 0 failures / 30 runs

fs_pool passes 17/17 on Windows: the 16 portable tests plus
pool_threaded_max_chunks_win, which races 8 CreateThread workers (created
suspended, released together) over the atomic up-slot reservation with
max_chunks_up 4, for 20 rounds. That covers the _WIN32 branch of the new
_InterlockedCompareExchange64 reservation, which was otherwise exercised
single-threaded only. The four POSIX-only tests (failed-truncate + three
pthread tests) are excluded; see the suite output in
windows-fs_pool-tests.txt.

The counter-balance test asserts the loaded-chunk state around delete-by-name,
not the delete return value: DeleteFileA cannot unlink a file that is still
open, so a loaded non-pooled chunk keeps its file on Windows (stock behaviour).

@dpavlov2024

Copy link
Copy Markdown
Author

Test pool_slot_name_parse...
Test pool_disabled_legacy...
Test pool_reuse_bounds_files...
Test pool_backlog_fallback...
Test pool_recovery...
Test pool_recovery_legacy_name...
Test pool_corrupt_slot...
Test pool_option_clamp...
Test pool_option_invalid...
Test pool_single_slot...
Test pool_mem_stream_ignores_option...
Test pool_delete_by_name...
Test pool_counter_balance...
Test pool_shrunk_pool_cleanup...
Test pool_crash_after_truncate...
Test pool_crash_mid_write...
Test pool_threaded_max_chunks_win...
SUCCESS: All unit tests have passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant