jetpants 0.8.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.rdoc +4 -9
  3. data/bin/jetpants +7 -6
  4. data/doc/capacity_plan.rdoc +77 -0
  5. data/doc/commands.rdoc +1 -1
  6. data/doc/jetpants_collins.rdoc +2 -1
  7. data/doc/online_schema_change.rdoc +45 -0
  8. data/doc/plugins.rdoc +7 -1
  9. data/doc/requirements.rdoc +1 -1
  10. data/doc/upgrade_helper.rdoc +68 -0
  11. data/lib/jetpants/db/client.rb +2 -1
  12. data/lib/jetpants/db/import_export.rb +12 -3
  13. data/lib/jetpants/db/replication.rb +6 -2
  14. data/lib/jetpants/db/schema.rb +40 -0
  15. data/lib/jetpants/db/server.rb +2 -2
  16. data/lib/jetpants/host.rb +12 -1
  17. data/lib/jetpants/pool.rb +41 -0
  18. data/lib/jetpants/shard.rb +201 -124
  19. data/lib/jetpants/table.rb +80 -10
  20. data/plugins/capacity_plan/capacity_plan.rb +353 -0
  21. data/plugins/capacity_plan/commandsuite.rb +19 -0
  22. data/plugins/capacity_plan/monkeypatch.rb +20 -0
  23. data/plugins/jetpants_collins/db.rb +45 -6
  24. data/plugins/jetpants_collins/jetpants_collins.rb +32 -21
  25. data/plugins/jetpants_collins/pool.rb +22 -1
  26. data/plugins/jetpants_collins/shard.rb +9 -2
  27. data/plugins/jetpants_collins/topology.rb +8 -9
  28. data/plugins/online_schema_change/commandsuite.rb +56 -0
  29. data/plugins/online_schema_change/db.rb +33 -0
  30. data/plugins/online_schema_change/online_schema_change.rb +5 -0
  31. data/plugins/online_schema_change/pool.rb +105 -0
  32. data/plugins/online_schema_change/topology.rb +56 -0
  33. data/plugins/simple_tracker/shard.rb +1 -1
  34. data/plugins/upgrade_helper/commandsuite.rb +212 -0
  35. data/plugins/upgrade_helper/db.rb +78 -0
  36. data/plugins/upgrade_helper/host.rb +22 -0
  37. data/plugins/upgrade_helper/pool.rb +259 -0
  38. data/plugins/upgrade_helper/shard.rb +61 -0
  39. data/plugins/upgrade_helper/upgrade_helper.rb +21 -0
  40. data/scripts/global_rowcount.rb +75 -0
  41. metadata +28 -15
@@ -0,0 +1,19 @@
1
+ require 'thor'
2
+
3
+ module Jetpants
4
+ class CommandSuite < Thor
5
+
6
+ desc 'capacity_snapshot', 'create a snapshot of the current useage'
7
+ def capacity_snapshot
8
+ Plugin::Capacity.new().snapshot
9
+ end
10
+
11
+ desc 'capacity_plan', 'capacity plan'
12
+ method_option :email, :email => 'email address to send capacity plan report to'
13
+ def capacity_plan
14
+ email = options[:email] || false
15
+ Plugin::Capacity.new().plan(email)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Enumerable
2
+ def sum
3
+ self.inject(0){|accum, i| accum + i }
4
+ end
5
+
6
+ def mean
7
+ self.sum/self.length.to_f
8
+ end
9
+
10
+ def sample_variance
11
+ m = self.mean
12
+ sum = self.inject(0){|accum, i| accum +(i-m)**2 }
13
+ sum/(self.length - 1).to_f
14
+ end
15
+
16
+ def standard_deviation
17
+ return Math.sqrt(self.sample_variance)
18
+ end
19
+
20
+ end
@@ -66,6 +66,11 @@ module Jetpants
66
66
  end
67
67
  end
68
68
 
69
+ # After changing the status of a node, clear its list of spare-node-related
70
+ # validation errors, so that we will re-probe when necessary
71
+ def after_collins_status=(value)
72
+ @spare_validation_errors = nil
73
+ end
69
74
 
70
75
  ##### NEW METHODS ##########################################################
71
76
 
@@ -76,14 +81,48 @@ module Jetpants
76
81
  end
77
82
 
