There is a danger of deadlock wherever we have database operations
inside a database iterator. The iterator itself pins a connection, so
the nested operations need another connection; if we've reached
maxOpenConns then it blocks until a connection is released. If all
connections are consumed by such iterators then none can make progress.
Luckily, we have limited concurrency for most such iterator loops. They
are part of scanning, pulling, reverting, etc where there is only ever
one such routine per folder. The exception is block reuse in the copier
routine, which is limited by the `Copiers` setting per folder. This
could in practice deadlock since you could set copiers to eight and end
up with six `AllLocalBlocksWithHash` iterators when `maxDBConns=6`, all
of which need to make additional database calls inside the loop.
This PR fixes the problem twice;
- The problematic loop does not need to be reentrant. The set of blocks
that may be returned by the iterator is finite so we can easily just
collect them to a slice before we start processing them. This avoids the
problem entirely.
- We do not need to limit database connections as strictly as we
currently do. Increase the maximum allowed, while reducing the number of
held-open idle connections slightly. This is not an exact science, but
ideally we want "most" operations to be able to use the pinned
connections to avoid cache churn. Most operations are short lived
queries, or single-goroutine iterators with short lived queries inside,
so four connections seems like it should usually be enough. 🤷
- Additionally, set a cap on Copiers. Currently you could set it to an
arbitrarily large number, which is not advantageous. Limit it to
2*NumCPU which scales somewhat with system performance.
Signed-off-by: Jakob Borg <jakob@kastelo.net>
This improves performance when running with fsync enabled. The key
observation is that the OS may coalesce multiple concurrent fsyncs, so
we gain some performance by issuing them in parallell. Since they run
from the finisher routine, a simple fix is to run multiple finisher
routines. That's the `after` line in the graph below. The other step is
to do the same for the directory fsyncs, issuing them concurrently with
a limiter. In both cases I used the Copiers value as the concurrency
factor. Additionally, add some buffering to the channels between
routines to minimise stalls where a routine needs to wait for another.
This is the `after2` line.
All in all, this speeds up syncing 25k tiny files from 190s to 130s, a
30% improvement.
<img width="821" height="540" alt="Screenshot 2026-07-25 at 22 58 34"
src="https://github.com/user-attachments/assets/18272c89-a99e-4ee4-9b5e-b278967ea7c6"
/>
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
This tweaks the casefs cache to do in-place updates for modifications we
perform, instead of dropping the entire cache. That is, we add, remove
and rename things in the cache after having done the corresponding
operation.
We still drop the cache entirely on larger operations like MkdirAll, and
before Walk, and the 1s expiry is unchanged.
The result is performance much closer to the original. I set up a device
with 150 000 tiny files and measured the time it took for a blank device
to come online and sync all the files, with default settings apart from
`fsync` being disabled, as it otherwise dominated the benchmark...
There are three runs, all are identical in the first 40 seconds which is
the index transmission & reception, then we can see the `sensitive` run
(casefs disabled) finishes at 137 seconds (137-40 = 97s spent syncing);
the `before` run finishes at 238s (198s spent syncing); the `after` run
finishes at 142 seconds (102s spent syncing). So, effectively the sync
time is cut in half, and is now very close to having case sensitive
enabled.
<img width="812" height="476" alt="Screenshot 2026-07-25 at 23 22 22"
src="https://github.com/user-attachments/assets/0aa39712-9fd7-46cf-969e-b6a20f150a3c"
/>
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
There was a rare, tricky race condition in the setup, where a pull could
get started, find the blocks from the file we injected in
updateLocalsFromScanning, copy them into a temp file, then fail the pull
because the file was already in place. Then later, after the revert,
another pull would start, find the temp file and reuse it, making it so
needed=0 instead of the expected needed>0 the test checks for.
This avoids all that by setting the local index data prior to getting it
from the "remote" device, thus avoiding the initial pull and temp file
creation etc.
Signed-off-by: Jakob Borg <jakob@kastelo.net>
The tooltip on the dismiss button for notifications confused me a bit
(wdym mean "may" recur?) so here is an attempt at a clearer message.
Signed-off-by: tbodt <tbodt@tbodt.com>
adds a bottom margin to buttons inside panel footers, ensuring a
comfortable gap between wrapped button rows. (Fixes#10689 )
Signed-off-by: rohitanwar <mst10041967@gmail.com>
Only run rename detection for new files. This skips an expensive check
for all updates to existing files. The tradeoff is that we no longer
immediately detect renames on top of another file as a rename -- this
may instead become a copy+delete operation on the destination.
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
Effectively nobody uses it, it cost more than it was worth, the code
doesn't carry its weight. Remove it and let any similar mechanism in the
future be an internal implementation detail.
Closes#10812
Signed-off-by: Jakob Borg <jakob@kastelo.net>
Adds a `lazyCollapse` directive that renders panel body content only
while expanded. Collapsed panels contribute zero watchers — content is
added to the DOM on expand (`show.bs.collapse`) and removed after the
collapse animation completes (`hidden.bs.collapse`).
Signed-off-by: Finomosec <1665799+Finomosec@users.noreply.github.com>
Previously we'd skip the health check for an up-to-date folder, so it
would continue looking up-to-date even when the folder path missing.
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
The recent change to disallow following symlinks by default conflicts
with the expected behavior of .stignore. This adds a new flag which may
be passed to OpenFile to skip default symlink-forbidding open flag.
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
We used a size computation to generate reasonably sized batches, but the
ProtoSize call is fairly expensive as it requires a conversion to a wire
type etc. We don't need that much precision. Instead, just limit to 1000
files or 5000 blocks, which is likely approximately that much data
anyway.
Closes#10707
Signed-off-by: Jakob Borg <jakob@kastelo.net>
The counts needs to be modified manually since we're not running
triggers during migrations. The schema version needed to be bumped.
Signed-off-by: Jakob Borg <jakob@kastelo.net>
While preparing the command, attempt to verify that the template
expansion happens in a way that will result in a non-shell-injection
command. I don't presume to say that this is a 100% prevention, and the
script itself can always do dumb shit with the file path later.
Nonetheless, we should make a best-effort attempt.
Equally, this could generate false positives for commands that are
strangely written but in fact safe. I think this is acceptable; external
versioning is currently used by approximately 0.02% of users, and
presumably most of them have a setup that is sane.
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
Resp. directories for the database, and both dirs and symlinks for
`FileInfo.FileSize`. Instead handle it in the UI progress percentage. We
even already have special cases there for deletions, might as well
handle directories just like any other zero-sized needed item there.
This went through the very thorough testing of running it on my laptop,
the migration was applied and it seemed to be working fine after.
---------
Signed-off-by: Simon Frei <freisim93@gmail.com>
During initial folder scan and the scan is interrupted with a
context.Canceled error, it was previously logged as a "Failed initial
scan" error, which can be misleading
The change adds a explicit check for context.Canceled and silently
ignores it (fixes#10363)
Signed-off-by: Henrik Bråthen <henrikbs@proton.me>
Verify that block requests have a hash and that it's correct. This helps
prevent certain races and ensure that only expected data is ever
returned in response to a request.
(In Syncthing prior to 1.28.1 the block hash was omitted for encrypted
requests from trusted devices. This breaks compatibility with that
specific config on those versions.)
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>
Ensure file was a file before the shortcut as well as after... (This was
implied when talking to a correct implementation, but not enforced.)
Make our file opening operations safe by default by ensuring the last
path component is not a symlink.
---------
Signed-off-by: Jakob Borg <jakob@kastelo.net>