Skip to content

Commit 0be7e31

Browse files
committed
feat: add Trilogy adapter support for MariaDB
- Add MariaDB service to CI with proper healthcheck - Fix GET_LOCK timeout for MariaDB (use large value instead of -1) - Add Trilogy models, tests, and schema for MariaDB database - Make Trilogy optional for TruffleRuby (trilogy gem not supported) - Configure Zeitwerk to ignore trilogy models when not configured - Add Rails edge to CI matrix (excluded for TruffleRuby) - Update docker-compose with MariaDB container Closes #129
1 parent f7f9aff commit 0be7e31

22 files changed

+377
-39
lines changed

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ jobs:
3636
MYSQL_PASSWORD: with_advisory_pass
3737
MYSQL_DATABASE: with_advisory_lock_test
3838
MYSQL_ROOT_HOST: '%'
39+
mariadb:
40+
image: mariadb:12
41+
ports:
42+
- 3306
43+
env:
44+
MARIADB_ROOT_PASSWORD: root
45+
MARIADB_DATABASE: with_advisory_lock_trilogy_test
46+
MARIADB_USER: with_advisory
47+
MARIADB_PASSWORD: with_advisory_pass
48+
MARIADB_ROOT_HOST: '%'
49+
options: >-
50+
--health-cmd "healthcheck.sh --su-mysql --connect --innodb_initialized"
51+
--health-interval 10s
52+
--health-timeout 5s
53+
--health-retries 5
3954
strategy:
4055
fail-fast: false
4156
matrix:
@@ -48,6 +63,11 @@ jobs:
4863
- 7.2
4964
- "8.0"
5065
- "8.1"
66+
- "edge"
67+
exclude:
68+
# TruffleRuby doesn't support Rails edge yet
69+
- ruby: 'truffleruby'
70+
rails: "edge"
5171
env:
5272
ACTIVERECORD_VERSION: ${{ matrix.rails }}
5373
RAILS_ENV: test
@@ -62,10 +82,14 @@ jobs:
6282
bundler-cache: true
6383
rubygems: latest
6484

85+
6586
- name: Setup test databases
87+
timeout-minutes: 5
6688
env:
6789
DATABASE_URL_PG: postgres://with_advisory:with_advisory_pass@localhost:${{ job.services.postgres.ports[5432] }}/with_advisory_lock_test
6890
DATABASE_URL_MYSQL: mysql2://with_advisory:with_advisory_pass@127.0.0.1:${{ job.services.mysql.ports[3306] }}/with_advisory_lock_test
91+
# Trilogy doesn't support TruffleRuby
92+
DATABASE_URL_TRILOGY: ${{ matrix.ruby != 'truffleruby' && format('trilogy://with_advisory:with_advisory_pass@127.0.0.1:{0}/with_advisory_lock_trilogy_test', job.services.mariadb.ports[3306]) || '' }}
6993
run: |
7094
cd test/dummy
7195
bundle exec rake db:test:prepare
@@ -74,5 +98,7 @@ jobs:
7498
env:
7599
DATABASE_URL_PG: postgres://with_advisory:with_advisory_pass@localhost:${{ job.services.postgres.ports[5432] }}/with_advisory_lock_test
76100
DATABASE_URL_MYSQL: mysql2://with_advisory:with_advisory_pass@127.0.0.1:${{ job.services.mysql.ports[3306] }}/with_advisory_lock_test
101+
# Trilogy doesn't support TruffleRuby
102+
DATABASE_URL_TRILOGY: ${{ matrix.ruby != 'truffleruby' && format('trilogy://with_advisory:with_advisory_pass@127.0.0.1:{0}/with_advisory_lock_trilogy_test', job.services.mariadb.ports[3306]) || '' }}
77103
WITH_ADVISORY_LOCK_PREFIX: ${{ github.run_id }}
78104
run: bin/rails test

Gemfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ gem 'ostruct'
1313

1414
activerecord_version = ENV.fetch('ACTIVERECORD_VERSION', '7.2')
1515

16-
gem 'activerecord', "~> #{activerecord_version}.0"
16+
if activerecord_version == 'edge'
17+
gem 'rails', github: 'rails/rails', branch: 'main'
18+
else
19+
gem 'activerecord', "~> #{activerecord_version}.0"
20+
gem 'railties'
21+
end
1722

1823
gem 'dotenv'
19-
gem 'railties'
2024

2125
platforms :ruby do
2226
gem 'mysql2'