78
83
  # Returns true if this database is a spare node and looks ready for use, false otherwise.
79
- # The default implementation just ensures a collins status of Provisioned.
80
- # Downstream plugins may override this to do additional checks to ensure the node is
81
- # in a sane state. (The caller of this method already checks that the node is SSHable,
82
- # and that MySQL is running, and the node isn't already in a pool -- so no need to
83
- # check any of those here.)
84
+ # Normally no need for plugins to override this (as of Jetpants 0.8.1), they should
85
+ # override DB#validate_spare instead.
84
86
  def usable_spare?
85
- collins_status.downcase == 'provisioned'
87
+ if @spare_validation_errors.nil?
88
+ @spare_validation_errors = []
89
+
90
+ # The order of checks is important -- if the node isn't even reachable by SSH,
91
+ # don't run any of the other checks, for example.
92
+ # Note that we probe concurrently in Topology#query_spare_assets, ahead of time
93
+ if !probed?
94
+ @spare_validation_errors << 'Attempt to probe node failed'
95
+ elsif !available?
96
+ @spare_validation_errors << 'Node is not reachable via SSH'
97
+ elsif !running?
98
+ @spare_validation_errors << 'MySQL is not running'
99
+ elsif pool
100
+ @spare_validation_errors << 'Node already has a pool'
101
+ else
102
+ validate_spare
103
+ end
104
+
105
+ unless @spare_validation_errors.empty?
106
+ error_text = @spare_validation_errors.join '; '
107
+ output "Removed from spare pool for failing checks: #{error_text}"
108
+ end
109
+ end
110
+ @spare_validation_errors.empty?
86
111
  end
87
112
 
113
+ # Performs validation checks on this node to see whether it is a usable spare.
114
+ # The default implementation just ensures a collins status of Allocated and state
115
+ # of SPARE.
116
+ # Downstream plugins may override this to do additional checks to ensure the node is
117
+ # in a sane condition.
118
+ # No need to check whether the node is SSHable, MySQL is running, or not already in
119
+ # a pool -- DB#usable_spare? already does that automatically.
120
+ def validate_spare
121
+ # Confirm node is in Allocated:SPARE status:state. (Because Collins find API hits a
122
+ # search index which isn't synchronously updated with all writes, there's potential
123
+ # for a find call to return assets that just transitioned to a different status or state.)
124
+ status_state = collins_status_state
125
+ @spare_validation_errors << "Unexpected status:state value: #{status_state}" unless status_state == 'allocated:spare'
126
+ end
88
127
  end
89
128
  end
@@ -156,7 +156,7 @@ module Jetpants
156
156
 
157
157
  # Pass in a hash mapping field name symbols to values to set
158
158
  # Symbol => String -- optionally set any Collins attribute
159
- # :status => String -- optionally set the status value for the asset
159
+ # :status => String -- optionally set the status value for the asset. Can optionally be a "status:state" string too.
160
160
  # :asset => Collins::Asset -- optionally pass this in to avoid an extra Collins API lookup, if asset already obtained
161
161
  #
162
162
  # Alternatively, pass in 2 strings (field_name, value) to set just a single Collins attribute (or status)
@@ -180,26 +180,30 @@ module Jetpants
180
180
  output "WARNING: unable to set Collins status to #{val}"
181
181
  next
182
182
  end
