mirror of
https://github.com/syncthing/syncthing.git
synced 2025-12-23 22:18:14 -05:00
feat: add debug commands for folder counts and files (#10206)
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", ```
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -376,6 +377,22 @@ func (s *DB) DropDevice(device protocol.DeviceID) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DB) DebugCounts(out io.Writer, folder string) error {
|
||||
fdb, err := s.getFolderDB(folder, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fdb.DebugCounts(out)
|
||||
}
|
||||
|
||||
func (s *DB) DebugFilePattern(out io.Writer, folder, name string) error {
|
||||
fdb, err := s.getFolderDB(folder, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fdb.DebugFilePattern(out, name)
|
||||
}
|
||||
|
||||
// forEachFolder runs the function for each currently open folderDB,
|
||||
// returning the first error that was encountered.
|
||||
func (s *DB) forEachFolder(fn func(fdb *folderDB) error) error {
|
||||
|
||||
@@ -16,7 +16,7 @@ type countsRow struct {
|
||||
Count int
|
||||
Size int64
|
||||
Deleted bool
|
||||
LocalFlags int64 `db:"local_flags"`
|
||||
LocalFlags protocol.FlagLocal `db:"local_flags"`
|
||||
}
|
||||
|
||||
func (s *folderDB) CountLocal(device protocol.DeviceID) (db.Counts, error) {
|
||||
|
||||
@@ -8,9 +8,14 @@ package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/db"
|
||||
"github.com/syncthing/syncthing/internal/itererr"
|
||||
@@ -126,3 +131,82 @@ func (s *folderDB) ListDevicesForFolder() ([]protocol.DeviceID, error) {
|
||||
}
|
||||
return devs, nil
|
||||
}
|
||||
|
||||
func (s *folderDB) DebugCounts(out io.Writer) error {
|
||||
type deviceCountsRow struct {
|
||||
countsRow
|
||||
|
||||
DeviceID string
|
||||
}
|
||||
|
||||
delMap := map[bool]string{
|
||||
true: "del",
|
||||
false: "---",
|
||||
}
|
||||
|
||||
var res []deviceCountsRow
|
||||
if err := s.stmt(`
|
||||
SELECT d.device_id as deviceid, s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
|
||||
INNER JOIN devices d ON d.idx = s.device_idx
|
||||
`).Select(&res); err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(out, 2, 2, 2, ' ', 0)
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\n", "DEVICE", "TYPE", "FLAGS", "DELETED", "COUNT", "SIZE")
|
||||
for _, row := range res {
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%d\t%d\n", shortDevice(row.DeviceID), shortType(row.Type), row.LocalFlags.HumanString(), delMap[row.Deleted], row.Count, row.Size)
|
||||
}
|
||||
return tw.Flush()
|
||||
}
|
||||
|
||||
func (s *folderDB) DebugFilePattern(out io.Writer, name string) error {
|
||||
type hashFileMetadata struct {
|
||||
db.FileMetadata
|
||||
|
||||
Version dbVector
|
||||
BlocklistHash []byte
|
||||
DeviceID string
|
||||
}
|
||||
name = "%" + name + "%"
|
||||
res := itererr.Zip(iterStructs[hashFileMetadata](s.stmt(`
|
||||
SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags, f.version, f.blocklist_hash as blocklisthash, d.device_id as deviceid FROM files f
|
||||
INNER JOIN devices d ON d.idx = f.device_idx
|
||||
WHERE f.name LIKE ?
|
||||
ORDER BY f.name, f.device_idx
|
||||
`).Queryx(name)))
|
||||
|
||||
delMap := map[bool]string{
|
||||
true: "del",
|
||||
false: "---",
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(out, 2, 2, 2, ' ', 0)
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "DEVICE", "TYPE", "NAME", "SEQUENCE", "DELETED", "MODIFIED", "SIZE", "FLAGS", "VERSION", "BLOCKLIST")
|
||||
for row, err := range res {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%d\t%s\t%s\t%d\t%s\t%s\t%s\n", shortDevice(row.DeviceID), shortType(row.Type), row.Name, row.Sequence, delMap[row.Deleted], row.ModTime().UTC().Format(time.RFC3339Nano), row.Size, row.LocalFlags.HumanString(), row.Version.HumanString(), shortHash(row.BlocklistHash))
|
||||
}
|
||||
return tw.Flush()
|
||||
}
|
||||
|
||||
func shortDevice(s string) string {
|
||||
if dev, err := protocol.DeviceIDFromString(s); err == nil && dev == protocol.LocalDeviceID {
|
||||
return "-local-"
|
||||
}
|
||||
short, _, _ := strings.Cut(s, "-")
|
||||
return short
|
||||
}
|
||||
|
||||
func shortType(t protocol.FileInfoType) string {
|
||||
return strings.TrimPrefix(t.String(), "FILE_INFO_TYPE_")
|
||||
}
|
||||
|
||||
func shortHash(bs []byte) string {
|
||||
if len(bs) == 0 {
|
||||
return "-nil-"
|
||||
}
|
||||
return base64.RawStdEncoding.EncodeToString(bs)[:8]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user