nandi 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +483 -0
- data/Rakefile +8 -0
- data/exe/nandi-enforce +36 -0
- data/lib/generators/nandi/check_constraint/USAGE +19 -0
- data/lib/generators/nandi/check_constraint/check_constraint_generator.rb +52 -0
- data/lib/generators/nandi/check_constraint/templates/add_check_constraint.rb +15 -0
- data/lib/generators/nandi/check_constraint/templates/validate_check_constraint.rb +9 -0
- data/lib/generators/nandi/compile/USAGE +19 -0
- data/lib/generators/nandi/compile/compile_generator.rb +62 -0
- data/lib/generators/nandi/foreign_key/USAGE +47 -0
- data/lib/generators/nandi/foreign_key/foreign_key_generator.rb +91 -0
- data/lib/generators/nandi/foreign_key/templates/add_foreign_key.rb +13 -0
- data/lib/generators/nandi/foreign_key/templates/add_reference.rb +11 -0
- data/lib/generators/nandi/foreign_key/templates/validate_foreign_key.rb +9 -0
- data/lib/generators/nandi/migration/USAGE +9 -0
- data/lib/generators/nandi/migration/migration_generator.rb +24 -0
- data/lib/generators/nandi/migration/templates/migration.rb +13 -0
- data/lib/generators/nandi/not_null_check/USAGE +19 -0
- data/lib/generators/nandi/not_null_check/not_null_check_generator.rb +56 -0
- data/lib/generators/nandi/not_null_check/templates/add_not_null_check.rb +11 -0
- data/lib/generators/nandi/not_null_check/templates/validate_not_null_check.rb +9 -0
- data/lib/nandi.rb +35 -0
- data/lib/nandi/compiled_migration.rb +86 -0
- data/lib/nandi/config.rb +126 -0
- data/lib/nandi/file_diff.rb +32 -0
- data/lib/nandi/file_matcher.rb +72 -0
- data/lib/nandi/formatting.rb +79 -0
- data/lib/nandi/instructions.rb +21 -0
- data/lib/nandi/instructions/add_check_constraint.rb +23 -0
- data/lib/nandi/instructions/add_column.rb +24 -0
- data/lib/nandi/instructions/add_foreign_key.rb +40 -0
- data/lib/nandi/instructions/add_index.rb +50 -0
- data/lib/nandi/instructions/add_reference.rb +23 -0
- data/lib/nandi/instructions/change_column_default.rb +23 -0
- data/lib/nandi/instructions/create_table.rb +83 -0
- data/lib/nandi/instructions/drop_constraint.rb +22 -0
- data/lib/nandi/instructions/drop_table.rb +21 -0
- data/lib/nandi/instructions/irreversible_migration.rb +15 -0
- data/lib/nandi/instructions/remove_column.rb +23 -0
- data/lib/nandi/instructions/remove_index.rb +41 -0
- data/lib/nandi/instructions/remove_not_null_constraint.rb +22 -0
- data/lib/nandi/instructions/remove_reference.rb +23 -0
- data/lib/nandi/instructions/validate_constraint.rb +22 -0
- data/lib/nandi/lockfile.rb +58 -0
- data/lib/nandi/migration.rb +388 -0
- data/lib/nandi/renderers.rb +7 -0
- data/lib/nandi/renderers/active_record.rb +13 -0
- data/lib/nandi/renderers/active_record/generate.rb +59 -0
- data/lib/nandi/renderers/active_record/instructions.rb +146 -0
- data/lib/nandi/safe_migration_enforcer.rb +143 -0
- data/lib/nandi/timeout_policies.rb +38 -0
- data/lib/nandi/timeout_policies/access_exclusive.rb +54 -0
- data/lib/nandi/timeout_policies/concurrent.rb +64 -0
- data/lib/nandi/validation.rb +11 -0
- data/lib/nandi/validation/add_column_validator.rb +43 -0
- data/lib/nandi/validation/add_reference_validator.rb +38 -0
- data/lib/nandi/validation/each_validator.rb +34 -0
- data/lib/nandi/validation/failure_helpers.rb +35 -0
- data/lib/nandi/validation/remove_index_validator.rb +30 -0
- data/lib/nandi/validation/result.rb +30 -0
- data/lib/nandi/validation/timeout_validator.rb +37 -0
- data/lib/nandi/validator.rb +102 -0
- data/lib/templates/nandi/renderers/active_record/generate/show.rb.erb +27 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_check_constraint/show.rb.erb +7 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_column/show.rb.erb +6 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_foreign_key/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_index/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/add_reference/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/change_column_default/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/create_table/show.rb.erb +8 -0
- data/lib/templates/nandi/renderers/active_record/instructions/drop_constraint/show.rb.erb +4 -0
- data/lib/templates/nandi/renderers/active_record/instructions/drop_table/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/irreversible_migration/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_column/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_index/show.rb.erb +4 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_not_null_constraint/show.rb.erb +1 -0
- data/lib/templates/nandi/renderers/active_record/instructions/remove_reference/show.rb.erb +5 -0
- data/lib/templates/nandi/renderers/active_record/instructions/validate_constraint/show.rb.erb +3 -0
- metadata +317 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e9c08faeee8155ae5b0e5357cabca63c2f31bfd65fb464e16991b47c4b9cab75
|
4
|
+
data.tar.gz: 0e408aa01adaf0ce11560acef08d899c208d2c4e3afdd31b3fb9777a0d0b2408
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fe9b90ee4085d5f7d208e481f9ae4fca325f87ab37ff7b3c1a74063adbe65a7777ab66e8afbb8b4d18408362ef1c71f88884eeab6643c19ffd3e8de3f98b007f
|
7
|
+
data.tar.gz: fdbca6280d8c5299661b78fa1163593f903605c056337c3e55b3ca512370717fa7c8af97bfe055393205a6c15fe224aa32a97616dfec8fb118daf39a10b42ca0
|
data/README.md
ADDED
@@ -0,0 +1,483 @@
|
|
1
|
+
# Nandi
|
2
|
+
|
3
|
+
Friendly Postgres migrations for people who don't want to take down their database to add a column!
|
4
|
+
|
5
|
+
## Supported
|
6
|
+
|
7
|
+
- Ruby 2.4 or above
|
8
|
+
- Rails 5.2 or above
|
9
|
+
- Postgres 11 or above
|
10
|
+
|
11
|
+
## What does it do?
|
12
|
+
|
13
|
+
Nandi provides an alternative API to ActiveRecord's built-in Migration DSL for defining changes to your database schema.
|
14
|
+
|
15
|
+
ActiveRecord makes many changes easy. Unfortunately, that includes things that should be done with great care. Consider this migration, for example:
|
16
|
+
|
17
|
+
```rb
|
18
|
+
class AddBarIDToFoos < ActiveRecord::Migration[5.2]
|
19
|
+
def change
|
20
|
+
add_reference :foos, :bars, foreign_key: true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
This is a perfectly ordinary thing to want to do - add a reference from one table to another and add a foreign key constraint, so that `bar_id` will always contain a value that appears in `bars`. But this actually takes very strict locks on both tables, `foos` and `bars`, while it checks that the constraint is valid. Depending on how large a table `bars` is, that could take a while; and if it does, your app will basically grind to a halt if it needs to access these tables. There are many such pitfalls around; and they generally only become dangerous when your database hits a certain size. There is hopefully a grizzled veteran engineer on your team who has memorised all the danger through bitter experience of 3am pages and 10 page post-mortems. But shouldn't we be able to do this with sofware, instead of scar tissue?
|
26
|
+
|
27
|
+
Enter Nandi!
|
28
|
+
|
29
|
+
![nandi](https://user-images.githubusercontent.com/2285130/56881872-bef6f500-6a59-11e9-8936-04d3861b6dce.gif)
|
30
|
+
|
31
|
+
Nandi offers availability-safe implementations of most common schema changes. It produces plain old ActiveRecord migration files, so existing Rails tooling can be leveraged for everything apart from correctness.
|
32
|
+
|
33
|
+
## Getting started
|
34
|
+
|
35
|
+
Add to your Gemfile:
|
36
|
+
|
37
|
+
```rb
|
38
|
+
gem 'nandi'
|
39
|
+
gem 'activerecord-safer_migrations' # Also required
|
40
|
+
```
|
41
|
+
|
42
|
+
Generate a new migration:
|
43
|
+
```sh
|
44
|
+
rails generate nandi:migration add_widgets
|
45
|
+
```
|
46
|
+
|
47
|
+
You'll get a fresh file, by default in `db/safe_migrations`. Let's use it to create a table with two fields, a name and a price, and the standard timestamps:
|
48
|
+
|
49
|
+
```rb
|
50
|
+
# db/safe_migrations/20190606060606_add_widgets.rb
|
51
|
+
|
52
|
+
class AddWidgets < Nandi::Migration
|
53
|
+
def up
|
54
|
+
create_table :widgets do |t|
|
55
|
+
t.text :name
|
56
|
+
t.integer :price
|
57
|
+
|
58
|
+
t.timestamps
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def down
|
63
|
+
drop_table :widgets
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
Looks good! So let's generate an actual runnable ActiveRecord migration file.
|
69
|
+
|
70
|
+
```sh
|
71
|
+
rails generate nandi:compile
|
72
|
+
```
|
73
|
+
|
74
|
+
The result will sort of look like this:
|
75
|
+
|
76
|
+
```rb
|
77
|
+
# db/migrate/20190606060606_add_widgets.rb
|
78
|
+
|
79
|
+
class AddWidgets < ActiveRecord::Migration[5.2]
|
80
|
+
set_lock_timeout(750)
|
81
|
+
set_statement_timeout(1500)
|
82
|
+
|
83
|
+
def up
|
84
|
+
create_table :widgets do |t|
|
85
|
+
t.column :name, :text
|
86
|
+
t.column :price, :integer
|
87
|
+
t.timestamps
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def down
|
92
|
+
drop_table :widgets
|
93
|
+
end
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
(But not quite - the indentation is likely to be skewiff and some syntax will be oddly formatted. We have focused on making sure that the output is correct, rather than readable, although the dream is to one day have the same files that you would write yourself if you knew exactly what you were doing.)
|
98
|
+
|
99
|
+
Now we can run the migration as we normally would.
|
100
|
+
|
101
|
+
```sh
|
102
|
+
rails db:migrate
|
103
|
+
```
|
104
|
+
|
105
|
+
And we're done!
|
106
|
+
|
107
|
+
Now in this case, Nandi hasn't done much for us. It's explicitly set reasonable timeouts, so slow operations won't block other work indefinitely, and that's that. Let's try another.
|
108
|
+
|
109
|
+
```rb
|
110
|
+
# db/safe_migrations/20190606060606_add_widgets_index_on_name_and_price.rb
|
111
|
+
|
112
|
+
class AddWidgetsIndexOnNameAndPrice < Nandi::Migration
|
113
|
+
def up
|
114
|
+
add_index :widgets, [:name, :price]
|
115
|
+
end
|
116
|
+
|
117
|
+
def down
|
118
|
+
remove_index :widgets, [:name, :price]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# db/migrate/20190606060606_add_widgets_index_on_name_and_price.rb
|
123
|
+
|
124
|
+
class AddWidgetsIndexOnNameAndPrice < ActiveRecord::Migration[5.2]
|
125
|
+
set_lock_timeout(750)
|
126
|
+
set_statement_timeout(1500)
|
127
|
+
|
128
|
+
disable_ddl_transaction!
|
129
|
+
def up
|
130
|
+
add_index(
|
131
|
+
:widgets,
|
132
|
+
%i[name price],
|
133
|
+
name: :idx_widgets_on_name_price,
|
134
|
+
algorithm: :concurrently,
|
135
|
+
using: :btree,
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
def down
|
140
|
+
remove_index(
|
141
|
+
:widgets,
|
142
|
+
column: %i[name price],
|
143
|
+
algorithm: :concurrently,
|
144
|
+
)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
Nandi has added in the `algorithm: :concurrently` option, ensuring that the index is not built immediately with the table locked in the meantime (a common source of pain). You can't use that option within a transaction, however, so Nandi uses the `disable_ddl_transaction!` macro. And we're ready to go.
|
150
|
+
|
151
|
+
But wait a minute - what about the foreign key one we started out with? The grizzled veterans among you know the workaround: add the constraint with the `NOT VALID` flag set, and then - in a separate follow-up transaction - validate the constraint. Nandi makes this easy:
|
152
|
+
|
153
|
+
```sh
|
154
|
+
rails generate nandi:foreign_key foos bars
|
155
|
+
```
|
156
|
+
|
157
|
+
We now have three new migration files:
|
158
|
+
|
159
|
+
```rb
|
160
|
+
# db/safe_migrations/20190611124816_add_reference_on_foos_to_bars.rb
|
161
|
+
|
162
|
+
class AddReferenceOnFoosToBars < Nandi::Migration
|
163
|
+
def up
|
164
|
+
add_reference :foos, :bar
|
165
|
+
end
|
166
|
+
|
167
|
+
def down
|
168
|
+
remove_reference :foos, :bar
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# db/safe_migrations/20190611124817_add_foreign_key_on_foos_to_bars.rb
|
173
|
+
|
174
|
+
class AddForeignKeyOnFoosToBars < Nandi::Migration
|
175
|
+
def up
|
176
|
+
add_foreign_key :foos, :bars
|
177
|
+
end
|
178
|
+
|
179
|
+
def down
|
180
|
+
drop_constraint :foos, :foos_bars_fk
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# db/safe_migrations/20190611124818_validate_foreign_key_on_foos_to_bars.rb
|
185
|
+
|
186
|
+
class ValidateForeignKeyOnFoosToBars < Nandi::Migration
|
187
|
+
def up
|
188
|
+
validate_constraint :foos, :foos_bars_fk
|
189
|
+
end
|
190
|
+
|
191
|
+
def down; end
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
Which, when compiled, takes care of things in the right order:
|
196
|
+
|
197
|
+
```rb
|
198
|
+
# db/migrate/20190611124816_add_reference_on_foos_to_bars.rb
|
199
|
+
|
200
|
+
class AddReferenceOnFoosToBars < ActiveRecord::Migration[5.2]
|
201
|
+
set_lock_timeout(5_000)
|
202
|
+
set_statement_timeout(1_500)
|
203
|
+
|
204
|
+
def up
|
205
|
+
add_reference(:foos, :bar)
|
206
|
+
end
|
207
|
+
def down
|
208
|
+
remove_reference(:foos, :bar)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# db/migrate/20190611124817_add_foreign_key_on_foos_to_bars.rb
|
213
|
+
|
214
|
+
class AddForeignKeyOnFoosToBars < ActiveRecord::Migration[5.2]
|
215
|
+
set_lock_timeout(750)
|
216
|
+
set_statement_timeout(1500)
|
217
|
+
|
218
|
+
def up
|
219
|
+
add_foreign_key(
|
220
|
+
:foos,
|
221
|
+
:bars,
|
222
|
+
{ name: :foos_bars_fk, validate: false },
|
223
|
+
)
|
224
|
+
end
|
225
|
+
|
226
|
+
def down
|
227
|
+
execute <<-SQL
|
228
|
+
ALTER TABLE foos DROP CONSTRAINT foos_bars_fk
|
229
|
+
SQL
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# db/migrate/20190611124818_validate_foreign_key_on_foos_to_bars.rb
|
234
|
+
|
235
|
+
# frozen_string_literal: true
|
236
|
+
|
237
|
+
class ValidateForeignKeyOnFoosToBars < ActiveRecord::Migration[5.2]
|
238
|
+
set_lock_timeout(750)
|
239
|
+
set_statement_timeout(1500)
|
240
|
+
|
241
|
+
def up
|
242
|
+
execute <<-SQL
|
243
|
+
ALTER TABLE foos VALIDATE CONSTRAINT foos_bars_fk
|
244
|
+
SQL
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
```
|
249
|
+
|
250
|
+
## Class methods
|
251
|
+
|
252
|
+
### `.set_lock_timeout(timeout)`
|
253
|
+
|
254
|
+
Override the default lock timeout for the duration of the migration. For migrations that require AccessExclusive locks, this is limited to 750ms.
|
255
|
+
|
256
|
+
### `.set_statement_timeout(timeout)`
|
257
|
+
|
258
|
+
Override the default statement timeout for the duration of the migration. For migrations that require AccessExclusive locks, this is limited to 1500ms.
|
259
|
+
|
260
|
+
## Migration methods
|
261
|
+
|
262
|
+
### `#add_column(table, name, type, **kwargs)`
|
263
|
+
Adds a new column. Nandi will explicitly set the column to be NULL, as validating a new NOT NULL constraint can be very expensive on large tables and cause availability issues.
|
264
|
+
|
265
|
+
### `#add_foreign_key(table, target, column: nil, name: nil)`
|
266
|
+
Add a foreign key constraint. The generated SQL will include the NOT VALID parameter, which will prevent immediate validation of the constraint, which locks the target table for writes potentially for a long time. Use the separate #validate_constraint method, in a separate migration; this only takes a row-level lock as it scans through.
|
267
|
+
|
268
|
+
### `#add_index(table, fields, **kwargs)`
|
269
|
+
Adds a new index to the database.
|
270
|
+
|
271
|
+
Nandi will:
|
272
|
+
|
273
|
+
- add the `CONCURRENTLY` option, which means the change takes a less restrictive lock at the cost of not running in a DDL transaction
|
274
|
+
- use the `BTREE` index type which is the safest to create.
|
275
|
+
|
276
|
+
Because index creation is particularly failure-prone, and because we cannot run in a transaction and therefore risk partially applied migrations that (in a Rails environment) require manual intervention, Nandi Validates that, if there is a add_index statement in the migration, it must be the only statement.
|
277
|
+
|
278
|
+
### `#create_table(table) {|columns_reader| ... }`
|
279
|
+
Creates a new table. Yields a ColumnsReader object as a block, to allow adding columns.
|
280
|
+
|
281
|
+
Examples:
|
282
|
+
|
283
|
+
```rb
|
284
|
+
create_table :widgets do |t|
|
285
|
+
t.text :foo, default: true
|
286
|
+
end
|
287
|
+
```
|
288
|
+
|
289
|
+
### `#add_reference(table, ref_name, **extra_args)`
|
290
|
+
Adds a new reference column. Nandi will validate that the foreign key flag is not set to true; use `add_foreign_key` and `validate_foreign_key` instead!
|
291
|
+
|
292
|
+
### `#remove_reference(table, ref_name, **extra_args)`
|
293
|
+
Removes a reference column.
|
294
|
+
|
295
|
+
### `#remove_column(table, name, **extra_args)`
|
296
|
+
Remove an existing column.
|
297
|
+
|
298
|
+
### `#drop_constraint(table, name)`
|
299
|
+
Drops an existing constraint.
|
300
|
+
|
301
|
+
### `#remove_not_null_constraint(table, column)`
|
302
|
+
Drops an existing NOT NULL constraint. Please not that this migration is not safely reversible; to enforce NOT NULL like behaviour, use a CHECK constraint and validate it in a separate migration.
|
303
|
+
|
304
|
+
### `#change_column_default(table, column, value)`
|
305
|
+
Changes the default value for this column when new rows are inserted into the table.
|
306
|
+
|
307
|
+
### `#remove_index(table, target)`
|
308
|
+
Drop an index from the database.
|
309
|
+
|
310
|
+
Nandi will add the `CONCURRENTLY` option, which means the change takes a less restrictive lock at the cost of not running in a DDL transaction.
|
311
|
+
Because we cannot run in a transaction and therefore risk partially applied migrations that (in a Rails environment) require manual intervention, Nandi Validates that, if there is a remove_index statement in the migration, it must be the only statement.
|
312
|
+
|
313
|
+
### `#drop_table(table)`
|
314
|
+
Drops an existing table.
|
315
|
+
|
316
|
+
### `#irreversible_migration`
|
317
|
+
Raises `ActiveRecord::IrreversibleMigration` error.
|
318
|
+
|
319
|
+
## Generators
|
320
|
+
|
321
|
+
Some schema changes need to be split across two migration files. Whenever you want to add a constraint to a column, you'll have to do this to avoid locking the table while Postgres validates that all existing data meets the constraint.
|
322
|
+
|
323
|
+
For some of the most common cases, we provide a Rails generator that generates both files for you.
|
324
|
+
|
325
|
+
### Not-null checks
|
326
|
+
|
327
|
+
To generate migration files for a not-null check, run this command:
|
328
|
+
|
329
|
+
```bash
|
330
|
+
rails generate nandi:not_null_check foos bar
|
331
|
+
```
|
332
|
+
|
333
|
+
This will generate two files:
|
334
|
+
|
335
|
+
```
|
336
|
+
db/safe_migrations/20190424123727_add_not_null_check_on_bar_to_foos.rb
|
337
|
+
db/safe_migrations/20190424123728_validate_not_null_check_on_bar_to_foos.rb
|
338
|
+
```
|
339
|
+
|
340
|
+
From there, you can simply `rails generate nandi:compile` as usual and you're done!
|
341
|
+
|
342
|
+
### Foreign key constraints
|
343
|
+
|
344
|
+
You may have spotted this generator in our worked example above. We've added it to this reference section too for completeness.
|
345
|
+
|
346
|
+
The simplest version of our foreign key migration generator is:
|
347
|
+
|
348
|
+
```
|
349
|
+
rails generate nandi:foreign_key foos bars
|
350
|
+
```
|
351
|
+
|
352
|
+
It will generate three files like these:
|
353
|
+
|
354
|
+
```
|
355
|
+
db/safe_migrations/20190424123726_add_reference_on_foos_to_bars.rb
|
356
|
+
db/safe_migrations/20190424123727_add_foreign_key_on_bars_to_foos.rb
|
357
|
+
db/safe_migrations/20190424123728_validate_foreign_key_on_bars_to_foos.rb
|
358
|
+
```
|
359
|
+
|
360
|
+
If you're adding the constraint to a column that already exists, you can use the `--no-column` flag to skip the first migration:
|
361
|
+
|
362
|
+
```
|
363
|
+
rails generate nandi:foreign_key foos bars --no-column
|
364
|
+
```
|
365
|
+
|
366
|
+
If your foreign key column is named differently, you can override it with the `--column` flag as seen in this example:
|
367
|
+
|
368
|
+
```
|
369
|
+
rails generate nandi:foreign_key foos bar --no-column --column special_bar_ids
|
370
|
+
```
|
371
|
+
|
372
|
+
We generate the name of your foreign key for you. If you want or need to override it (e.g. if it exceeds the max length of a constraint name in Postgres), you can use the `--name` flag:
|
373
|
+
|
374
|
+
|
375
|
+
```
|
376
|
+
rails generate nandi:foreign_key foos bar --name my_fk
|
377
|
+
```
|
378
|
+
|
379
|
+
## Configuration
|
380
|
+
|
381
|
+
Nandi can be configured in various ways, typically in an initializer:
|
382
|
+
|
383
|
+
```rb
|
384
|
+
Nandi.configure do |config|
|
385
|
+
config.lock_timeout = 1_000
|
386
|
+
end
|
387
|
+
```
|
388
|
+
|
389
|
+
The configuration parameters are as follows.
|
390
|
+
|
391
|
+
### `access_exclusive_lock_timeout_limit` (Integer)
|
392
|
+
|
393
|
+
The maximum lock timeout for migrations that take an ACCESS EXCLUSIVE lock and therefore block all reads and writes. Default: 5,000ms.
|
394
|
+
|
395
|
+
### `access_exclusive_statement_timeout_limit` (Integer)
|
396
|
+
|
397
|
+
The maximum statement timeout for migrations that take an ACCESS EXCLUSIVE lock and therefore block all reads and writes. Default: 1,500ms.
|
398
|
+
|
399
|
+
### `concurrent_statement_timeout_limit` (Integer)
|
400
|
+
|
401
|
+
The minimum statement timeout for migrations that take place concurrently. Default: 3,600,000ms (ie, 3 hours).
|
402
|
+
|
403
|
+
### `lock_timeout` (Integer)
|
404
|
+
|
405
|
+
The default lock timeout for migrations. Can be overridden by way of the `set_lock_timeout` class method in a given migration. Default: 5,000ms.
|
406
|
+
|
407
|
+
### `migration_directory` (String)
|
408
|
+
|
409
|
+
The directory for Nandi migrations. Default: `db/safe_migrations`
|
410
|
+
|
411
|
+
### `output_directory` (String)
|
412
|
+
|
413
|
+
The directory for output files. Default: `db/migrate`
|
414
|
+
|
415
|
+
### `renderer` (Class)
|
416
|
+
|
417
|
+
The rendering backend used to produce output. The only supported option at current is `Nandi::Renderers::ActiveRecord`, which produces ActiveRecord migrations.
|
418
|
+
|
419
|
+
### `statement_timeout` (Integer)
|
420
|
+
|
421
|
+
The default statement timeout for migrations that take permissive locks. Can be overridden by way of the `set_statement_timeout` class method in a given migration. Default: 10,800,000ms (ie, 3 hours).
|
422
|
+
|
423
|
+
### `access_exclusive_statement_timeout` (Integer)
|
424
|
+
|
425
|
+
The default statement timeout for migrations that take ACCESS EXCLUSIVE locks. Can be overridden by way of the `set_statement_timeout` class method in a given migration. Default: 1500ms.
|
426
|
+
|
427
|
+
### `compile_files` (String)
|
428
|
+
The files to compile when the compile generator is run. Default: `all`
|
429
|
+
|
430
|
+
May be one of the following:
|
431
|
+
- 'all' compiles all files
|
432
|
+
- 'git-diff' only files changed since last commit
|
433
|
+
- a full or partial version timestamp, eg '20190101010101', '20190101'
|
434
|
+
- a timestamp range , eg '>=20190101010101'
|
435
|
+
|
436
|
+
### `lockfile_directory` (String)
|
437
|
+
The directory where .nandilock.yml will be stored. Default: `db/` in working directory.
|
438
|
+
|
439
|
+
#post_process {|migration| ... }
|
440
|
+
|
441
|
+
Register a block to be called on output, for example a code formatter. Whatever is returned will be written to the output file.
|
442
|
+
|
443
|
+
```rb
|
444
|
+
config.post_process { |migration| MyFormatter.format(migration) }
|
445
|
+
```
|
446
|
+
|
447
|
+
#register_method(name, klass)
|
448
|
+
|
449
|
+
Register a custom DDL method.
|
450
|
+
|
451
|
+
Parameters:
|
452
|
+
|
453
|
+
`name` (Symbol) - The name of the method to create. This will be monkey-patched into Nandi::Migration.
|
454
|
+
|
455
|
+
`klass` (Class) — The class to initialise with the arguments to the method. It should define a `template` instance method which will return a subclass of Cell::ViewModel from the Cells templating library and a `procedure` method that returns the name of the method. It may optionally define a `mixins` method, which will return an array of `Module`s to be mixed into any migration that uses this method.
|
456
|
+
|
457
|
+
## `.nandiignore`
|
458
|
+
|
459
|
+
To protect people from writing unsafe migrations, we provide a script [`nandi-enforce`](https://github.com/gocardless/nandi/blob/master/exe/nandi-enforce) that ensures all migrations in the specified directories are safe migrations generated by Nandi.
|
460
|
+
|
461
|
+
In the off cases where you need to write a migration by hand, add a `.nandiignore` to the root of your repository with your migration files:
|
462
|
+
|
463
|
+
```
|
464
|
+
db/migrate/20190324144824_my_handwritten_migration.rb
|
465
|
+
db/migrate/20190327130801_another_handwritten_migration.rb
|
466
|
+
db/migrate/20190327134957_one_more_handwritten_migration.rb
|
467
|
+
```
|
468
|
+
|
469
|
+
## Why Nandi?
|
470
|
+
|
471
|
+
You may have noticed a GIF of an adorable baby elephant above. This elephant is called Nandi, and she was the star of many an internal presentation slide here at GoCardless. Of course, Postgres is elephant-themed; but it is sometimes an angry elephant, motivating the creation of gems like this one. What better mascot than a harmless, friendly calf?
|
472
|
+
|
473
|
+
## Generate documentation
|
474
|
+
|
475
|
+
```sh
|
476
|
+
bundle exec yard
|
477
|
+
```
|
478
|
+
|
479
|
+
## Run tests
|
480
|
+
|
481
|
+
```sh
|
482
|
+
bundle exec rspec
|
483
|
+
```
|