183
- if attrs[:state]
184
- previous_state = asset.state.name
185
- previous_status = asset.status
186
- if previous_state != attrs[:state].to_s || previous_status != attrs[:status].to_s
187
- success = Jetpants::Plugin::JetCollins.set_status!(asset, attrs[:status], 'changed through jetpants', attrs[:state])
188
- unless success
189
- Jetpants::Plugin::JetCollins.state_create!(attrs[:state], attrs[:state], attrs[:state], attrs[:status])
190
- success = Jetpants::Plugin::JetCollins.set_status!(asset, attrs[:status], 'changed through jetpants', attrs[:state])
191
- end
192
- raise "#{self}: Unable to set Collins state to #{attrs[:state]} and Unable to set Collins status to #{attrs[:status]}" unless success
193
- output "Collins state changed from #{previous_state} to #{attrs[:state]}"
194
- output "Collins status changed from #{previous_status} to #{attrs[:status]}"
195
- end
196
- else
197
- previous_value = asset.status
198
- if previous_value != val.to_s
199
- success = Jetpants::Plugin::JetCollins.set_status!(asset, val)
200
- raise "#{self}: Unable to set Collins status to #{val}" unless success
201
- output "Collins status changed from #{previous_value} to #{val}"
183
+ state_val = attrs[:state]
184
+ previous_status = asset.status.capitalize
185
+ # Allow setting status:state at once via foo.collins_status = 'allocated:running'
186
+ if val.include? ':'
187
+ raise "Attempting to set state in two places" if state_val
188
+ vals = val.split(':', 2)
189
+ val = vals.first.capitalize
190
+ state_val = vals.last.upcase
191
+ end
192
+ if state_val
193
+ previous_state = asset.state.name.upcase
194
+ next unless previous_state != state_val.to_s.upcase || previous_status != val.to_s.capitalize
195
+ success = Jetpants::Plugin::JetCollins.set_status!(asset, val, 'changed through jetpants', state_val)
196
+ unless success
197
+ # If we failed to set to this state, try creating the state as new
198
+ Jetpants::Plugin::JetCollins.state_create!(state_val, state_val, state_val, val)
199
+ success = Jetpants::Plugin::JetCollins.set_status!(asset, val, 'changed through jetpants', state_val)
202
200
  end
201
+ raise "#{self}: Unable to set Collins state to #{state_val} and Unable to set Collins status to #{val}" unless success
202
+ output "Collins status:state changed from #{previous_status}:#{previous_state} to #{val.capitalize}:#{state_val.upcase}"
203
+ elsif previous_status != val.to_s.capitalize
204
+ success = Jetpants::Plugin::JetCollins.set_status!(asset, val)
205
+ raise "#{self}: Unable to set Collins status to #{val}" unless success
206
+ output "Collins status changed from #{previous_status} to #{val}"
203
207
  end
204
208
  when :state
205
209
  unless asset && asset.status && attrs[:status]
@@ -231,9 +235,16 @@ module Jetpants
231
235
 
232
236
  end
233
237
 
238
+ # Returns a single downcased "status:state" string, useful when trying to compare both fields
239
+ # at once.
240
+ def collins_status_state
241
+ values = collins_get :status, :state
242
+ "#{values[:status]}:#{values[:state]}".downcase
243
+ end
244
+
234
245
  end # module JetCollins
235
246
  end # module Plugin
236
- end
247
+ end # module Jetpants
237
248
 
238
249
 
239
250
  # load all the monkeypatches for other Jetpants classes
@@ -70,7 +70,7 @@ module Jetpants
70
70
  master_read_weight: master_read_weight
71
71
  [@master, slaves].flatten.each do |db|
72
72
  current_status = (db.collins_status || '').downcase
73
- db.collins_status = 'Allocated' unless current_status == 'maintenance'
73
+ db.collins_status = 'Allocated:RUNNING' unless current_status == 'maintenance'
74
74
  db.collins_pool = @name
75
75
  end
76
76
  @master.collins_secondary_role = 'MASTER'
@@ -128,11 +128,32 @@ module Jetpants
128
128
  end
129
129
  end
130
130
  end
131
+
132
+ # Clean up any slaves that are no longer slaving (again only looking at current datacenter)
133
+ assets = Jetpants.topology.server_node_assets(@name, :slave)
134
+ assets.reject! {|a| a.location && a.location.upcase != Plugin::JetCollins.datacenter}
135
+ assets.map(&:to_db).each do |db|
136
+ if !db.running? || db.pool != self
137
+ db.output "Not replicating from new master, removing from pool #{self}"
138
+ db.collins_pool = ''
139
+ db.collins_secondary_role = ''
140
+ db.collins_status = 'Unallocated'
141
+ end
142
+ end
131
143
  end
132
144
 
133
145
 
134
146
  ##### NEW METHODS ##########################################################
135
147
 
148
+ # Returns the pool's creation time (as a unix timestamp) according to Collins.
149
+ # (note: may be off by a few hours until https://github.com/tumblr/collins/issues/80
150
+ # is resolved)
151
+ # Not called from anything in jetpants_collins, but available to your own
152
+ # custom automation if useful
153
+ def collins_creation_timestamp
154
+ collins_asset.created.to_time.to_i
155
+ end
156
+
136
157
  # Called from DB#after_probe_master and DB#after_probe_slave for machines
