class ZeroDowntimeMigrations::Validation::AddColumn

Public Instance Methods

validate!() click to toggle source
  # File lib/zero_downtime_migrations/validation/add_column.rb
4 def validate!
5   return if options[:default].nil? # only nil is safe
6   error!(message)
7 end

Private Instance Methods

column() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
71 def column
72   args[1]
73 end
column_default() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
75 def column_default
76   options[:default].inspect
77 end
column_title() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
79 def column_title
80   column.to_s.camelize
81 end
column_type() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
83 def column_type
84   args[2]
85 end
message() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
11       def message
12         <<-MESSAGE.strip_heredoc
13           Adding a column with a default is unsafe!
14 
15           This can take a long time with significant database
16           size or traffic and lock your table!
17 
18           First let’s add the column without a default. When we add
19           a column with a default it has to lock the table while it
20           performs an UPDATE for ALL rows to set this new default.
21 
22             class Add#{column_title}To#{table_title} < ActiveRecord::Migration
23               def change
24                 add_column :#{table}, :#{column}, :#{column_type}
25               end
26             end
27 
28           Then we’ll set the new column default in a separate migration.
29           Note that this does not update any existing data! This only
30           sets the default for newly inserted rows going forward.
31 
32             class AddDefault#{column_title}To#{table_title} < ActiveRecord::Migration
33               def change
34                 change_column_default :#{table}, :#{column}, #{column_default}
35               end
36             end
37 
38           Finally we’ll backport the default value for existing data in
39           batches. This should be done in its own migration as well.
40           Updating in batches allows us to lock 1000 rows at a time
41           (or whatever batch size we prefer).
42 
43             class BackportDefault#{column_title}To#{table_title} < ActiveRecord::Migration
44               def change
45                 #{table_model}.select(:id).find_in_batches.with_index do |records, index|
46                   puts "Processing batch \#{index + 1}\\r"
47                   #{table_model}.where(id: records).update_all(#{column}: #{column_default})
48                 end
49               end
50             end
51 
52           Note that in some cases it may not even be necessary to backport a default value.
53 
54             class #{table_model} < ActiveRecord::Base
55               def #{column}
56                 self["#{column}"] ||= #{column_default}
57               end
58             end
59 
60           If you're 100% positive that this migration is already safe, then wrap the
61           call to `add_column` in a `safety_assured` block.
62 
63             class Add#{column_title}To#{table_title} < ActiveRecord::Migration
64               def change
65                 safety_assured { add_column :#{table}, :#{column}, :#{column_type}, default: #{column_default} }
66               end
67             end
68         MESSAGE
69       end
table() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
87 def table
88   args[0]
89 end
table_model() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
91 def table_model
92   table_title.singularize
93 end
table_title() click to toggle source
   # File lib/zero_downtime_migrations/validation/add_column.rb
95 def table_title
96   table.to_s.camelize
97 end