mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
The `search3`-less fallback emitted one batch per album, and `SyncService.writeSongs` opens a transaction per batch, so syncing a large library that way ran one transaction per album — thousands of fsyncs, and an invalidation round for every list observing `songs` with each. Accumulate what each `getAlbum` returns and flush at `PAGE_SIZE`, so the fallback writes the same ~500-row batches every other fetch already does. Albums are fetched concurrently and complete out of order, so the accumulator sits behind a mutex and the send happens outside it. Batches are in-flight memory, so the flush is per rows reached rather than per album page. Measured per `Song` (strings, boxed longs and genre list included): ~0.7 KiB, so a 500-row batch is ~334 KiB plus ~51 KiB of the `names`/`genres` structures `writeSongs` derives, where a whole album page (~5,500 rows) would be ~3.8 MiB. `albumSongs()` is also a bare `channelFlow`, whose channel holds 64 elements whatever their size, so 500-row batches would have made its worst case ~21 MiB; it now carries the same `.buffer(1)` that `entityBatches()` already uses on itself. The rest of the in-flight rows scale with `syncConcurrency`, so that leaves the default's batch in hand and a few MiB at the highest setting. `SubsonicSourceTest.albumSongsAreEmittedInBoundedBatches` asserts the emitted sizes, [500, 100] for 600 songs spread over 200 three-song albums, which pins the carried remainder as well as the flush: verified it fails on the pre-change code (200 batches of 3) and against a mutant that drops the remainder ([500, 99]). `docs/architecture.md`'s page cap was 20x out of date, so it is corrected alongside. `ktlintCheck`, `testDebugUnitTest` and `lintDebug` pass.