mirror of
https://github.com/syncthing/syncthing.git
synced 2025-12-24 06:28:10 -05:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25ae01b0d7 | ||
|
|
66583927f8 | ||
|
|
f0328abeaa | ||
|
|
4b8d07d91c | ||
|
|
c33daca3b4 | ||
|
|
a533f453f8 |
1
.github/workflows/trigger-nightly.yaml
vendored
1
.github/workflows/trigger-nightly.yaml
vendored
@@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
|
||||
trigger-nightly:
|
||||
if: github.repository_owner == 'syncthing'
|
||||
runs-on: ubuntu-latest
|
||||
name: Push to release-nightly to trigger build
|
||||
steps:
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Вида „{{receiveEncrypted}}“ може да бъде избран само при добавяне на папка.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Видът папката „{{receiveEncrypted}}“ не може да бъде променян след нейното създаване. Трябва да я премахнете, изтриете или разшифровате съдържанието и да добавите папката отново.",
|
||||
"Folders": "Папки",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Грешка при започване на наблюдението за промени на следните папки. Всяка минута ще бъде извършван нов опит, така че грешката скоро може да изчезне. Ако все пак не изчезне, отстранете нейната първопричина или потърсете помощ ако не съумявате.",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Грешка при започване на наблюдението за промени на следните папки. Всяка минута ще бъде извършван нов опит, така че грешката скоро може да изчезне. Ако все пак не изчезне, отстранете първопричината или ако не съумявате потърсете помощ.",
|
||||
"Forever": "Завинаги",
|
||||
"Full Rescan Interval (s)": "Интервал на пълно обхождане (секунди)",
|
||||
"GUI": "Интерфейс",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabética",
|
||||
"Altered by ignoring deletes.": "Cambiado por ignorar o borrado.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre acendido cando o cartafol é de tipo \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Un comando externo xestiona as versións. Ten que eliminar o ficheiro do cartafol compartido. Si a ruta ao aplicativo contén espazos, deberían ir acotados.",
|
||||
"Anonymous Usage Reporting": "Informe anónimo de uso",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do informe de uso anónimo cambiou. Quere usar o novo formato?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Corpo:",
|
||||
"Bugs": "Erros",
|
||||
"Cancel": "Cancelar",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Non se pode activar cando o cartafol é de tipo \"{{foldertype}}\".",
|
||||
"Changelog": "Rexistro de cambios",
|
||||
"Clean out after": "Limpar despois",
|
||||
"Cleaning Versions": "Limpando Versións",
|
||||
@@ -80,6 +82,7 @@
|
||||
"Custom Range": "Rango personalizado",
|
||||
"Danger!": "Perigo!",
|
||||
"Database Location": "Localización da Base de Datos",
|
||||
"Debug": "Depurar",
|
||||
"Debugging Facilities": "Ferramentas de depuración",
|
||||
"Default": "Predeterminado",
|
||||
"Default Configuration": "Configuración Predeterminada",
|
||||
@@ -140,6 +143,7 @@
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Activa o envío de atributos extendidos a outros dispositivos, e aplicar os atributos extendidos recibidos. Podería requerir a execución con privilexios elevados.",
|
||||
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Activa o envío de atributos extendidos a outros dispositivos, pero non aplica atributos extendidos que se reciben. Isto podería afectar significativamente ao rendemento. Sempre está activado cando «Sincr Atributos Extendidos\" está activado.",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Activa o envío de información sobre a propiedade a outros dispositivos, e aplica a información sobre a propiedade cando se recibe. Normalmente require a execución con privilexios elevados.",
|
||||
"Enables sending ownership information to other devices, but not applying incoming ownership information. This can have a significant performance impact. Always enabled when \"Sync Ownership\" is enabled.": "Activa o envío a outros dispositivos de información sobre a propiedade, pero non aplica información entrante sobre a propiedade. Isto pode afectar en gran medida ao rendemento. Está sempre activado cando \"Sincronización da propiedade\" está activada.",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduza un número non negativo (por exemplo, \"2.35\") e seleccione unha unidade. As porcentaxes son como partes totais do tamaño do disco.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Introduza un número de porto non privilexiado (1024-65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduza direccións separadas por comas (\"tcp://ip:porto\", \"tcp://host:porto\") ou \"dynamic\" para realizar o descubrimento automático da dirección.",
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/syncthing/syncthing/internal/db"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -65,6 +66,8 @@ func Open(path string, opts ...Option) (*DB, error) {
|
||||
}
|
||||
|
||||
_ = os.MkdirAll(path, 0o700)
|
||||
initTmpDir(path)
|
||||
|
||||
mainPath := filepath.Join(path, "main.db")
|
||||
mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, migrations)
|
||||
if err != nil {
|
||||
@@ -114,6 +117,8 @@ func OpenForMigration(path string) (*DB, error) {
|
||||
}
|
||||
|
||||
_ = os.MkdirAll(path, 0o700)
|
||||
initTmpDir(path)
|
||||
|
||||
mainPath := filepath.Join(path, "main.db")
|
||||
mainBase, err := openBase(mainPath, 1, pragmas, schemas, migrations)
|
||||
if err != nil {
|
||||
@@ -143,3 +148,24 @@ func (s *DB) Close() error {
|
||||
}
|
||||
return wrap(s.baseDB.Close())
|
||||
}
|
||||
|
||||
func initTmpDir(path string) {
|
||||
if build.IsWindows || build.IsDarwin || os.Getenv("SQLITE_TMPDIR") != "" {
|
||||
// Doesn't use SQLITE_TMPDIR, isn't likely to have a tiny
|
||||
// ram-backed temp directory, or already set to something.
|
||||
return
|
||||
}
|
||||
|
||||
// Attempt to override the SQLite temporary directory by setting the
|
||||
// env var prior to the (first) database being opened and hence
|
||||
// SQLite becoming initialized. We set the temp dir to the same
|
||||
// place we store the database, in the hope that there will be
|
||||
// enough space there for the operations it needs to perform, as
|
||||
// opposed to /tmp and similar, on some systems.
|
||||
dbTmpDir := filepath.Join(path, ".tmp")
|
||||
if err := os.MkdirAll(dbTmpDir, 0o700); err == nil {
|
||||
os.Setenv("SQLITE_TMPDIR", dbTmpDir)
|
||||
} else {
|
||||
slog.Warn("Failed to create temp directory for SQLite", slogutil.FilePath(dbTmpDir), slogutil.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,28 @@ package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/syncthing/syncthing/internal/db"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/thejerf/suture/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
internalMetaPrefix = "dbsvc"
|
||||
lastMaintKey = "lastMaint"
|
||||
internalMetaPrefix = "dbsvc"
|
||||
lastMaintKey = "lastMaint"
|
||||
lastSuccessfulGCSeqKey = "lastSuccessfulGCSeq"
|
||||
|
||||
gcMinChunks = 5
|
||||
gcChunkSize = 100_000 // approximate number of rows to process in a single gc query
|
||||
gcMaxRuntime = 5 * time.Minute // max time to spend on gc, per table, per run
|
||||
)
|
||||
|
||||
func (s *DB) Service(maintenanceInterval time.Duration) suture.Service {
|
||||
@@ -91,16 +100,41 @@ func (s *Service) periodic(ctx context.Context) error {
|
||||
}
|
||||
|
||||
return wrap(s.sdb.forEachFolder(func(fdb *folderDB) error {
|
||||
fdb.updateLock.Lock()
|
||||
defer fdb.updateLock.Unlock()
|
||||
// Get the current device sequence, for comparison in the next step.
|
||||
seq, err := fdb.GetDeviceSequence(protocol.LocalDeviceID)
|
||||
if err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
// Get the last successful GC sequence. If it's the same as the
|
||||
// current sequence, nothing has changed and we can skip the GC
|
||||
// entirely.
|
||||
meta := db.NewTyped(fdb, internalMetaPrefix)
|
||||
if prev, _, err := meta.Int64(lastSuccessfulGCSeqKey); err != nil {
|
||||
return wrap(err)
|
||||
} else if seq == prev {
|
||||
slog.DebugContext(ctx, "Skipping unnecessary GC", "folder", fdb.folderID, "fdb", fdb.baseName)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := garbageCollectOldDeletedLocked(ctx, fdb); err != nil {
|
||||
// Run the GC steps, in a function to be able to use a deferred
|
||||
// unlock.
|
||||
if err := func() error {
|
||||
fdb.updateLock.Lock()
|
||||
defer fdb.updateLock.Unlock()
|
||||
|
||||
if err := garbageCollectOldDeletedLocked(ctx, fdb); err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
if err := garbageCollectBlocklistsAndBlocksLocked(ctx, fdb); err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
return tidy(ctx, fdb.sql)
|
||||
}(); err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
if err := garbageCollectBlocklistsAndBlocksLocked(ctx, fdb); err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
return tidy(ctx, fdb.sql)
|
||||
|
||||
// Update the successful GC sequence.
|
||||
return wrap(meta.PutInt64(lastSuccessfulGCSeqKey, seq))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -119,7 +153,7 @@ func tidy(ctx context.Context, db *sqlx.DB) error {
|
||||
}
|
||||
|
||||
func garbageCollectOldDeletedLocked(ctx context.Context, fdb *folderDB) error {
|
||||
l := slog.With("fdb", fdb.baseDB)
|
||||
l := slog.With("folder", fdb.folderID, "fdb", fdb.baseName)
|
||||
if fdb.deleteRetention <= 0 {
|
||||
slog.DebugContext(ctx, "Delete retention is infinite, skipping cleanup")
|
||||
return nil
|
||||
@@ -171,37 +205,108 @@ func garbageCollectBlocklistsAndBlocksLocked(ctx context.Context, fdb *folderDB)
|
||||
}
|
||||
defer tx.Rollback() //nolint:errcheck
|
||||
|
||||
if res, err := tx.ExecContext(ctx, `
|
||||
DELETE FROM blocklists
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM files WHERE files.blocklist_hash = blocklists.blocklist_hash
|
||||
)`); err != nil {
|
||||
return wrap(err, "delete blocklists")
|
||||
} else {
|
||||
slog.DebugContext(ctx, "Blocklist GC", "fdb", fdb.baseName, "result", slogutil.Expensive(func() any {
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return slogutil.Error(err)
|
||||
}
|
||||
return slog.Int64("rows", rows)
|
||||
}))
|
||||
}
|
||||
// Both blocklists and blocks refer to blocklists_hash from the files table.
|
||||
for _, table := range []string{"blocklists", "blocks"} {
|
||||
// Count the number of rows
|
||||
var rows int64
|
||||
if err := tx.GetContext(ctx, &rows, `SELECT count(*) FROM `+table); err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
|
||||
if res, err := tx.ExecContext(ctx, `
|
||||
DELETE FROM blocks
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM blocklists WHERE blocklists.blocklist_hash = blocks.blocklist_hash
|
||||
)`); err != nil {
|
||||
return wrap(err, "delete blocks")
|
||||
} else {
|
||||
slog.DebugContext(ctx, "Blocks GC", "fdb", fdb.baseName, "result", slogutil.Expensive(func() any {
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return slogutil.Error(err)
|
||||
chunks := max(gcMinChunks, rows/gcChunkSize)
|
||||
l := slog.With("folder", fdb.folderID, "fdb", fdb.baseName, "table", table, "rows", rows, "chunks", chunks)
|
||||
|
||||
// Process rows in chunks up to a given time limit. We always use at
|
||||
// least gcMinChunks chunks, then increase the number as the number of rows
|
||||
// exceeds gcMinChunks*gcChunkSize.
|
||||
t0 := time.Now()
|
||||
for i, br := range randomBlobRanges(int(chunks)) {
|
||||
if d := time.Since(t0); d > gcMaxRuntime {
|
||||
l.InfoContext(ctx, "GC was interrupted due to exceeding time limit", "processed", i, "runtime", time.Since(t0))
|
||||
break
|
||||
}
|
||||
return slog.Int64("rows", rows)
|
||||
}))
|
||||
|
||||
// The limit column must be an indexed column with a mostly random distribution of blobs.
|
||||
// That's the blocklist_hash column for blocklists, and the hash column for blocks.
|
||||
limitColumn := table + ".blocklist_hash"
|
||||
if table == "blocks" {
|
||||
limitColumn = "blocks.hash"
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(`
|
||||
DELETE FROM %s
|
||||
WHERE %s AND NOT EXISTS (
|
||||
SELECT 1 FROM files WHERE files.blocklist_hash = %s.blocklist_hash
|
||||
)`, table, br.SQL(limitColumn), table)
|
||||
|
||||
if res, err := tx.ExecContext(ctx, q); err != nil {
|
||||
return wrap(err, "delete from "+table)
|
||||
} else {
|
||||
l.DebugContext(ctx, "GC query result", "processed", i, "runtime", time.Since(t0), "result", slogutil.Expensive(func() any {
|
||||
rows, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return slogutil.Error(err)
|
||||
}
|
||||
return slog.Int64("rows", rows)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wrap(tx.Commit())
|
||||
}
|
||||
|
||||
// blobRange defines a range for blob searching. A range is open ended if
|
||||
// start or end is nil.
|
||||
type blobRange struct {
|
||||
start, end []byte
|
||||
}
|
||||
|
||||
// SQL returns the SQL where clause for the given range, e.g.
|
||||
// `column >= x'49249248' AND column < x'6db6db6c'`
|
||||
func (r blobRange) SQL(name string) string {
|
||||
var sb strings.Builder
|
||||
if r.start != nil {
|
||||
fmt.Fprintf(&sb, "%s >= x'%x'", name, r.start)
|
||||
}
|
||||
if r.start != nil && r.end != nil {
|
||||
sb.WriteString(" AND ")
|
||||
}
|
||||
if r.end != nil {
|
||||
fmt.Fprintf(&sb, "%s < x'%x'", name, r.end)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// randomBlobRanges returns n blobRanges in random order
|
||||
func randomBlobRanges(n int) []blobRange {
|
||||
ranges := blobRanges(n)
|
||||
rand.Shuffle(len(ranges), func(i, j int) { ranges[i], ranges[j] = ranges[j], ranges[i] })
|
||||
return ranges
|
||||
}
|
||||
|
||||
// blobRanges returns n blobRanges
|
||||
func blobRanges(n int) []blobRange {
|
||||
// We use three byte (24 bit) prefixes to get fairly granular ranges and easy bit
|
||||
// conversions.
|
||||
rangeSize := (1 << 24) / n
|
||||
ranges := make([]blobRange, 0, n)
|
||||
var prev []byte
|
||||
for i := range n {
|
||||
var pref []byte
|
||||
if i < n-1 {
|
||||
end := (i + 1) * rangeSize
|
||||
pref = intToBlob(end)
|
||||
}
|
||||
ranges = append(ranges, blobRange{prev, pref})
|
||||
prev = pref
|
||||
}
|
||||
return ranges
|
||||
}
|
||||
|
||||
func intToBlob(n int) []byte {
|
||||
var pref [4]byte
|
||||
binary.BigEndian.PutUint32(pref[:], uint32(n)) //nolint:gosec
|
||||
// first byte is always zero and not part of the range
|
||||
return pref[1:]
|
||||
}
|
||||
|
||||
37
internal/db/sqlite/db_service_test.go
Normal file
37
internal/db/sqlite/db_service_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2025 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBlobRange(t *testing.T) {
|
||||
exp := `
|
||||
hash < x'249249'
|
||||
hash >= x'249249' AND hash < x'492492'
|
||||
hash >= x'492492' AND hash < x'6db6db'
|
||||
hash >= x'6db6db' AND hash < x'924924'
|
||||
hash >= x'924924' AND hash < x'b6db6d'
|
||||
hash >= x'b6db6d' AND hash < x'db6db6'
|
||||
hash >= x'db6db6'
|
||||
`
|
||||
|
||||
ranges := blobRanges(7)
|
||||
buf := new(bytes.Buffer)
|
||||
for _, r := range ranges {
|
||||
fmt.Fprintln(buf, r.SQL("hash"))
|
||||
}
|
||||
|
||||
if strings.TrimSpace(buf.String()) != strings.TrimSpace(exp) {
|
||||
t.Log(buf.String())
|
||||
t.Error("unexpected output")
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ var bufPool = sync.Pool{
|
||||
},
|
||||
}
|
||||
|
||||
const hashLength = sha256.Size
|
||||
|
||||
var hashPool = sync.Pool{
|
||||
New: func() any {
|
||||
return sha256.New()
|
||||
@@ -43,9 +45,6 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou
|
||||
counter = &noopCounter{}
|
||||
}
|
||||
|
||||
hf := hashPool.Get().(hash.Hash) //nolint:forcetypeassert
|
||||
const hashLength = sha256.Size
|
||||
|
||||
var blocks []protocol.BlockInfo
|
||||
var hashes, thisHash []byte
|
||||
|
||||
@@ -62,8 +61,14 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou
|
||||
hashes = make([]byte, 0, hashLength*numBlocks)
|
||||
}
|
||||
|
||||
hf := hashPool.Get().(hash.Hash) //nolint:forcetypeassert
|
||||
// A 32k buffer is used for copying into the hash function.
|
||||
buf := bufPool.Get().(*[bufSize]byte)[:] //nolint:forcetypeassert
|
||||
defer func() {
|
||||
bufPool.Put((*[bufSize]byte)(buf))
|
||||
hf.Reset()
|
||||
hashPool.Put(hf)
|
||||
}()
|
||||
|
||||
var offset int64
|
||||
lr := io.LimitReader(r, int64(blocksize)).(*io.LimitedReader)
|
||||
@@ -102,9 +107,6 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou
|
||||
|
||||
hf.Reset()
|
||||
}
|
||||
bufPool.Put((*[bufSize]byte)(buf))
|
||||
hf.Reset()
|
||||
hashPool.Put(hf)
|
||||
|
||||
if len(blocks) == 0 {
|
||||
// Empty file
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "STDISCOSRV" "1" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "STDISCOSRV" "1" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "STRELAYSRV" "1" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "STRELAYSRV" "1" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-BEP" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.SH INTRODUCTION AND DEFINITIONS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-CONFIG" "5" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.SH OVERVIEW
|
||||
@@ -148,7 +148,7 @@ may no longer correspond to the defaults.
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<maxConcurrentWrites>16</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
@@ -250,7 +250,7 @@ may no longer correspond to the defaults.
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<maxConcurrentWrites>16</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
@@ -340,7 +340,7 @@ GUI.
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<maxConcurrentWrites>16</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
@@ -607,7 +607,7 @@ folder is located on a FAT partition, and \fB0\fP otherwise.
|
||||
.TP
|
||||
.B maxConcurrentWrites
|
||||
Maximum number of concurrent write operations while syncing. Increasing this might increase or
|
||||
decrease disk performance, depending on the underlying storage. Default is \fB2\fP\&.
|
||||
decrease disk performance, depending on the underlying storage. Default is \fB16\fP\&.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
@@ -1555,7 +1555,7 @@ are set, \fI\%\-\-auditfile\fP takes priority.
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<maxConcurrentWrites>16</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.sp
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.SH DESCRIPTION
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-FAQ" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.INDENT 0.0
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.SH ANNOUNCEMENTS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.SH MODE OF OPERATION
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.SH ROUTER SETUP
|
||||
|
||||
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-RELAY" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-RELAY" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-relay \- Relay Protocol v1
|
||||
.SH WHAT IS A RELAY?
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-REST-API" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-REST-API" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.sp
|
||||
@@ -156,7 +156,7 @@ Returns the current configuration.
|
||||
\(dqmarkerName\(dq: \(dq.stfolder\(dq,
|
||||
\(dqcopyOwnershipFromParent\(dq: false,
|
||||
\(dqmodTimeWindowS\(dq: 0,
|
||||
\(dqmaxConcurrentWrites\(dq: 2,
|
||||
\(dqmaxConcurrentWrites\(dq: 16,
|
||||
\(dqdisableFsync\(dq: false,
|
||||
\(dqblockPullOrder\(dq: \(dqstandard\(dq,
|
||||
\(dqcopyRangeMethod\(dq: \(dqstandard\(dq,
|
||||
@@ -328,7 +328,7 @@ Returns the current configuration.
|
||||
\(dqmarkerName\(dq: \(dq.stfolder\(dq,
|
||||
\(dqcopyOwnershipFromParent\(dq: false,
|
||||
\(dqmodTimeWindowS\(dq: 0,
|
||||
\(dqmaxConcurrentWrites\(dq: 2,
|
||||
\(dqmaxConcurrentWrites\(dq: 16,
|
||||
\(dqdisableFsync\(dq: false,
|
||||
\(dqblockPullOrder\(dq: \(dqstandard\(dq,
|
||||
\(dqcopyRangeMethod\(dq: \(dqstandard\(dq,
|
||||
@@ -398,14 +398,14 @@ config, modify the needed parts and post it again.
|
||||
\fBNOTE:\fP
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
Return format changed in versions 0.13.0, 1.19.0 and 1.23.0.
|
||||
Return format changed in versions 0.13.0, 0.14.14, 1.2.0, 1.19.0, 1.23.0 and 1.25.0.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
Returns the list of configured devices and some metadata associated
|
||||
with them. The list also contains the local device itself as not connected.
|
||||
with them.
|
||||
.sp
|
||||
The connection types are \fBTCP (Client)\fP, \fBTCP (Server)\fP, \fBRelay (Client)\fP and \fBRelay (Server)\fP\&.
|
||||
The connection types are \fBtcp\-client\fP, \fBtcp\-server\fP, \fBrelay\-client\fP, \fBrelay\-server\fP, \fBquic\-client\fP and \fBquic\-server\fP\&.
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
@@ -446,7 +446,7 @@ The connection types are \fBTCP (Client)\fP, \fBTCP (Server)\fP, \fBRelay (Clien
|
||||
\(dqoutBytesTotal\(dq: 550,
|
||||
\(dqpaused\(dq: false,
|
||||
\(dqstartedAt\(dq: \(dq2015\-11\-07T00:09:47Z\(dq,
|
||||
\(dqtype\(dq: \(dqTCP (Client)\(dq
|
||||
\(dqtype\(dq: \(dqtcp\-client\(dq
|
||||
}
|
||||
},
|
||||
\(dqtotal\(dq: {
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-SECURITY" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-SECURITY" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.sp
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||
.sp
|
||||
|
||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "SYNCTHING" "1" "Aug 29, 2025" "v2.0.0" "Syncthing"
|
||||
.TH "SYNCTHING" "1" "Sep 06, 2025" "v2.0.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.SH SYNOPSIS
|
||||
|
||||
Reference in New Issue
Block a user