137
158
  # that are unreachable via SSH, or reachable but MySQL isn't running.
138
159
  def slaves_according_to_collins
@@ -77,13 +77,13 @@ module Jetpants
77
77
  db.collins_secondary_role = ''
78
78
  db.collins_status = 'Unallocated'
79
79
  end
80
- else
80
+ elsif @state != :initializing
81
81
  # Note that we don't call Pool#slaves here to get all 3 types in one shot,
82
82
  # because that potentially includes child shards, and we don't want to overwrite
83
83
  # their pool setting...
84
84
  [@master, active_slaves, standby_slaves, backup_slaves].flatten.each do |db|
85
85
  current_status = (db.collins_status || '').downcase
86
- db.collins_status = 'Allocated' unless current_status == 'maintenance'
86
+ db.collins_status = 'Allocated:RUNNING' unless current_status == 'maintenance'
87
87
  db.collins_pool = @name
88
88
  end
89
89
  @master.collins_secondary_role = 'MASTER'
@@ -91,6 +91,13 @@ module Jetpants
91
91
  standby_slaves.each {|db| db.collins_secondary_role = 'STANDBY_SLAVE'}
92
92
  backup_slaves.each {|db| db.collins_secondary_role = 'BACKUP_SLAVE'}
93
93
  end
94
+
95
+ # handle lockless master migration situations
96
+ if @state == :child && master.master && !@parent
97
+ to_be_ejected_master = master.master
98
+ to_be_ejected_master.collins_secondary_role = :standby_slave # not accurate, but no better option for now
99
+ end
100
+
94
101
  true
95
102
  end
96
103
 
@@ -73,7 +73,7 @@ module Jetpants
73
73
  end
74
74
 
75
75
 
76
- # Returns (count) DB objects. Pulls from machines in the Provisioned status
76
+ # Returns (count) DB objects. Pulls from machines in the spare state
77
77
  # and converts them to the Allocated status.
78
78
  # You can pass in :role to request spares with a particular secondary_role
79
79
  def claim_spares(count, options={})
@@ -84,7 +84,7 @@ module Jetpants
84
84
  db.collins_pool = ''
85
85
  db.collins_secondary_role = ''
86
86
  db.collins_slave_weight = ''
87
- db.collins_status = 'Allocated'
87
+ db.collins_status = 'Allocated:CLAIMED'
88
88
  db
89
89
  end
90
90
  end
@@ -181,6 +181,7 @@ module Jetpants
181
181
 
182
182
  if primary_roles.count == 1
183
183
  selector[:type] = '^CONFIGURATION$'
184
+ selector[:primary_role] = primary_roles.first
184
185
  else
185
186
  values = primary_roles.map {|r| "primary_role = ^#{r}$"}
186
187
  selector[:query] = 'type = ^CONFIGURATION$ AND (' + values.join(' OR ') + ')'
@@ -239,7 +240,7 @@ module Jetpants
239
240
 
240
241
  private
241
242
 
242
- # Helper method to query Collins for spare provisioned DBs.
243
+ # Helper method to query Collins for spare DBs.
243
244
  def query_spare_assets(count, options={})
244
245
  # Intentionally no remoteLookup=true here. We only want to grab spare nodes
245
246
  # from the datacenter that Jetpants is running in.
@@ -247,7 +248,8 @@ module Jetpants
247
248
  operation: 'and',
248
249
  details: true,
249
250
  type: 'SERVER_NODE',
250
- status: 'Provisioned',
251
+ status: 'Allocated',
252
+ state: 'SPARE',
251
253
  primary_role: 'DATABASE',
252
254
  size: 100,
253
255
  }
@@ -257,15 +259,12 @@ module Jetpants
257
259
  keep_nodes = []
258
260
 
259
261
  # Probe concurrently for speed reasons
260
- nodes.map(&:to_db).concurrent_each(&:probe)
262
+ nodes.map(&:to_db).concurrent_each {|db| db.probe rescue nil}
261
263
 
262
264
  # Now iterate in a single-threaded way for simplicity
263
265
  nodes.each do |node|
264
266
  db = node.to_db
