Deluan Quintão 7736bbb545
fix(cache): write the completion marker before closing the cache writer (#5927)
* fix(cache): write the completion marker before closing the cache writer

Readers of an in-progress cache write see EOF the moment the writer closes,
but the .complete marker was created after the close, on the background
goroutine — so a fully-read stream did not mean the cache was done touching
disk. The new artwork precache spec ends right at EOF, and its
GinkgoT().TempDir() cleanup raced the marker creation, failing the Windows CI
job with 'unlinkat ...: The directory is not empty' (the race also reproduces
on macOS, 2 of 3 runs, with the tightened test).

Writing the marker after a clean copy but before Close makes reader-EOF imply
every on-disk write for the entry is finished. A failed writer Close still
invalidates the entry, which removes both the marker and the data file. The
existing marker test now asserts the marker exists immediately at EOF instead
of Eventually.

* fix(artwork): never dispatch queue items after the drain context is cancelled

The 10x Windows stress run for the previous commit surfaced a second flake in
the same package: 'leaves undispatched items queued when cancelled mid-batch'
lost row alc7 in 4 of 10 runs. In drain, when a semaphore slot is free and the
context is already cancelled, both cases of the blocking select are ready and
Go picks one at random — so a cancelled drain could still dispatch items. A
non-blocking Done check before the select gives cancellation priority.

The race was invisible on Linux/macOS only by accident: the spec seeded the
album repo with a single album (each SetData overwrote the last), so only the
final row (alc7) resolved to absent and got deleted when dispatched; the
others fell on the retry path and survived. Nanosecond enqueue timestamps
made alc0 always first out of the mock dequeue, masking the race, while
Windows' coarse clock ties the timestamps and randomizes the order. The spec
now seeds all eight albums, which made the race reproduce locally on the
first try (row alc0) and now guards the fix on every platform.

* test: give cache-init waits a 10s timeout for loaded CI runners

A 10x parallel Windows stress run timed out one artwork spec in BeforeEach:
the FileCache init goroutine (mkdir + reload walk) took over Gomega's default
1s Eventually timeout under shared-runner disk contention. Bump the three
identical init waits (two artwork suites and the utils/cache helper) to 10s.

* test(scanner): widen watcher debounce margins for loaded CI runners

The watcher debouncing spec asserts 'no scan yet' inside 20ms Consistently
windows while the debounce wait was only 50ms — a 2.5x margin that a loaded
Windows runner blows through by delaying the timer-reset notification, firing
the scan early (failed all three FlakeAttempts in a 10x stress run). Raise the
test debounce wait to 200ms (10x the observation windows) and the scan-fired
Eventually timeouts to 2s to match.

* refactor(artwork): collapse drain cancellation into a single exit path

Replace the non-blocking ctx pre-check plus duplicated select exit with one
select and a ctx.Err() check after it. Besides removing the duplication, this
closes the residual race: a cancellation landing between the two selects could
still let the blocking select randomly pick the free semaphore slot and
dispatch the item. Now a dispatch is only possible when the context was live
after slot acquisition.

* style: trim flaky-test fix comments to single lines

Compress each two-line comment added by this PR to the one line that carries
the invariant; drop the narration around it.
2026-08-10 14:10:05 -04:00
2026-07-20 09:49:48 -04:00

Navidrome logo

Navidrome Music Server  Tweet

Last Release Build Downloads Docker Pulls Dev Chat Subreddit Contributor Covenant Gurubase

Navidrome is an open source web-based music collection server and streamer. It gives you freedom to listen to your music collection from any browser or mobile device. It's like your personal Spotify!

Note: The master branch may be in an unstable or even broken state during development. Please use releases instead of the master branch in order to get a stable set of binaries.

Check out our Live Demo!

Any feedback is welcome! If you need/want a new feature, find a bug or think of any way to improve Navidrome, please file a GitHub issue or join the discussion in our Subreddit. If you want to contribute to the project in any other way (ui/backend dev, translations, themes), please join the chat in our Discord server.

Installation

See instructions on the project's website

Cloud Hosting

PikaPods has partnered with us to offer you an officially supported, cloud-hosted solution. A share of the revenue helps fund the development of Navidrome at no additional cost for you.

PikaPods

Features

  • Handles very large music collections
  • Streams virtually any audio format available
  • Reads and uses all your beautifully curated metadata
  • Great support for compilations (Various Artists albums) and box sets (multi-disc albums)
  • Multi-user, each user has their own play counts, playlists, favourites, etc...
  • Very low resource usage
  • Multi-platform, runs on macOS, Linux and Windows. Docker images are also provided
  • Ready to use binaries for all major platforms, including Raspberry Pi
  • Automatically monitors your library for changes, importing new files and reloading new metadata
  • Supports lyrics from sidecar .ttml, .yaml/.yml Lyricsfile, .elrc, .lrc, .srt, .txt files and embedded TTML, Enhanced LRC, LRC, SRT, and plain-text tags (via lyricspriority)
  • Themeable, modern and responsive Web interface based on Material UI
  • Compatible with all Subsonic/Madsonic/Airsonic clients
  • Transcoding on the fly. Can be set per user/player. Opus encoding is supported
  • Translated to various languages

Translations

Navidrome uses POEditor for translations, and we are always looking for more contributors

Documentation

All documentation can be found in the project's website: https://www.navidrome.org/docs. Here are some useful direct links:

Screenshots

Languages
Go 81.8%
JavaScript 15.2%
Rust 2.3%
Makefile 0.2%
Shell 0.2%
Other 0.2%