Skip to content

Commit

Permalink
Merge pull request #49349 from fractaledmind/ar-sqlite-tune-connectio…
Browse files Browse the repository at this point in the history
…n-config

Performance tune the SQLite3 adapter connection configuration
  • Loading branch information
guilleiguaran committed Sep 24, 2023
2 parents 7c07787 + c6d7ffc commit d8391b2
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
6 changes: 6 additions & 0 deletions activerecord/CHANGELOG.md
@@ -1,3 +1,9 @@
* Performance tune the SQLite3 adapter connection configuration

For Rails applications, the Write-Ahead-Log in normal syncing mode with a capped journal size, a healthy shared memory buffer and a shared cache will perform, on average, 2× better.

*Stephen Margheim*

* Allow SQLite3 `busy_handler` to be configured with simple max number of `retries`

Retrying busy connections without delay is a preferred practice for performance-sensitive applications. Add support for a `database.yml` `retries` integer, which is used in a simple `busy_handler` function to retry busy connections without exponential backoff up to the max number of `retries`.
Expand Down
Expand Up @@ -723,7 +723,29 @@ def configure_connection
end
end

# Enforce foreign key constraints
# https://www.sqlite.org/pragma.html#pragma_foreign_keys
# https://www.sqlite.org/foreignkeys.html
raw_execute("PRAGMA foreign_keys = ON", "SCHEMA")
unless @memory_database
# Journal mode WAL allows for greater concurrency (many readers + one writer)
# https://www.sqlite.org/pragma.html#pragma_journal_mode
raw_execute("PRAGMA journal_mode = WAL", "SCHEMA")
# Set more relaxed level of database durability
# 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
# https://www.sqlite.org/pragma.html#pragma_synchronous
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
# Set the global memory map so all processes can share some data
# https://www.sqlite.org/pragma.html#pragma_mmap_size
# https://www.sqlite.org/mmap.html
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
end
# Impose a limit on the WAL file to prevent unlimited growth
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
# Set the local connection cache to 2000 pages
# https://www.sqlite.org/pragma.html#pragma_cache_size
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
end
end
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
Expand Down
31 changes: 31 additions & 0 deletions activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
Expand Up @@ -136,6 +136,26 @@ def test_encoding
assert_equal "UTF-8", @conn.encoding
end

def test_default_pragmas
if in_memory_db?
assert_equal [{ "foreign_keys" => 1 }], @conn.execute("PRAGMA foreign_keys")
assert_equal [{ "journal_mode" => "memory" }], @conn.execute("PRAGMA journal_mode")
assert_equal [{ "synchronous" => 2 }], @conn.execute("PRAGMA synchronous")
assert_equal [{ "journal_size_limit" => 67108864 }], @conn.execute("PRAGMA journal_size_limit")
assert_equal [], @conn.execute("PRAGMA mmap_size")
assert_equal [{ "cache_size" => 2000 }], @conn.execute("PRAGMA cache_size")
else
with_file_connection do |conn|
assert_equal [{ "foreign_keys" => 1 }], conn.execute("PRAGMA foreign_keys")
assert_equal [{ "journal_mode" => "wal" }], conn.execute("PRAGMA journal_mode")
assert_equal [{ "synchronous" => 1 }], conn.execute("PRAGMA synchronous")
assert_equal [{ "journal_size_limit" => 67108864 }], conn.execute("PRAGMA journal_size_limit")
assert_equal [{ "mmap_size" => 134217728 }], conn.execute("PRAGMA mmap_size")
assert_equal [{ "cache_size" => 2000 }], conn.execute("PRAGMA cache_size")
end
end
end

def test_exec_no_binds
with_example_table "id int, data string" do
result = @conn.exec_query("SELECT id, data FROM ex")
Expand Down Expand Up @@ -815,6 +835,17 @@ def with_strict_strings_by_default
ensure
SQLite3Adapter.strict_strings_by_default = false
end

def with_file_connection(options = {})
options = options.dup
db_config = ActiveRecord::Base.configurations.configurations.find { |config| !config.database.include?(":memory:") }
options[:database] ||= db_config.database
conn = ActiveRecord::Base.sqlite3_connection(options)

yield(conn)
ensure
conn.disconnect! if conn
end
end
end
end

0 comments on commit d8391b2

Please sign in to comment.