265
- db.probe
266
- if node.pool || !db.available? || !db.running? || !db.usable_spare?
267
- db.output "Removed from potential spare pool for failing checks"
268
- else
267
+ if db.usable_spare?
269
268
  keep_nodes << node
270
269
  break if keep_nodes.size >= count
271
270
  end
@@ -0,0 +1,56 @@
1
+ require 'thor'
2
+
3
+ module Jetpants
4
+ class CommandSuite < Thor
5
+
6
+ desc 'alter_table', 'perform an alter table'
7
+ method_option :pool, :desc => 'Name of pool to run the alter table on'
8
+ method_option :dry_run, :desc => 'Dry run of the alter table', :type => :boolean
9
+ method_option :alter, :desc => 'The alter statment (eg ADD COLUMN c1 INT)'
10
+ method_option :database, :desc => 'Database to run the alter table on'
11
+ method_option :table, :desc => 'Table to run the alter table on'
12
+ method_option :all_shards, :desc => 'To run on all the shards', :type => :boolean
13
+ def alter_table
14
+ unless options[:all_shards]
15
+ pool_name = options[:pool] || ask('Please enter a name of a pool: ')
16
+ pool = Jetpants.topology.pool(pool_name)
17
+ raise "#{pool_name} is not a pool name" unless pool
18
+ end
19
+
20
+ database = options[:database] || false
21
+ table = options[:table] || ask('Please enter a name of a table: ')
22
+ alter = options[:alter] || ask('Please enter a alter table statment (eg ADD COLUMN c1 INT): ')
23
+
24
+ if options[:all_shards]
25
+ Jetpants.topology.alter_table_shards(database, table, alter, options[:dry_run])
26
+ else
27
+ unless pool.alter_table(database, table, alter, options[:dry_run])
28
+ print "check for errors during online schema change\n"
29
+ end
30
+ end
31
+ end
32
+
33
+ desc 'alter_table_drop', 'drop the old table after the alter table is complete'
34
+ method_option :pool, :desc => 'Name of pool that your ran the alter table on'
35
+ method_option :table, :desc => 'Table you ran the alter table on'
36
+ method_option :database, :desc => 'Database you ran the alter table on'
37
+ method_option :all_shards, :desc => 'To run on all the shards', :type => :boolean
38
+ def alter_table_drop
39
+ unless options[:all_shards]
40
+ pool_name = options[:pool] || ask('Please enter a name of a pool: ')
41
+ pool = Jetpants.topology.pool(pool_name)
42
+ raise "#{pool_name} is not a pool name" unless pool
43
+ end
44
+
45
+ database = options[:database] || false
46
+ table = options[:table] || ask('Please enter a name of a table: ')
47
+
48
+ if options[:all_shards]
49
+ Jetpants.topology.drop_old_alter_table_shards(database, table)
50
+ else
51
+ pool.drop_old_alter_table(database, table)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ module Jetpants
2
+ class DB
3
+
4
+ # Creates a temporary user for use of pt-table-checksum and pt-upgrade,
5
+ # yields to the supplied block, and then drops the user.
6
+ # The user will have a randomly-generated 50-character password, and will
7
+ # have elevated permissions (ALL PRIVILEGES on the application schema, and
8
+ # a few global privs as well) since these are necessary to run the tools.
9
+ # The block will be passed the randomly-generated password.
10
+ def with_online_schema_change_user(username, database)
11
+ password = DB.random_password
12
+ create_user username, password
13
+ grant_privileges username, '*', 'PROCESS', 'REPLICATION CLIENT', 'REPLICATION SLAVE', 'SUPER'
14
+ grant_privileges username, database, 'ALL PRIVILEGES'
15
+ begin
16
+ yield password
17
+ rescue StandardError, Interrupt, IOError
18
+ drop_user username
19
+ raise
20
+ end
21
+ drop_user username
22
+ end
23
+
24
+ # make sure there is enough space to do an online schema change
25
+ def has_space_for_alter?(table_name, database_name=nil)
26
+ database_name ||= app_schema
27
+ table_size = dir_size("#{mysql_directory}/#{database_name}/#{table_name}.ibd")
28
+
29
+ table_size < mount_stats['available']
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ require 'json'
2
+
3
+
4
+ # load all the monkeypatches for other Jetpants classes
5
+ %w(pool db topology commandsuite).each {|mod| require "online_schema_change/#{mod}"}
@@ -0,0 +1,105 @@
1
+ # JetCollins monkeypatches to add Collins integration
2
+
3
+ module Jetpants
4
+ class Pool
5
+ collins_attr_accessor :online_schema_change
6
+
7
+ def alter_table(database, table, alter, dry_run=true, force=false)
8
+ database ||= app_schema
9
+ error = false
10
+
11
+ raise "not enough space to run alter table on #{table}" unless master.has_space_for_alter?(table, database)
12
+
13
+ unless(check_collins_for_alter)
14
+ raise "alter table already running on #{@name}"
15
+ end
16
+
17
+ max_threads = max_threads_running(30,1)
18
+ max_threads = 50 unless max_threads > 50
19
+
20
+ critical_threads_running = 2 * max_threads > 500 ? 2 * max_threads : 500
21
+
22
+ update_collins_for_alter(database, table, alter)
23
+
24
+ master.with_online_schema_change_user('pt-osc', database) do |password|
25
+
26
+ command = "pt-online-schema-change --nocheck-replication-filters --max-load='Threads_running:#{max_threads}' --critical-load='Threads_running:#{critical_threads_running}' --nodrop-old-table --retries=10 --set-vars='wait_timeout=100000' --dry-run --print --alter '#{alter}' D=#{database},t=#{table},h=#{master.ip},u=#{'pt-osc'},p=#{password}"
27
+
28
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}]---------------------------------------------------------------------------------------\n"
29
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}] #{command}\n"
30
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}]---------------------------------------------------------------------------------------\n"
31
+
32
+ IO.popen command do |io|
33
+ io.each do |line|
34
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}] #{line.gsub("\n","")}\n"
35
+ end
36
+ error = true if $?.to_i > 0
37
+ end
38
+
39
+ if !(dry_run || error)
40
+ continue = 'no'
41
+ unless force
42
+ continue = ask('Dry run complete would you like to continue?: (YES/no)')
43
+ end
44
+
45
+ if force || continue == 'YES'
46
+ command = "pt-online-schema-change --nocheck-replication-filters --max-load='Threads_running:#{max_threads}' --critical-load='Threads_running:#{critical_threads_running}' --nodrop-old-table --retries=10 --set-vars='wait_timeout=100000' --execute --print --alter '#{alter}' D=#{database},t=#{table},h=#{master.ip},u=#{'pt-osc'},p=#{password}"
47
+
48
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}]---------------------------------------------------------------------------------------\n\n\n"
49
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}] #{command}\n"
50
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}]\n\n---------------------------------------------------------------------------------------\n\n\n"
51
+
52
+ IO.popen command do |io|
53
+ io.each do |line|
54
+ print "[#{@name.to_s.red}][#{Time.now.to_s.blue}] #{line.gsub("\n","")}\n"
55
+ end
56
+ error = true if $?.to_i > 0
57
+ end #end execute
58
+
59
+ end #end continue
60
+
61
+ end #end if ! dry run
62
+
63
+ end #end user grant block
64
+
65
+ clean_up_collins_for_alter
66
+
67
+ ! error
68
+ end
69
+
70
+ # update collins for tracking alters, so there is only one running at a time
71
+ def update_collins_for_alter(database, table, alter)
72
+ self.collins_online_schema_change = JSON.pretty_generate({
73
+ 'running' => true,
74
+ 'started' => Time.now.to_i,
75
+ 'database' => database,
76
+ 'table' => table,
77
+ 'alter' => alter
78
+ })
79
+
80
+ end
81
+
82
+ # check if a alter is already running
83
+ def check_collins_for_alter()
84
+ return true if self.collins_online_schema_change.empty?
85
+ meta = JSON.parse(self.collins_online_schema_change)
86
+ if(meta['running'])
87
+ return false
88
+ end
89
+ return true
90
+ end
91
+
92
+ # clean up collins after alter
93
+ def clean_up_collins_for_alter()
94
+ self.collins_online_schema_change = ''
95
+ end
96
+
97
+ # drop old table after an alter, this is because
98
+ # we do not drop the table after an alter
99
+ def drop_old_alter_table(database, table)
100
+ database ||= app_schema
101
+ master.mysql_root_cmd("USE #{database}; DROP TABLE _#{table}_old")
102
+ end
103
+
104
+ end
105
+ end