docker-compose.yml

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@ services:
22
pg:
33
image: postgres:17-alpine
44
environment:
5-
POSTGRES_USER: ${DB_USER}
6-
POSTGRES_PASSWORD: ${DB_PASSWORD}
7-
POSTGRES_DB: ${DB_NAME}
5+
POSTGRES_USER: test
6+
POSTGRES_PASSWORD: test
7+
POSTGRES_DB: with_advisory_lock_test
88
ports:
99
- "5433:5432"
1010
mysql:
1111
image: mysql:8
1212
environment:
13-
MYSQL_USER: ${DB_USER}
14-
MYSQL_PASSWORD: ${DB_PASSWORD}
15-
MYSQL_DATABASE: ${DB_NAME}
13+
MYSQL_USER: test
14+
MYSQL_PASSWORD: test
15+
MYSQL_DATABASE: with_advisory_lock_test
1616
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
1717
MYSQL_ROOT_HOST: '%'
1818
ports:
1919
- "3366:3306"
20+
mariadb:
21+
image: mariadb:12
22+
environment:
23+
MARIADB_USER: test
24+
MARIADB_PASSWORD: test
25+
MARIADB_DATABASE: with_advisory_lock_test_trilogy
26+
MARIADB_RANDOM_ROOT_PASSWORD: "yes"
27+
MARIADB_ROOT_HOST: '%'
28+
ports:
29+
- "3368:3306"

lib/with_advisory_lock/mysql_advisory.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ def try_advisory_lock(lock_keys, lock_name:, shared:, transaction:, timeout_seco
1616
# MySQL's GET_LOCK already provides native timeout support, making the blocking
1717
# parameter redundant. MySQL doesn't have separate try/blocking functions like PostgreSQL.
1818

19-
# MySQL GET_LOCK supports native timeout:
20-
# - timeout_seconds = nil: wait indefinitely (-1)
19+
# MySQL/MariaDB GET_LOCK supports native timeout:
20+
# - timeout_seconds = nil: wait indefinitely
2121
# - timeout_seconds = 0: try once, no wait (0)
2222
# - timeout_seconds > 0: wait up to timeout_seconds
23+
#
24+
# Note: MySQL accepts -1 for infinite wait, but MariaDB does not.
25+
# Using a large value (1 year) for cross-compatibility.
2326
mysql_timeout = case timeout_seconds
24-
when nil then -1
27+
when nil then 31_536_000 # 1 year in seconds
2528
when 0 then 0
2629
else timeout_seconds.to_i
2730
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# frozen_string_literal: true
2+
3+
class TrilogyLabel < TrilogyRecord
4+
self.table_name = 'trilogy_labels'
5+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# frozen_string_literal: true
2+
3+
class TrilogyRecord < ActiveRecord::Base
4+
self.abstract_class = true
5+
establish_connection :trilogy
6+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
class TrilogyTag < TrilogyRecord
4+
self.table_name = 'trilogy_tags'
5+
6+
after_save do
7+
TrilogyTagAudit.create(tag_name: name)
8+
TrilogyLabel.create(name: name)
9+
end
10+
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# frozen_string_literal: true
2+
3+
class TrilogyTagAudit < TrilogyRecord
4+
self.table_name = 'trilogy_tag_audits'
5+
end

test/dummy/config/application.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ module TestSystemApp
1414
class Application < Rails::Application
1515
config.load_defaults [Rails::VERSION::MAJOR, Rails::VERSION::MINOR].join('.')
1616
config.eager_load = true
17+
18+
# Ignore trilogy models when DATABASE_URL_TRILOGY is not set (e.g., TruffleRuby)
19+
unless ENV['DATABASE_URL_TRILOGY'] && !ENV['DATABASE_URL_TRILOGY'].empty?
20+
config.autoload_lib(ignore: %w[])
21+
initializer 'ignore_trilogy_models', before: :set_autoload_paths do |app|
22+
trilogy_models = %w[trilogy_record trilogy_tag trilogy_tag_audit trilogy_label]
23+
trilogy_models.each do |model|
24+
Rails.autoloaders.main.ignore(Rails.root.join('app', 'models', "#{model}.rb"))
25+
end
26+
end
27+
end
1728
config.serve_static_files = false
1829
config.public_file_server.enabled = false
1930
config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }

test/dummy/config/database.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,11 @@ test:
1111
url: "<%= ENV['DATABASE_URL_MYSQL'] %>"
1212
properties:
1313
allowPublicKeyRetrieval: true
14+
<% if ENV['DATABASE_URL_TRILOGY'] && !ENV['DATABASE_URL_TRILOGY'].empty? %>
15+
trilogy:
16+
<<: *default
17+
url: "<%= ENV['DATABASE_URL_TRILOGY'] %>"
18+
adapter: trilogy
19+
properties:
20+
allowPublicKeyRetrieval: true
21+
<% end %>

0 commit comments

Comments
 (0)