Windows: DST timestamp "fix" (bug 18078) marks ~90% of library as changed after every clock change; freezes server when triggered by a folder browse
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 45/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- perl, sqlite
- Domain
- database, operating-systems
Research direction
The bug is in Slim/Utils/Scanner/Local.pm around line 209 in rescan, and Slim/Utils/Misc.pm findAndScanDirectoryTree. Read those subs and the tracks/scanned_files schema. Reproduce by toggling metainformation.lastRescanTimeIsDST on Windows, then verify only actually changed files are re-read and folder browse doesn't block server. Done: no global shift, scan counts match real changes.
Written by the indexing model from the issue text.
Description
Describe the bug
This morning my Windows Lyrion server froze while streaming music.
Turns out the issue is related to the daylight saving time change last night (reported before as #824 but closed as stale).
My AI traced it to two interacting things:
- Perl's
stat()on Windows (LMS ships Perl 5.32.1, MSWin32-x64-multi-thread, gcc build on the Microsoft C runtime) reportsmtimeone hour off for files whose modification date lies in the other DST half of the year. Right now (standard time) a file saved in July reads 3600 s early, a file saved in November reads correctly; in summer it is the reverse. This is the old CRT_statbehaviour; a single global offset cannot describe it. Slim::Utils::Scanner::Local::rescan(bug 18078 block,Local.pm~line 209) compensates by shifting every row:UPDATE tracks SET timestamp = timestamp ± 3600. That is only correct for rows whose stored value was itself affected by the CRT error at the time of the last scan; for every other row it introduces a one-hour mismatch. In addition the persistentscanned_filestable is not shifted at all.
Net effect: after the shift, scanned_files.timestamp doesn't match tracks.timestamp for most rows, so almost every file lands in the changed set and gets its tags re-read.
Worse, the block also runs in the in-process rescan that Slim::Utils::Misc::findAndScanDirectoryTree triggers when a client browses the music folder (Scanner::Local->rescan($path, { no_async => 1, recursive => 0 })). If that is the first scan after the clock change, tracks get shifted while the scanned_files rows from the previous full scan are not, the changed query returns essentially every track under the folder, and the server re-reads them synchronously. Today that froze my server for good: port 9000 stopped answering, players stalled, the Windows service hung in STOP_PENDING until perl.exe was killed, and the isScanning flag survived in the DB and had to be cleared with abortscan.
[26-10-25 09:30:59.9909] Slim::Utils::Scanner::Local::rescan (180) Discovering audio files in D:\la musique 'pop'\platten
[26-10-25 09:31:00.4055] Slim::Utils::Scanner::Local::__ANON__ (216) DST has changed since last scan - fix timestamps in tracks table
[26-10-25 09:31:01.0025] Slim::Utils::Scanner::Local::deleteTracks (407) Removing deleted audio files (0)
[26-10-25 09:31:01.0026] Slim::Utils::Scanner::Local::updateTracks (574) Rescanning changed audio files (59479)
To Reproduce
- Windows, NTFS, library whose files carry modification dates from both halves of the year, last full scan done before a DST change.
- Let the clock change happen (or set
metainformation.lastRescanTimeIsDSTto the opposite of the current state). - Either run "Look for new and changed media" → nearly every file is reported as changed and re-read (see numbers below), or
- browse the music folder from a client (iPeng, Material, ...) → the same re-read runs inside the server process and blocks it.
Expected behavior
Only files that actually changed are re-read. A folder browse never performs a library-sized synchronous rescan inside the server.
Measurements (after the DST block had run, before the files were re-read)
Sample of 1,500 random local tracks, real_mtime − tracks.timestamp (real mtime read via the Win32 API):
| offset | share of resolvable tracks | file dates | Perl stat vs real mtime |
|---|---|---|---|
| +3600 s | ~72 % | winter-dated (Nov 2017 …) | equal |
| +7200 s | ~15 % | summer-dated (Jul 2001, Apr 2003, May 2010) | Perl reads 3600 s early |
| 0 | ~13 % | winter-dated (Nov 2009) | equal |
External scanner run right afterwards (scanner.pl --rescan, three media dirs):
09:56:06 Discovering audio files in D:\la musique 'pop'\platten
09:56:30 Removing deleted audio files (2)
09:56:30 Rescanning changed audio files (50422)
10:04:20 Scanning new audio files (0)
10:04:20 Discovering audio files in D:\la musique 'pop'\unorg
10:04:38 Removing deleted audio files (1)
10:04:39 Rescanning changed audio files (6350)
10:06:48 Discovering audio files in D:\la musique 'pop'\kinder
10:06:49 Rescanning changed audio files (666)
10:07:07 Scanning new audio files (0)
57,438 files re-read, 3 files actually deleted, 0 new, 0 actually changed, 11 minutes. Afterwards a sample of 300 random tracks shows perl stat mtime == tracks.timestamp for all 300, i.e. the re-read merely rewrote what the shift had broken.
System Information
- OS: Windows 11 Pro 10.0.26300, LMS running as the
squeezesvcservice - Hardware: x86_64 (Ryzen 9 9950X), library on an internal NTFS volume
- Web skin: Default
- Browser: Chrome
- LMS Version: 9.1.1 (1779815526), Perl 5.32.1 MSWin32-x64-multi-thread
- Players: Squeezebox3, iPeng (the browse that triggered the in-process scan came from iPeng), SqueezeLite
- Library: ~57,000 local files, SQLite,
dbhighmemon
Additional context / suggestions
- The blanket ±3600 shift cannot be correct given the CRT behaviour. Options: (a) read mtime through the Win32 API (
Win32API::FileGetFileTime, orWin32::UTCFileTime) so the value is DST-independent and drop the shift; or (b) at minimum shiftscanned_filestoo, so the in-process path does not see a global mismatch. findAndScanDirectoryTreeshould not run a potentially library-sized synchronous rescan in the server; cap thechangedset or hand off to the external scanner above some threshold.- Consider resetting
isScanningon server start when no scanner process exists.
Happy to test a patch; I can reproduce on demand by toggling the DST flag in metainformation.
- Dominant language
- Perl
- Stars
- 1.8k
- Forks
- 386
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 14
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- No pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from LMS-Community/slimserver
-
Sync playback crashes repeatedly in a sync group (v9.1.1)Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
LMS-Community/slimserver#1701 · 6 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
LMS-Community/slimserver#1555 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
LMS-Community/slimserver#1706 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 55/100
LMS-Community/slimserver#1683 · 3 comments ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
LMS-Community/slimserver#1677 · 1 comment ·
Maintainers usually reply within 1 day
All issues in LMS-Community/slimserver
Similar issues
-
ProcessCopy sets SyncState of origin Process to not_syncPossibly taken @stefane2741 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
✏️ Editing - Tags
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
openfoodfacts/openfoodfacts-server#15127 · 2 comments ·
Maintainers usually reply within 1 day
-
FIDO2 (ecdsa-sk / ed25519-sk) ingress keys rejected: missing in get_supported_ssh_algorithms_listOpen
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ovh/the-bastion#695 ·
-
Range length wrong when an abbreviated range end contains a 0 (`pages = {315-20}` gives `\range{pages}{11}`)Possibly taken @plk claimed this 1 day ago. OpenBug fixedindev
Difficulty 2/5 1-3 hours Newbie friendliness 78/100