Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Balkan Ruby 2024 — How and why to run SQLite on...

Avatar for Stephen Stephen
April 27, 2024

Balkan Ruby 2024 — How and why to run SQLite on Rails in production

Learn how to run a Rails application in production with SQLite, covering performance, resilience, enhancements, and deployment.

Avatar for Stephen

Stephen

April 27, 2024
Tweet

More Decks by Stephen

Other Decks in Programming

Transcript

  1. Stephen Margheim · 27 April 2024 SQLite on Rails How

    (and why) to run SQLite in production Balkan Ruby
  2. Who am I? • Head of Engineering at Test IO

    • Open source developer rails/rails sparklemotion/sqlite3-ruby oldmoe/litestack joeldrapper/phlex joeldrapper/literal fractaledmind/activerecord-enhancedsqlite3-adapter fractaledmind/litestream-ruby fractaledmind/acidic_job • Writer: https://fractaledmind.github.io Stephen Margheim · @fractaledmind 🇺🇸 🛫 🇩🇪 💒 🇩🇰 📍 🇯🇲 💕 🐶🐕
  3. 1. Write-Ahead-Logging journal mode 2. IMMEDIATE transactions 3. non-GVL-blocking timeout

    mechanism 4. fair retry interval 5. isolated connection pools for reading and writing
  4. # config/litestream.yml dbs: - path: storage/production.sqlite3 replicas: - type: s3

    bucket: $LITESTREAM_REPLICA_BUCKET path: storage/production.sqlite3 access-key-id: $LITESTREAM_ACCESS_KEY_ID secret-access-key: $LITESTREAM_SECRET_ACCESS_KEY
  5. # config/initializers/litestream.rb Litestream.configure do |config| creds = Rails.application.credentials.litestream config.replica_bucket =

    creds.replica_bucket config.replica_key_id = creds.replica_key_id config.replica_access_key = creds.replica_access_key end
  6. • deferred foreign keys, • generated columns, • returning values

    from inserts, • PRAGMA tuning, • extension loading, and • improved concurrency support $ bundle add activerecord-enhancedsqlite3-adapter
  7. create_table :virtual_columns, force: true do |t| t.string :name t.virtual :upper_name,

    type: :string, as: "UPPER(name)", stored: true t.virtual :lower_name, type: :string, as: "LOWER(name)", stored: false t.virtual :octet_name, type: :integer, as: "LENGTH(name)" end
  8. $ bundle add sqlite-vss # /config/database.yml default: &default adapter: sqlite3

    pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000 extensions: - sqlite_vss
  9. • Active Record (e.g. PostgreSQL, MySQL) • Solid Cache (e.g.

    Redis, Memcached) • Solid Queue (e.g. Sidekiq, Goodjob) • Solid Cable* (e.g. Redis, PostgreSQL)
  10. $ bundle config set build.sqlite3 \ "--with-sqlite-cflags=' -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0

    -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_USE_ALLOCA'"
  11. # /config/database.yml development: <<: *default database: storage/<%= `git branch --show-current`.chomp

    || 'development' %>.sqlite3 # /config/environments/development.rb config.after_initialize do ActiveRecord::Tasks::DatabaseTasks.prepare_all end