123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- // 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 (
- "fmt"
- "os"
- "testing"
- "time"
- "github.com/syncthing/syncthing/internal/timeutil"
- "github.com/syncthing/syncthing/lib/config"
- "github.com/syncthing/syncthing/lib/osutil"
- "github.com/syncthing/syncthing/lib/protocol"
- "github.com/syncthing/syncthing/lib/rand"
- )
- var globalFi protocol.FileInfo
- func BenchmarkUpdate(b *testing.B) {
- db, err := Open(b.TempDir())
- if err != nil {
- b.Fatal(err)
- }
- b.Cleanup(func() {
- if err := db.Close(); err != nil {
- b.Fatal(err)
- }
- })
- fs := make([]protocol.FileInfo, 100)
- t0 := time.Now()
- seed := 0
- size := 1000
- const numBlocks = 500
- fdb, err := db.getFolderDB(folderID, true)
- if err != nil {
- b.Fatal(err)
- }
- for size < 200_000 {
- for {
- local, err := db.CountLocal(folderID, protocol.LocalDeviceID)
- if err != nil {
- b.Fatal(err)
- }
- if local.Files >= size {
- break
- }
- fs := make([]protocol.FileInfo, 1000)
- for i := range fs {
- fs[i] = genFile(rand.String(24), numBlocks, 0)
- }
- if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
- b.Fatal(err)
- }
- }
- var files, blocks int
- if err := fdb.sql.QueryRowx(`SELECT count(*) FROM files`).Scan(&files); err != nil {
- b.Fatal(err)
- }
- if err := fdb.sql.QueryRowx(`SELECT count(*) FROM blocks`).Scan(&blocks); err != nil {
- b.Fatal(err)
- }
- d := time.Since(t0)
- b.Logf("t=%s, files=%d, blocks=%d, files/s=%.01f, blocks/s=%.01f", d, files, blocks, float64(files)/d.Seconds(), float64(blocks)/d.Seconds())
- b.Run(fmt.Sprintf("n=Insert100Loc/size=%d", size), func(b *testing.B) {
- for range b.N {
- for i := range fs {
- fs[i] = genFile(rand.String(24), numBlocks, 0)
- }
- if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=RepBlocks100/size=%d", size), func(b *testing.B) {
- for range b.N {
- for i := range fs {
- fs[i].Blocks = genBlocks(fs[i].Name, seed, 64)
- fs[i].Version = fs[i].Version.Update(42)
- }
- seed++
- if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=RepSame100/size=%d", size), func(b *testing.B) {
- for range b.N {
- for i := range fs {
- fs[i].Version = fs[i].Version.Update(42)
- }
- if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=Insert100Rem/size=%d", size), func(b *testing.B) {
- for range b.N {
- for i := range fs {
- fs[i].Blocks = genBlocks(fs[i].Name, seed, 64)
- fs[i].Version = fs[i].Version.Update(42)
- fs[i].Sequence = timeutil.StrictlyMonotonicNanos()
- }
- if err := db.Update(folderID, protocol.DeviceID{42}, fs); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=GetGlobal100/size=%d", size), func(b *testing.B) {
- for range b.N {
- for i := range fs {
- _, ok, err := db.GetGlobalFile(folderID, fs[i].Name)
- if err != nil {
- b.Fatal(err)
- }
- if !ok {
- b.Fatal("should exist")
- }
- }
- }
- b.ReportMetric(float64(b.N)*100.0/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=LocalSequenced/size=%d", size), func(b *testing.B) {
- count := 0
- for range b.N {
- cur, err := db.GetDeviceSequence(folderID, protocol.LocalDeviceID)
- if err != nil {
- b.Fatal(err)
- }
- it, errFn := db.AllLocalFilesBySequence(folderID, protocol.LocalDeviceID, cur-100, 0)
- for f := range it {
- count++
- globalFi = f
- }
- if err := errFn(); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=AllLocalBlocksWithHash/size=%d", size), func(b *testing.B) {
- count := 0
- for range b.N {
- it, errFn := db.AllLocalBlocksWithHash(folderID, globalFi.Blocks[0].Hash)
- for range it {
- count++
- }
- if err := errFn(); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "blocks/s")
- })
- b.Run(fmt.Sprintf("n=GetDeviceSequenceLoc/size=%d", size), func(b *testing.B) {
- for range b.N {
- _, err := db.GetDeviceSequence(folderID, protocol.LocalDeviceID)
- if err != nil {
- b.Fatal(err)
- }
- }
- })
- b.Run(fmt.Sprintf("n=GetDeviceSequenceRem/size=%d", size), func(b *testing.B) {
- for range b.N {
- _, err := db.GetDeviceSequence(folderID, protocol.DeviceID{42})
- if err != nil {
- b.Fatal(err)
- }
- }
- })
- b.Run(fmt.Sprintf("n=RemoteNeed/size=%d", size), func(b *testing.B) {
- count := 0
- for range b.N {
- it, errFn := db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0)
- for f := range it {
- count++
- globalFi = f
- }
- if err := errFn(); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "files/s")
- })
- b.Run(fmt.Sprintf("n=LocalNeed100Largest/size=%d", size), func(b *testing.B) {
- count := 0
- for range b.N {
- it, errFn := db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderLargestFirst, 100, 0)
- for f := range it {
- globalFi = f
- count++
- }
- if err := errFn(); err != nil {
- b.Fatal(err)
- }
- }
- b.ReportMetric(float64(count)/b.Elapsed().Seconds(), "files/s")
- })
- size += 1000
- }
- }
- func TestBenchmarkDropAllRemote(t *testing.T) {
- if testing.Short() || os.Getenv("LONG_TEST") == "" {
- t.Skip("slow test")
- }
- db, err := Open(t.TempDir())
- if err != nil {
- t.Fatal(err)
- }
- t.Cleanup(func() {
- if err := db.Close(); err != nil {
- t.Fatal(err)
- }
- })
- fs := make([]protocol.FileInfo, 1000)
- seq := 0
- for {
- local, err := db.CountLocal(folderID, protocol.LocalDeviceID)
- if err != nil {
- t.Fatal(err)
- }
- if local.Files >= 15_000 {
- break
- }
- for i := range fs {
- seq++
- fs[i] = genFile(rand.String(24), 64, seq)
- }
- if err := db.Update(folderID, protocol.DeviceID{42}, fs); err != nil {
- t.Fatal(err)
- }
- if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
- t.Fatal(err)
- }
- }
- t0 := time.Now()
- if err := db.DropAllFiles(folderID, protocol.DeviceID{42}); err != nil {
- t.Fatal(err)
- }
- d := time.Since(t0)
- t.Log("drop all took", d)
- }
- func TestBenchmarkSizeManyFilesRemotes(t *testing.T) {
- // Reports the database size for a setup with many files and many remote
- // devices each announcing every files, with fairly long file names and
- // "worst case" version vectors.
- if testing.Short() || os.Getenv("LONG_TEST") == "" {
- t.Skip("slow test")
- }
- dir := t.TempDir()
- db, err := Open(dir)
- if err != nil {
- t.Fatal(err)
- }
- t.Cleanup(func() {
- if err := db.Close(); err != nil {
- t.Fatal(err)
- }
- })
- // This is equivalent to about 800 GiB in 100k files (i.e., 8 MiB per
- // file), shared between 31 devices where each have touched every file.
- const numFiles = 1e5
- const numRemotes = 30
- const numBlocks = 64
- const filenameLen = 64
- fs := make([]protocol.FileInfo, 1000)
- n := 0
- seq := 0
- for n < numFiles {
- for i := range fs {
- seq++
- fs[i] = genFile(rand.String(filenameLen), numBlocks, seq)
- for r := range numRemotes {
- fs[i].Version = fs[i].Version.Update(42 + protocol.ShortID(r))
- }
- }
- if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
- t.Fatal(err)
- }
- for r := range numRemotes {
- if err := db.Update(folderID, protocol.DeviceID{byte(42 + r)}, fs); err != nil {
- t.Fatal(err)
- }
- }
- n += len(fs)
- t.Log(n, (numRemotes+1)*n)
- }
- if err := db.Close(); err != nil {
- t.Fatal(err)
- }
- size := osutil.DirSize(dir)
- t.Logf("Total size: %.02f MiB", float64(size)/1024/1024)
- }
|