mirror of
https://github.com/syncthing/syncthing.git
synced 2026-03-25 09:43:06 -04:00
This adds two debugging commands that print information directly from the database; one for folder counts, and one for file metadata for files matching a pattern in a folder. E.g., ``` % syncthing debug database-counts p3jms-73gps DEVICE TYPE FLAGS DELETED COUNT SIZE -local- FILE ------- --- 0 0 -local- FILE --G---- --- 2473 70094796496 -local- DIRECTORY ------- --- 0 0 -local- DIRECTORY --G---- --- 19 2432 PSEUDOP FILE ------- --- 2473 70094796496 PSEUDOP FILE -nG---- --- 0 0 PSEUDOP DIRECTORY ------- --- 19 2432 PSEUDOP DIRECTORY -nG---- --- 0 0 ``` ``` % syncthing debug database-file p3jms-73gps 20240929-DSCF1387 DSCF1387 DEVICE TYPE NAME SEQUENCE DELETED MODIFIED SIZE FLAGS VERSION BLOCKLIST -local- FILE Austin/20240929-DSCF1387.raf 1204 --- 2024-09-29T01:10:54Z 48911888 --G---- HX2ELNU:1744213700 fsQdMvUL PSEUDOP FILE Austin/20240929-DSCF1387.raf 22279 --- 2024-09-29T01:10:54Z 48911888 ------- HX2ELNU:1744213700 fsQdMvUL -local- FILE Austin/20240929-DSCF1387.xmp 1196 --- 2024-10-16T08:08:35.137501751Z 5579 --G---- HX2ELNU:1744213700 xDGMnepi PSEUDOP FILE Austin/20240929-DSCF1387.xmp 19910 --- 2024-10-16T08:08:35.137501751Z 5579 ------- HX2ELNU:1744213700 xDGMnepi ``` The local flag bits get a string representation for the bitmask, ``` FlagLocalUnsupported: "u", FlagLocalIgnored: "i", FlagLocalMustRescan: "r", FlagLocalReceiveOnly: "e", FlagLocalGlobal: "G", FlagLocalNeeded: "n", FlagLocalRemoteInvalid: "v", ```
129 lines
3.9 KiB
Go
129 lines
3.9 KiB
Go
// 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 (
|
|
"github.com/syncthing/syncthing/internal/db"
|
|
"github.com/syncthing/syncthing/lib/protocol"
|
|
)
|
|
|
|
type countsRow struct {
|
|
Type protocol.FileInfoType
|
|
Count int
|
|
Size int64
|
|
Deleted bool
|
|
LocalFlags protocol.FlagLocal `db:"local_flags"`
|
|
}
|
|
|
|
func (s *folderDB) CountLocal(device protocol.DeviceID) (db.Counts, error) {
|
|
var res []countsRow
|
|
if err := s.stmt(`
|
|
SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
|
|
INNER JOIN devices d ON d.idx = s.device_idx
|
|
WHERE d.device_id = ? AND s.local_flags & {{.FlagLocalIgnored}} = 0
|
|
`).Select(&res, device.String()); err != nil {
|
|
return db.Counts{}, wrap(err)
|
|
}
|
|
return summarizeCounts(res), nil
|
|
}
|
|
|
|
func (s *folderDB) CountNeed(device protocol.DeviceID) (db.Counts, error) {
|
|
if device == protocol.LocalDeviceID {
|
|
return s.needSizeLocal()
|
|
}
|
|
return s.needSizeRemote(device)
|
|
}
|
|
|
|
func (s *folderDB) CountGlobal() (db.Counts, error) {
|
|
var res []countsRow
|
|
err := s.stmt(`
|
|
SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
|
|
WHERE s.local_flags & {{.FlagLocalGlobal}} != 0 AND s.local_flags & {{.LocalInvalidFlags}} = 0
|
|
`).Select(&res)
|
|
if err != nil {
|
|
return db.Counts{}, wrap(err)
|
|
}
|
|
return summarizeCounts(res), nil
|
|
}
|
|
|
|
func (s *folderDB) CountReceiveOnlyChanged() (db.Counts, error) {
|
|
var res []countsRow
|
|
err := s.stmt(`
|
|
SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
|
|
WHERE local_flags & {{.FlagLocalReceiveOnly}} != 0
|
|
`).Select(&res)
|
|
if err != nil {
|
|
return db.Counts{}, wrap(err)
|
|
}
|
|
return summarizeCounts(res), nil
|
|
}
|
|
|
|
func (s *folderDB) needSizeLocal() (db.Counts, error) {
|
|
// The need size for the local device is the sum of entries with the
|
|
// need bit set.
|
|
var res []countsRow
|
|
err := s.stmt(`
|
|
SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
|
|
WHERE s.local_flags & {{.FlagLocalNeeded}} != 0
|
|
`).Select(&res)
|
|
if err != nil {
|
|
return db.Counts{}, wrap(err)
|
|
}
|
|
return summarizeCounts(res), nil
|
|
}
|
|
|
|
func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
|
|
var res []countsRow
|
|
// See neededGlobalFilesRemote for commentary as that is the same query without summing
|
|
if err := s.stmt(`
|
|
SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
|
|
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND NOT EXISTS (
|
|
SELECT 1 FROM FILES f
|
|
INNER JOIN devices d ON d.idx = f.device_idx
|
|
WHERE f.name = g.name AND f.version = g.version AND d.device_id = ?
|
|
)
|
|
GROUP BY g.type, g.local_flags, g.deleted
|
|
|
|
UNION ALL
|
|
|
|
SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
|
|
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND EXISTS (
|
|
SELECT 1 FROM FILES f
|
|
INNER JOIN devices d ON d.idx = f.device_idx
|
|
WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
|
|
)
|
|
GROUP BY g.type, g.local_flags, g.deleted
|
|
`).Select(&res, device.String(),
|
|
device.String()); err != nil {
|
|
return db.Counts{}, wrap(err)
|
|
}
|
|
|
|
return summarizeCounts(res), nil
|
|
}
|
|
|
|
func summarizeCounts(res []countsRow) db.Counts {
|
|
c := db.Counts{
|
|
DeviceID: protocol.LocalDeviceID,
|
|
}
|
|
for _, r := range res {
|
|
switch {
|
|
case r.Deleted:
|
|
c.Deleted += r.Count
|
|
case r.Type == protocol.FileInfoTypeFile:
|
|
c.Files += r.Count
|
|
c.Bytes += r.Size
|
|
case r.Type == protocol.FileInfoTypeDirectory:
|
|
c.Directories += r.Count
|
|
c.Bytes += r.Size
|
|
case r.Type == protocol.FileInfoTypeSymlink:
|
|
c.Symlinks += r.Count
|
|
c.Bytes += r.Size
|
|
}
|
|
}
|
|
return c
|
|
}
|