* fix(mount): count manifest sizes in merge condition to prevent manifest accumulation
shouldMergeChunks only counted non-manifest chunk sizes toward the bloat
threshold, so overlapping manifests accumulated undetected across flush
cycles.
During sustained random writes (e.g. fio), each metadata flush compacts
non-manifest chunks and may group them into a new manifest via
MaybeManifestize, while carrying forward all existing manifests. Since
the merge condition only checked non-manifest totals against 2x file
size, the growing pile of redundant manifests never triggered a merge.
In a real workload: 4 GB file, 25 manifests each covering ~4 GB
(107 GB manifest data on volume servers), but shouldMergeChunks saw only
4.2 GB of non-manifest chunks vs the 8.6 GB threshold -- no merge.
Fix: include manifest coverage sizes in totalChunkSize. This correctly
detects the 111 GB total vs 8.6 GB threshold and triggers the merge,
which re-reads the file as clean chunks and lets the filer's MinusChunks
(which resolves manifests) garbage-collect all redundant sub-chunks.
* test(mount,filer): add manifest chunk correctness tests
Filer-level tests (filechunk_manifest_test.go):
- TestManifestRoundTripPreservesChunks: create -> serialize -> deserialize
preserves fileId, offset, size, and timestamp for all sub-chunks
- TestCompactResolvedOverlappingManifests: two manifests covering the same
range, older sub-chunks correctly identified as garbage after compaction
- TestDoMinusChunksWithResolvedManifests: old manifest sub-chunks detected
as garbage when compared against new clean chunks (mirrors filer cleanup)
- TestManifestizeSmallBatchWithRemainder: batch=3 with 7 chunks produces
2 manifests + 1 remainder, all data recoverable after resolve
- TestCompactMultipleOverlappingManifestGenerations: 5 generations of
full-file manifests, compaction keeps only the newest generation
- TestManifestBloatDetection: with N overlapping manifests, merge triggers
at N >= 2 (stored > 2x file size)
Mount-level test (weedfs_file_sync_test.go):
- TestFlushCycleManifestAccumulation: simulates the flushMetadataToFiler
pipeline over multiple cycles with a small manifest batch (5 chunks).
Verifies merge triggers at cycle 2 when accumulated manifests exceed
the 2x threshold. Uses SeparateManifestChunks + CompactFileChunks +
shouldMergeChunks to mirror the real code path.
* fix(mount): reduce chunk fragmentation from random writes
Random writes via FUSE mount create many small partially-overlapping
chunks that bloat storage and slow reads. Two complementary fixes:
1. Fill gaps at seal time: when a partial writable chunk is sealed for
upload, read existing file data into the unwritten regions so
SaveContent uploads one complete chunk instead of many fragments.
No overhead for sequential writes (IsComplete short-circuits).
2. Merge on fsync: after CompactFileChunks, if total non-manifest
chunk data exceeds 2x the logical file size, re-read and re-upload
the entire file as properly-sized chunks. The filer's cleanupChunks
garbage-collects all superseded chunks.
* revert FillGaps: reading from volume servers during write path is too expensive
* test(mount): add tests for chunk merge condition
Extract shouldMergeChunks as a testable function and add tests covering:
- empty file, single chunk, non-overlapping chunks (no merge)
- exactly 2x boundary (no merge) vs just over 2x (merge)
- manifest chunks extend file size but don't count toward stored total
- input slices are not mutated (safe append)
- CompactFileChunks + condition: fully superseded, staggered overlaps,
75% overlap pattern, many 4K writes at 1K step
- randomized bloat detection (50 iterations)
- visible content preserved after compaction (100 iterations)
* fix(mount): cap merge buffer at file size for small files
Avoids allocating a full ChunkSizeLimit buffer when the file being
merged is smaller. Addresses PR review feedback.