db_open.go 3.2 KB

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