Skip to content

Commit dd33679

Browse files
committed
Use separate SQLite connection pools for reading and writing.
By only allowing a single write connection, we can leverage database/sql's auto-blocking behavior to avoid SQLITE_BUSY errors on multiple write requests.
1 parent ae399f7 commit dd33679

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

database.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,16 @@ func initializeDatabase(path string) (*sql.DB, error) {
8686
accessible = true
8787
}
8888

89-
db, err := sql.Open("sqlite", path)
90-
if err != nil {
91-
return nil, fmt.Errorf("failed to create SQLite file at %q; %w", path, err)
92-
}
89+
// This object's connections are intended for serial writing, so we set
90+
// IMMEDIATE transactions to make debugging of locking issues easier.
91+
db, err := sql.Open("sqlite", path + "?_txlock=immediate")
92+
if err != nil {
93+
return nil, fmt.Errorf("failed to open read/write SQLite handle; %w", err)
94+
}
95+
96+
// Maxing out at one connection so that there can only be one write at any
97+
// time; everyone else will have to block on the connection availability.
98+
db.SetMaxOpenConns(1)
9399

94100
// Make sure to eventually close all idle connections in the pool so that
95101
// SQLite actually commits its WAL journal.
@@ -165,6 +171,19 @@ CREATE INDEX index_links ON links(tid, fid);
165171
return db, nil
166172
}
167173

174+
func initializeReadOnlyDatabase(path string) (*sql.DB, error) {
175+
ro_db, err := sql.Open("sqlite", path + "?_pragma=query_only")
176+
if err != nil {
177+
return nil, fmt.Errorf("failed to open read-only SQLite handle; %w", err)
178+
}
179+
180+
// Make sure to eventually close all idle connections in the pool so that
181+
// SQLite actually commits its WAL journal.
182+
ro_db.SetConnMaxIdleTime(1 * time.Minute)
183+
184+
return ro_db, nil
185+
}
186+
168187
/**********************************************************************/
169188

170189
// Pre-building the insertion statements for efficiency when iterating over and

main.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ func main() {
2323

2424
db, err := initializeDatabase(dbpath)
2525
if err != nil {
26-
log.Fatalf("failed to create the initial SQLite file at %q; %v", dbpath, err)
26+
log.Fatalf("failed to initialize SQLite file at %q; %v", dbpath, err)
2727
}
2828
defer db.Close()
2929

30+
ro_db, err := initializeReadOnlyDatabase(dbpath)
31+
if err != nil {
32+
log.Fatalf("failed to create read-only connections to %q; %w", dbpath, err)
33+
}
34+
defer ro_db.Close()
35+
3036
tokenizer, err := newUnicodeTokenizer(false)
3137
if err != nil {
3238
log.Fatalf("failed to create the default tokenizer; %v", err)
@@ -49,14 +55,14 @@ func main() {
4955
// Setting up the endpoints.
5056
http.HandleFunc("POST " + prefix + "/register/start", newRegisterStartHandler(verifier))
5157
http.HandleFunc("POST " + prefix + "/register/finish", newRegisterFinishHandler(db, verifier, tokenizer, timeout))
52-
http.HandleFunc("POST " + prefix + "/deregister/start", newDeregisterStartHandler(db, verifier))
58+
http.HandleFunc("POST " + prefix + "/deregister/start", newDeregisterStartHandler(ro_db, verifier))
5359
http.HandleFunc("POST " + prefix + "/deregister/finish", newDeregisterFinishHandler(db, verifier, timeout))
5460

55-
http.HandleFunc(prefix + "/registered", newListRegisteredDirectoriesHandler(db))
56-
http.HandleFunc(prefix + "/query", newQueryHandler(db, tokenizer, wild_tokenizer, "/query"))
57-
http.HandleFunc(prefix + "/retrieve/metadata", newRetrieveMetadataHandler(db))
58-
http.HandleFunc(prefix + "/retrieve/file", newRetrieveFileHandler(db))
59-
http.HandleFunc(prefix + "/list", newListFilesHandler(db))
61+
http.HandleFunc(prefix + "/registered", newListRegisteredDirectoriesHandler(ro_db))
62+
http.HandleFunc(prefix + "/query", newQueryHandler(ro_db, tokenizer, wild_tokenizer, "/query"))
63+
http.HandleFunc(prefix + "/retrieve/metadata", newRetrieveMetadataHandler(ro_db))
64+
http.HandleFunc(prefix + "/retrieve/file", newRetrieveFileHandler(ro_db))
65+
http.HandleFunc(prefix + "/list", newListFilesHandler(ro_db))
6066

6167
http.HandleFunc(prefix + "/", newDefaultHandler())
6268

0 commit comments

Comments
 (0)