db_open.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright (C) 2025 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package sqlite
  7. import (
  8. "log/slog"
  9. "os"
  10. "path/filepath"
  11. "sync"
  12. "time"
  13. "github.com/syncthing/syncthing/internal/db"
  14. "github.com/syncthing/syncthing/internal/slogutil"
  15. )
  16. const maxDBConns = 16
  17. type DB struct {
  18. pathBase string
  19. deleteRetention time.Duration
  20. *baseDB
  21. folderDBsMut sync.RWMutex
  22. folderDBs map[string]*folderDB
  23. folderDBOpener func(folder, path string, deleteRetention time.Duration) (*folderDB, error)
  24. }
  25. var _ db.DB = (*DB)(nil)
  26. type Option func(*DB)
  27. func WithDeleteRetention(d time.Duration) Option {
  28. return func(s *DB) {
  29. s.deleteRetention = d
  30. }
  31. }
  32. func Open(path string, opts ...Option) (*DB, error) {
  33. pragmas := []string{
  34. "journal_mode = WAL",
  35. "optimize = 0x10002",
  36. "auto_vacuum = INCREMENTAL",
  37. "default_temp_store = MEMORY",
  38. "temp_store = MEMORY",
  39. }
  40. schemas := []string{
  41. "sql/schema/common/*",
  42. "sql/schema/main/*",
  43. }
  44. migrations := []string{
  45. "sql/migrations/common/*",
  46. "sql/migrations/main/*",
  47. }
  48. _ = os.MkdirAll(path, 0o700)
  49. mainPath := filepath.Join(path, "main.db")
  50. mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, migrations)
  51. if err != nil {
  52. return nil, err
  53. }
  54. db := &DB{
  55. pathBase: path,
  56. baseDB: mainBase,
  57. folderDBs: make(map[string]*folderDB),
  58. folderDBOpener: openFolderDB,
  59. }
  60. for _, opt := range opts {
  61. opt(db)
  62. }
  63. return db, nil
  64. }
  65. // Open the database with options suitable for the migration inserts. This
  66. // is not a safe mode of operation for normal processing, use only for bulk
  67. // inserts with a close afterwards.
  68. func OpenForMigration(path string) (*DB, error) {
  69. pragmas := []string{
  70. "journal_mode = OFF",
  71. "default_temp_store = MEMORY",
  72. "temp_store = MEMORY",
  73. "foreign_keys = 0",
  74. "synchronous = 0",
  75. "locking_mode = EXCLUSIVE",
  76. }
  77. schemas := []string{
  78. "sql/schema/common/*",
  79. "sql/schema/main/*",
  80. }
  81. migrations := []string{
  82. "sql/migrations/common/*",
  83. "sql/migrations/main/*",
  84. }
  85. _ = os.MkdirAll(path, 0o700)
  86. mainPath := filepath.Join(path, "main.db")
  87. mainBase, err := openBase(mainPath, 1, pragmas, schemas, migrations)
  88. if err != nil {
  89. return nil, err
  90. }
  91. db := &DB{
  92. pathBase: path,
  93. baseDB: mainBase,
  94. folderDBs: make(map[string]*folderDB),
  95. folderDBOpener: openFolderDBForMigration,
  96. }
  97. // // Touch device IDs that should always exist and have a low index
  98. // // numbers, and will never change
  99. // db.localDeviceIdx, _ = db.deviceIdxLocked(protocol.LocalDeviceID)
  100. // db.tplInput["LocalDeviceIdx"] = db.localDeviceIdx
  101. return db, nil
  102. }
  103. func OpenTemp() (*DB, error) {
  104. // SQLite has a memory mode, but it works differently with concurrency
  105. // compared to what we need with the WAL mode. So, no memory databases
  106. // for now.
  107. dir, err := os.MkdirTemp("", "syncthing-db")
  108. if err != nil {
  109. return nil, wrap(err)
  110. }
  111. path := filepath.Join(dir, "db")
  112. slog.Debug("Test DB", slogutil.FilePath(path))
  113. return Open(path)
  114. }
  115. func (s *DB) Close() error {
  116. s.folderDBsMut.Lock()
  117. defer s.folderDBsMut.Unlock()
  118. for folder, fdb := range s.folderDBs {
  119. fdb.Close()
  120. delete(s.folderDBs, folder)
  121. }
  122. return wrap(s.baseDB.Close())
  123. }