Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.env
/coverage
db/scopes.db
db/scopes.db-wal
db/scopes.db-shm
spec/examples.txt
12 changes: 11 additions & 1 deletion lib/scopes_extractor/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ def connect
database_path = resolve_database_path
ensure_db_directory(database_path)

ScopesExtractor.db = Sequel.sqlite(database_path)
ScopesExtractor.db = Sequel.sqlite(database_path, timeout: 10_000,
after_connect: method(:configure_concurrency))
ScopesExtractor.logger.info "Connected to database: #{database_path}".green

ScopesExtractor.db
Expand Down Expand Up @@ -50,6 +51,15 @@ def reset

private

# Applied to every connection in Sequel's pool via :after_connect.
# +conn+ is the raw SQLite3::Database driver connection, so use #execute.
# WAL persists in the DB header (one writer, non-blocking readers);
# synchronous = NORMAL is per-connection and must be set on each one.
def configure_concurrency(conn)
conn.execute('PRAGMA journal_mode = WAL')
conn.execute('PRAGMA synchronous = NORMAL')
end

def resolve_database_path
path = Config.database_path
path.start_with?('/') ? path : File.join(ScopesExtractor.root, path)
Expand Down
27 changes: 26 additions & 1 deletion spec/scopes_extractor/database_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
end

after do
FileUtils.rm_f(test_db_path)
ScopesExtractor.db&.disconnect
ScopesExtractor.db = nil
# WAL mode leaves -wal/-shm side-files alongside the main database file.
FileUtils.rm_f([test_db_path, "#{test_db_path}-wal", "#{test_db_path}-shm"])
end

describe '.connect' do
Expand All @@ -31,6 +34,28 @@
expect(ScopesExtractor.logger).to receive(:info).with(/Connected to database/)
described_class.connect
end

it 'enables WAL journal mode for concurrent access' do
db = described_class.connect
expect(db['PRAGMA journal_mode'].first[:journal_mode]).to eq('wal')
end

it 'sets synchronous to NORMAL on every pooled connection' do
db = described_class.connect

# NORMAL = 1, and it is a per-connection pragma, so exercise several
# concurrent connections to ensure after_connect ran on each.
results = Array.new(5).map do
Thread.new { db['PRAGMA synchronous'].first[:synchronous] }.value
end

expect(results).to all(eq(1))
end

it 'raises the busy timeout above the 5s default' do
db = described_class.connect
expect(db['PRAGMA busy_timeout'].first[:timeout]).to eq(10_000)
end
end

describe '.migrate' do
Expand Down
Loading