Add per-table concurrent timeout overrides - #160
Conversation
…errides Lets apps configure a table_overrides hash on register_database so a specific table (e.g. a huge payments table) can have a longer concurrent statement/lock timeout than the database-wide default, without loosening it for every table. Falls back to the database-level value for any table (or key) not listed; per-migration set_lock_timeout/set_statement_timeout still takes precedence. Bumps version to 3.2.0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
table and mixins were each concatenating up_instructions + down_instructions inline; pull that into a shared private helper instead.
| def normalize_table_overrides(table_overrides) | ||
| (table_overrides || {}).transform_keys(&:to_sym) | ||
| end |
There was a problem hiding this comment.
config doesn't allow non-sym keys for other params, why would we want to be so forgiving in this case?
There was a problem hiding this comment.
IMO the table_overrides config is a special because the keys are table names. Nandi already handles table names as strings or symbols in DB instructions (e.g. add_index :table_name and add_index "table_name").
It's conceivable that someone might assume that this config behaves the same way. By silently ignoring strings we would use the default timeout.
There was a problem hiding this comment.
Okay, then we could also ensure that string table-name inputs are supported by using @table_overrides = (config[:table_overrides] || {}).with_indifferent_access, so we don't silently ignore if someone accesses config using a string table name.
There was a problem hiding this comment.
This would cause the inner hash to be tolerant of string keys too. So table_overrides: { payments: { "concurrent_statement_timeout" => 600_000 } } would be supported.
In this case I just want to handle table names as strings rather than changing the behaviour of how inner options can be configured.
There was a problem hiding this comment.
with_indifferent_access is not deep by default
There was a problem hiding this comment.
It is deep by default. I tested this in a rails console
{ payments: { "inner_key" => "ok" } }.with_indifferent_access[:payments][:inner_key]
=> "ok"
There was a problem hiding this comment.
That's surprising. I expected it to operate on a single level like the other hash helpers. Good to know :)
| end | ||
| end | ||
|
|
||
| context "when a table_overrides entry exists for the requested table" do |
There was a problem hiding this comment.
Shouldn't these specs live in the main migration spec file?
There was a problem hiding this comment.
These tests are testing different things in different environments.
The migration specs are effectively an integration test. The specs that were added checks that the table overrides work with the rest of the migration logic.
The multi_database specs are testing the actual override timeout calculation logic in isolation.
There was a problem hiding this comment.
Ok, I was confused that there’s logic and specs specifically for the “multi-database” case, but nothing for the “single-database” use case. Then I realised that the multi-database code superseded the single-database setup, so everything in Nandi is now multi-database by default.
Summary
table_overridesconfig hash toregister_database, letting apps overrideconcurrent_lock_timeout/concurrent_statement_timeoutfor specific tables (e.g. a hugepaymentstable needing a longer statement timeout than the database-wide default).set_lock_timeout/set_statement_timeoutstill takes precedence over both.table_overridesdefaults to{}, no behaviour changes for existing configs.Bumps version to 3.2.0.
Test plan
bundle exec rspec— 447 examples, 0 failuresbundle exec rubocop— 0 offensesMultiDatabase::Database,Config, andMigrationcovering: override present/absent, partial-key overrides, string/symbol table names, per-migration override precedence, and multi-databasedatabase_name+ table resolution together🤖 Generated with Claude Code