Name the scan lock file from the database's resolved path (closes #101)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
The lock file was the configured database path with .lock appended, so a scan reaching the database through a symlink to it locked a different file and ran alongside a scan holding the lock through the real path. Before it takes the lock, scan now creates the database file, empty, if it is missing, following a symlink to its target as SQLite would, and names the lock file from that file's path with symlinks resolved. A scan started before the database exists therefore takes the same lock file as every later scan through the same path. testDBPath resolves its temp dir, so lock errors name the expected path where the temp dir is itself reached through a symlink. Model: opus-5-5
This commit is contained in:
@@ -271,16 +271,21 @@ All three subcommands operate on a single SQLite database file:
|
||||
error (exit 1) telling the user to run `scan` first.
|
||||
- Only one `scan` runs against a database at a time. For its whole run, `scan`
|
||||
holds an exclusive `flock(2)` lock on a lock file beside the database, named
|
||||
by appending `.lock` to the database path (`/var/lib/sfdupes/db.sqlite.lock`
|
||||
by default), taken before it walks the filesystem or opens the database. A
|
||||
second `scan` against the same database does not wait: it fails at once with a
|
||||
one-line error naming the lock file and exits 1, without walking anything or
|
||||
opening the database, and the running scan carries on. The lock file is
|
||||
created on first use, open to its owner only, and left in place: a leftover
|
||||
file blocks nothing, because the lock ends with the process holding it however
|
||||
it ends, a fatal error or an interrupt included, and deleting the file while a
|
||||
scan runs would let a second scan start. `report` and `trees` never take the
|
||||
lock, so they run during a scan.
|
||||
by appending `.lock` to the database's path with symlinks resolved
|
||||
(`/var/lib/sfdupes/db.sqlite.lock` by default), taken before it walks the
|
||||
filesystem or opens the database. Every path to one database, through a
|
||||
symlink to the file or to a directory above it, names the same lock file. Just
|
||||
before it takes the lock, `scan` creates the database file, empty, if it is
|
||||
missing (through a symlink at the database path, it creates the symlink's
|
||||
target), so the lock file is named the same way before the first scan sets up
|
||||
the database as after. A second `scan` against the same database does not
|
||||
wait: it fails at once with a one-line error naming the lock file and exits 1,
|
||||
without walking anything or opening the database, and the running scan carries
|
||||
on. The lock file is created on first use, open to its owner only, and left in
|
||||
place: a leftover file blocks nothing, because the lock ends with the process
|
||||
holding it however it ends, a fatal error or an interrupt included, and
|
||||
deleting the file while a scan runs would let a second scan start. `report`
|
||||
and `trees` never take the lock, so they run during a scan.
|
||||
- While `scan` runs, the database is in WAL journal mode with a busy timeout, so
|
||||
running a report while a cron `scan` is in progress is safe. The filesystem is
|
||||
authoritative; the database is an eventually-consistent reflection of it.
|
||||
|
||||
Reference in New Issue
Block a user