Content you’re reporting an issue with
Have you made any edits to this content in Roon?
Is the album identified in Roon?
Is this content from local files, TIDAL, or Qobuz?
Screenshot of import settings
Description of the issue
Roon Server version: 2.67 (build 1661) production
Server OS: GentooPlayer (Linux 6.12.77 x86_64, PREEMPT_RT), .NET 10 runtime
Hardware: Intel NUC, i5-8259U, 16 GB RAM, NVMe SSD
Library size: ~22,400 tracks / ~3,900 albums / 91 playlists
Audio chain: RoonServer → RAAT → RoonBridge host (RPi4) → Diretta → Diretta Target (RPi4, Allo DigiOne Signature) → SPDIF → DAC
Summary
After a fresh RoonServer restart, track-to-track transitions take ~1 second.
Over the following days of uptime they get progressively worse, reaching
25–30 seconds by day 3–5. A restart fixes it, then it degrades again.
Root cause I traced
The delay maps exactly to the library mutation step. From the RoonServer log:
[library] endmutation in 28791ms
I tracked endmutation timing across 5 days of uptime:
Day 0 (after restart): ~130 ms
Day 1: ~5,000–11,000 ms
Day 2: ~11,000 ms
Day 3: 15,000 → 28,000 ms
Day 5: 28,000–30,000 ms
During the stall the CPU is idle and RAM is free (8+ GB free, no swap,
no THP, NVMe disk, the DB flush itself is <100 ms), so the thread is
blocked/waiting, not working.
The actual culprit is the Sentry telemetry cache.
~/.RoonServer/SentryCache had accumulated 1,477 .envelope files (42 MB).
It appears the Sentry .NET SDK enumerates/manages this directory during
library operations, so endmutation scales with the number of cached
envelopes.
Proof: with the server still running (no restart), I deleted the
*.envelope files. endmutation dropped from 28,791 ms to ~130 ms
immediately, and transitions went back to ~1 second. A ~200x improvement
from a single rm, no restart.
Why the cache fills up
The envelopes are almost all failed network captures:
651 × System.Net.WebException: The request was aborted: The request was canceled.
526 × System.Net.WebException: (504) Gateway Timeout
128 × System.IndexOutOfRangeException: Index was outside the bounds of the array
16 × System.AggregateException: A Task's exception(s) were not observed
(+ assorted SocketException / IOException)
The 504 / canceled errors are Roon ARC connectivity checks failing
~150 times per hour, 24/7 (multinat Failed, UPnP NotFound, NAT-PMP NotFound,
Port Check 504 from api.roonlabs.net). I do not use ARC and there is no way
to disable it, so it retries forever and Sentry can never upload the events
(network is the thing failing), so they pile up on disk indefinitely.
The 128 × IndexOutOfRangeException is a separate, genuine bug. Stack trace:
Dictionary<TKey,TValue>.TryInsert
RemotingServerV2._PutObject
Sooloos_Broker_Api_PerformerLite_Adapter.Serialize
RemotingServerV2.NotifyStateChange
ServerConnectionV2.OnBeforeEntry
SynchronizationContextThread.OnBeforeEntry
IndexOutOfRangeException inside Dictionary.TryInsert is the classic signature
of a Dictionary being accessed from multiple threads without locking.
Three issues for you to consider
-
SentryCache should be bounded. A growing offline cache should never be
able to degrade core library performance 200x. Please cap the cache size /
file count, or stop touching it on the library hot path. -
ARC needs a real off switch. When ARC can’t establish connectivity it
should back off, not retry every ~25–30 s forever and generate an
un-uploadable exception each time. Users without port forwarding pay this
cost permanently. (The “port 0” workaround resets on every restart.) -
Thread-safety bug: concurrent unlocked access to a Dictionary in
PerformerLite serialization (stack above).
My current workaround
Hourly cron that deletes SentryCache/*.envelope older than 5 minutes.
Keeps transitions at ~1 s with no restarts. Happy to provide full logs.