dynamo-autoscale 0.1.4 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,31 @@
1
- DynamoAutoscale.with_config 'logger' do |config|
2
- if config[:sync]
3
- STDOUT.sync = true
4
- STDERR.sync = true
5
- end
1
+ config = DynamoAutoscale.config[:logger] || {}
6
2
 
7
- if config[:log_to]
8
- STDOUT.reopen(config[:log_to])
9
- STDERR.reopen(config[:log_to])
10
- end
11
-
12
- DynamoAutoscale::Logger.logger = ::Logger.new(STDOUT)
3
+ if config[:sync]
4
+ STDOUT.sync = true
5
+ STDERR.sync = true
6
+ end
13
7
 
14
- if ENV['PRETTY_LOG']
15
- DynamoAutoscale::Logger.logger.formatter = DynamoAutoscale::PrettyFormatter.new
16
- else
17
- DynamoAutoscale::Logger.logger.formatter = Logger::Formatter.new
18
- end
8
+ if config[:log_to]
9
+ STDOUT.reopen(config[:log_to])
10
+ STDERR.reopen(config[:log_to])
11
+ end
19
12
 
20
- if ENV['DEBUG']
21
- DynamoAutoscale::Logger.logger.level = ::Logger::DEBUG
22
- elsif config[:level]
23
- DynamoAutoscale::Logger.logger.level = ::Logger.const_get(config[:level])
24
- end
13
+ DynamoAutoscale::Logger.logger = ::Logger.new(STDOUT)
25
14
 
26
- if ENV['SILENT'] # or DynamoAutoscale.env == "test"
27
- DynamoAutoscale::Logger.logger.level = ::Logger::FATAL
28
- end
15
+ if config[:style] == "pretty"
16
+ DynamoAutoscale::Logger.logger.formatter = DynamoAutoscale::PrettyFormatter.new
17
+ else
18
+ DynamoAutoscale::Logger.logger.formatter = Logger::Formatter.new
29
19
  end
30
20
 
31
21
  if ENV['DEBUG']
32
- AWS.config({
33
- logger: DynamoAutoscale::Logger.logger,
34
- })
22
+ DynamoAutoscale::Logger.logger.level = ::Logger::DEBUG
23
+ elsif config[:level]
24
+ DynamoAutoscale::Logger.logger.level = ::Logger.const_get(config[:level])
35
25
  end
26
+
27
+ if ENV['SILENT']
28
+ DynamoAutoscale::Logger.logger.level = ::Logger::FATAL
29
+ end
30
+
31
+ AWS.config(logger: DynamoAutoscale::Logger.logger) if ENV['DEBUG']
@@ -0,0 +1,15 @@
1
+ Signal.trap("USR1") do
2
+ DynamoAutoscale.logger.info "[signal] Caught SIGUSR1. Dumping CSV for all tables in #{Dir.pwd}"
3
+
4
+ DynamoAutoscale.tables.each do |name, table|
5
+ table.to_csv! path: File.join(Dir.pwd, "#{table.name}.csv")
6
+ end
7
+ end
8
+
9
+ Signal.trap("USR2") do
10
+ DynamoAutoscale.logger.info "[signal] Caught SIGUSR2. Dumping graphs for all tables in #{Dir.pwd}"
11
+
12
+ DynamoAutoscale.tables.each do |name, table|
13
+ table.graph! path: File.join(Dir.pwd, "#{table.name}.png")
14
+ end
15
+ end
@@ -28,6 +28,7 @@ Gem::Specification.new do |gem|
28
28
  gem.add_dependency 'ruby-prof'
29
29
  gem.add_dependency 'colored'
30
30
  gem.add_dependency 'activesupport'
31
+ gem.add_dependency 'pony'
31
32
 
32
33
  # ensure the gem is built out of versioned files
33
34
  gem.files = `git ls-files -z`.split("\0")
@@ -0,0 +1,79 @@
1
+ :aws:
2
+ # REQUIRED
3
+ #
4
+ # Here is where you specify your AWS ID and key. It needs to be a pair that
5
+ # can access both CloudWatch and DynamoDB. For exact details on IAM policies,
6
+ # check the project README.
7
+ :access_key_id: "your_id"
8
+ :secret_access_key: "your_key"
9
+
10
+ # REQUIRED
11
+ #
12
+ # At the moment, only the us-east-1 region is supported. Changing this may
13
+ # have strange behvaviour. Don't do it.
14
+ :dynamo_db_endpoint: "dynamodb.us-east-1.amazonaws.com"
15
+
16
+ :logger:
17
+ # Pretty logging makes the log output colourful. Who doesn't like colours?
18
+ :style: pretty
19
+ :level: INFO
20
+
21
+ # If you want to receive email reports whenever a scale operation happens, you
22
+ # can specify this email config. If you don't want to receive emails, just
23
+ # remove this from your config file.
24
+ #
25
+ # dynamo-autoscale uses Pony to send email, this hash is just given to Pony
26
+ # verbatim. Further documentation on what options Pony accepts can be found on
27
+ # their GitHub: https://github.com/benprew/pony
28
+ :email:
29
+ :to: "john.doe@example.com"
30
+ :from: "dynamo-autoscale@example.com"
31
+ :via: :smtp
32
+ :via_options:
33
+ :port: 25
34
+ :enable_starttls_auto: false
35
+ :authentication: :plain
36
+ :address: "mailserver.example.com"
37
+ :user_name: "user"
38
+ :password: "password"
39
+
40
+ # REQUIRED
41
+ #
42
+ # Specify the path to your ruleset. Further information on the syntax and
43
+ # purpose of rulesets can be found in the README.
44
+ :ruleset: "rulesets/gradual_tail.rb"
45
+
46
+ # REQUIRED
47
+ #
48
+ # The following is an array of tables to monitor and autoscale. You need to
49
+ # specify at least one.
50
+ :tables:
51
+ - "products-api-production4_dodgy_product_weights"
52
+
53
+ # If you don't want to scale dynamo just yet, you can say that you want to run
54
+ # in "dry run" mode, which will monitor your production databases but all of the
55
+ # scaling events will only be tracked locally.
56
+ #
57
+ # Very useful for making sure your rules will do the right thing.
58
+ :dry_run: true
59
+
60
+ # Because you are very limited by how many downscales you have per day, and
61
+ # because downscaling both reads and writes at the same time only counts as a
62
+ # single downscale, the following option will queue up downscales until it can
63
+ # apply 1 for reads and 1 for writes at the same time. It is recommended that
64
+ # you turn this one.
65
+ :group_downscales: true
66
+
67
+ # This option only works in conjunction with the above group_downscales option.
68
+ # If a downscale stays queued for a long time, you can specify a timeout and
69
+ # just apply a single read or write downscale after a specified amount of time
70
+ # passes.
71
+ #
72
+ # Specified in seconds.
73
+ :flush_after: 3600
74
+
75
+ # The following two options are configurable minimums and maximums for
76
+ # provisioned throughputs. Dynamo-autoscale will not go below or above whatever
77
+ # you set here.
78
+ :minimum_throughput: 10
79
+ :maximum_throughput: 20000
@@ -1,6 +1,7 @@
1
1
  module DynamoAutoscale
2
2
  class Actioner
3
3
  include DynamoAutoscale::Logger
4
+ attr_accessor :table, :upscales, :downscales
4
5
 
5
6
  def self.minimum_throughput
6
7
  @minimum_throughput ||= 10
@@ -18,8 +19,6 @@ module DynamoAutoscale
18
19
  @maximum_throughput = new_maximum_throughput
19
20
  end
20
21
 
21
- attr_accessor :table, :upscales, :downscales
22
-
23
22
  def initialize table, opts = {}
24
23
  @table = table
25
24
  @downscales = 0
@@ -63,6 +62,13 @@ module DynamoAutoscale
63
62
  @last_scale_check = now
64
63
  end
65
64
 
65
+ # This should be overwritten by deriving classes. In the Dynamo actioner,
66
+ # this should check that the table is in an :active state. In the local
67
+ # actioner this will be faked.
68
+ def can_run?
69
+ false
70
+ end
71
+
66
72
  def upscales
67
73
  check_day_reset!
68
74
  @upscales
@@ -129,6 +135,7 @@ module DynamoAutoscale
129
135
  if result = scale(metric, to)
130
136
  @provisioned[metric][Time.now.utc] = to
131
137
  @upscales += 1
138
+ ScaleReport.new(table).send
132
139
  end
133
140
 
134
141
  return result
@@ -146,9 +153,8 @@ module DynamoAutoscale
146
153
  end
147
154
 
148
155
  if @pending[metric]
149
- previous_pending = @pending[metric].last
150
156
  logger.info "[#{metric}][scaling down] " +
151
- "#{previous_pending} -> #{to.round(2)} (overwritten pending)"
157
+ "#{@pending[metric]} -> #{to.round(2)} (overwritten pending)"
152
158
  else
153
159
  logger.info "[#{metric}][scaling down] " +
154
160
  "#{from ? from.round(2) : "Unknown"} -> #{to.round(2)}"
@@ -158,8 +164,7 @@ module DynamoAutoscale
158
164
  end
159
165
 
160
166
  def queue_operation! metric, value
161
- @pending[metric] = [Time.now.utc, value]
162
-
167
+ @pending[metric] = value
163
168
  try_flush!
164
169
  end
165
170
 
@@ -168,6 +173,8 @@ module DynamoAutoscale
168
173
  if flush_operations!
169
174
  @downscales += 1
170
175
  @last_action = Time.now.utc
176
+ ScaleReport.new(table).send
177
+
171
178
  return true
172
179
  else
173
180
  return false
@@ -179,31 +186,38 @@ module DynamoAutoscale
179
186
 
180
187
  def flush_operations!
181
188
  result = nil
189
+ now = Time.now.utc
182
190
 
183
191
  if @pending[:writes] and @pending[:reads]
184
- _, wvalue = @pending[:writes]
185
- _, rvalue = @pending[:reads]
192
+ wvalue = @pending[:writes]
193
+ rvalue = @pending[:reads]
186
194
 
187
195
  if result = scale_both(rvalue, wvalue)
188
- @provisioned[:writes][Time.now.utc] = wvalue
189
- @provisioned[:reads][Time.now.utc] = rvalue
196
+ @provisioned[:writes][now] = wvalue
197
+ @provisioned[:reads][now] = rvalue
198
+
199
+ table.scale_events[now] = {
200
+ writes: wvalue,
201
+ reads: rvalue,
202
+ }
190
203
 
191
204
  @pending[:writes] = nil
192
205
  @pending[:reads] = nil
193
206
  end
194
207
  elsif @pending[:writes]
195
- time, value = @pending[:writes]
208
+ value = @pending[:writes]
196
209
 
197
210
  if result = scale(:writes, value)
198
211
  @provisioned[:writes][Time.now.utc] = value
199
-
212
+ table.scale_events[now] = { writes: value }
200
213
  @pending[:writes] = nil
201
214
  end
202
215
  elsif @pending[:reads]
203
- time, value = @pending[:reads]
216
+ value = @pending[:reads]
204
217
 
205
218
  if result = scale(:reads, value)
206
219
  @provisioned[:reads][Time.now.utc] = value
220
+ table.scale_events[now] = { reads: value }
207
221
  @pending[:reads] = nil
208
222
  end
209
223
  end
@@ -1,5 +1,6 @@
1
1
  module DynamoAutoscale
2
2
  class CWPoller < Poller
3
+ include DynamoAutoscale::Logger
3
4
  INTERVAL = 1.minute
4
5
 
5
6
  def poll tables, &block
@@ -1,5 +1,7 @@
1
1
  module DynamoAutoscale
2
2
  class Dispatcher
3
+ include DynamoAutoscale::Logger
4
+
3
5
  def initialize
4
6
  @last_check = {}
5
7
  end
@@ -26,8 +28,14 @@ module DynamoAutoscale
26
28
  block.call(table, time, datum) if block
27
29
 
28
30
  if @last_check[table.name].nil? or @last_check[table.name] < time
29
- DynamoAutoscale.rules.test(table)
30
- @last_check[table.name] = time
31
+ if DynamoAutoscale.actioners[table].can_run?
32
+ logger.debug "[dispatcher] Checking rules..."
33
+ DynamoAutoscale.rules.test(table)
34
+ @last_check[table.name] = time
35
+ else
36
+ logger.debug "[dispatcher] Skipped rule check, table is not ready " +
37
+ "to have its throughputs modified."
38
+ end
31
39
  else
32
40
  logger.debug "[dispatcher] Skipped rule check, already checked for " +
33
41
  "a later data point."
@@ -1,5 +1,7 @@
1
1
  module DynamoAutoscale
2
2
  class DynamoActioner < Actioner
3
+ include DynamoAutoscale::Logger
4
+
3
5
  def dynamo
4
6
  @dynamo ||= AWS::DynamoDB.new.tables[table.name]
5
7
  end
@@ -19,14 +21,13 @@ module DynamoAutoscale
19
21
  dynamo_scale(read_capacity_units: reads, write_capacity_units: writes)
20
22
  end
21
23
 
24
+ def can_run?
25
+ dynamo.status == :active
26
+ end
27
+
22
28
  private
23
29
 
24
30
  def dynamo_scale opts
25
- if dynamo.status == :updating
26
- logger.warn "[actioner] Cannot scale throughputs. Table is updating."
27
- return false
28
- end
29
-
30
31
  dynamo.provision_throughput(opts)
31
32
  return true
32
33
  rescue AWS::DynamoDB::Errors::ValidationException => e
@@ -1,14 +1,28 @@
1
1
  module DynamoAutoscale
2
2
  class LocalActioner < Actioner
3
- # Dummy scaling method.
3
+ include DynamoAutoscale::Logger
4
+
4
5
  def scale metric, value
6
+ @updating_until = rand(4.0..7.0).minutes.from_now.utc
5
7
  return true
6
8
  end
7
9
 
8
10
  def scale_both reads, writes
11
+ @updating_until = rand(4.0..7.0).minutes.from_now.utc
9
12
  return true
10
13
  end
11
14
 
15
+ def can_run?
16
+ return true if @updating_until.nil?
17
+
18
+ if Time.now.utc > @updating_until
19
+ @updating_until = nil
20
+ return true
21
+ end
22
+
23
+ return false
24
+ end
25
+
12
26
  # These filters use the arrays inside the local actioner to fake the
13
27
  # provisioned reads and writes when the local data enters the system. It
14
28
  # makes it look like we're actually modifying the provisioned numbers.
@@ -17,7 +31,6 @@ module DynamoAutoscale
17
31
  actioner = DynamoAutoscale.actioners[table]
18
32
 
19
33
  actioner.provisioned_reads.reverse_each do |rtime, reads|
20
- logger.debug "Checking if #{time} > #{rtime}"
21
34
  if time > rtime
22
35
  logger.debug "[filter] Faked provisioned_reads to be #{reads} at #{time}"
23
36
  datum[:provisioned_reads] = reads
@@ -1,5 +1,7 @@
1
1
  module DynamoAutoscale
2
2
  class LocalDataPoll < Poller
3
+ include DynamoAutoscale::Logger
4
+
3
5
  def initialize *args
4
6
  super(*args)
5
7
  @cache = Hash.new { |h, k| h[k] = {} }
@@ -11,5 +11,9 @@ module DynamoAutoscale
11
11
  def logger
12
12
  DynamoAutoscale::Logger.logger
13
13
  end
14
+
15
+ def self.included base
16
+ base.extend DynamoAutoscale::Logger
17
+ end
14
18
  end
15
19
  end
@@ -1,6 +1,6 @@
1
1
  module DynamoAutoscale
2
2
  class Metrics
3
- extend DynamoAutoscale::Logger
3
+ include DynamoAutoscale::Logger
4
4
 
5
5
  DEFAULT_OPTS = {
6
6
  namespace: 'AWS/DynamoDB',
@@ -1,5 +1,8 @@
1
1
  module DynamoAutoscale
2
2
  class Poller
3
+ include DynamoAutoscale::Logger
4
+ attr_accessor :tables, :filters
5
+
3
6
  # The poller constructor accepts a hash of options. The following arguments
4
7
  # are valid but optional:
5
8
  #
@@ -9,11 +12,12 @@ module DynamoAutoscale
9
12
  # each datum before it gets sent to the dispatcher. It helps fake setting
10
13
  # provisioned throughput.
11
14
  def initialize opts = {}
12
- @opts = opts
15
+ @tables = opts[:tables] || []
16
+ @filters = opts[:filters] || []
13
17
  end
14
18
 
15
19
  def run &block
16
- poll(@opts[:tables]) do |table_name, data|
20
+ poll(tables) do |table_name, data|
17
21
  logger.debug "[poller] Got data: #{data}"
18
22
  table = DynamoAutoscale.tables[table_name]
19
23
 
@@ -29,9 +33,7 @@ module DynamoAutoscale
29
33
  consumed_reads: data[:consumed_reads][time],
30
34
  }
31
35
 
32
- if @opts[:filters]
33
- @opts[:filters].each { |filter| filter.call(table, time, datum) }
34
- end
36
+ filters.each { |filter| filter.call(table, time, datum) }
35
37
 
36
38
  DynamoAutoscale.dispatcher.dispatch(table, time, datum, &block)
37
39
  end
@@ -118,6 +118,7 @@ module DynamoAutoscale
118
118
  if @opts[:times].nil? or @count[table.name] == @opts[:times]
119
119
  @count[table.name] = 0
120
120
  logger.info "[rule] Triggered rule: #{self.to_english}"
121
+ table.triggered_rules[Time.now.utc] = self
121
122
 
122
123
  if scale = @opts[:scale]
123
124
  new_val = table.send("last_#{scale[:on]}_for", @metric) * scale[:by]
@@ -1,5 +1,7 @@
1
1
  module DynamoAutoscale
2
2
  class RuleSet
3
+ include DynamoAutoscale::Logger
4
+
3
5
  attr_accessor :rules
4
6
 
5
7
  def initialize path = nil, &block
@@ -23,10 +25,12 @@ module DynamoAutoscale
23
25
  rules = self.for(table.name)
24
26
 
25
27
  rules.select(&:reads?).each do |rule|
28
+ # logger.debug "[rule_set] Checking rule: #{rule.to_english}"
26
29
  break result = true if rule.test(table)
27
30
  end
28
31
 
29
32
  rules.select(&:writes?).each do |rule|
33
+ # logger.debug "[rule_set] Checking rule: #{rule.to_english}"
30
34
  break result = true if rule.test(table)
31
35
  end
32
36
 
@@ -0,0 +1,39 @@
1
+ module DynamoAutoscale
2
+ class ScaleReport
3
+ include DynamoAutoscale::Logger
4
+
5
+ TEMPLATE = File.join(DynamoAutoscale.root, 'templates', 'scale_report_email.erb')
6
+
7
+ def initialize table
8
+ @table = table
9
+ @erb = ERB.new(File.read(TEMPLATE))
10
+
11
+ if config = DynamoAutoscale.config[:email]
12
+ @enabled = true
13
+ Pony.options = config
14
+ else
15
+ @enabled = false
16
+ end
17
+ end
18
+
19
+ def send
20
+ return false unless @enabled
21
+
22
+ result = Pony.mail({
23
+ subject: "Scale event for #{@table.name}",
24
+ body: @erb.result(binding),
25
+ })
26
+
27
+ if result
28
+ logger.info "[mailer] Mail sent successfully."
29
+ result
30
+ else
31
+ logger.error "[mailer] Failed to send email. Result: #{result.inspect}"
32
+ false
33
+ end
34
+ rescue => e
35
+ logger.error "[mailer] Encountered an error: #{e.class}:#{e.message}"
36
+ false
37
+ end
38
+ end
39
+ end
@@ -5,7 +5,7 @@ module DynamoAutoscale
5
5
  # TODO: This time window may need changing.
6
6
  TIME_WINDOW = 7.days
7
7
 
8
- attr_reader :name, :data
8
+ attr_reader :name, :data, :triggered_rules, :scale_events
9
9
 
10
10
  def initialize name
11
11
  @name = name
@@ -13,7 +13,9 @@ module DynamoAutoscale
13
13
  end
14
14
 
15
15
  def clear_data
16
- @data = RBTree.new
16
+ @data = RBTree.new
17
+ @triggered_rules = RBTree.new
18
+ @scale_events = RBTree.new
17
19
  end
18
20
 
19
21
  # `tick` takes two arguments. The first is a Time object, the second is
@@ -48,13 +50,9 @@ module DynamoAutoscale
48
50
  end
49
51
 
50
52
  @data[time] = datum
51
-
52
- # The code below here just makes sure that we're trimming data points that
53
- # are outside of the time window.
54
- logger.debug "[table] Pruning data that may be outside of time window..."
55
- now = Time.now.utc
56
- to_delete = @data.each.take_while { |key, _| key < (now - TIME_WINDOW) }
57
- to_delete.each { |key, _| @data.delete(key) }
53
+ remove_expired_data! @data
54
+ remove_expired_data! @triggered_rules
55
+ remove_expired_data! @scale_events
58
56
  end
59
57
 
60
58
  # Gets the last amount of provisioned throughput for whatever metric you
@@ -281,11 +279,18 @@ module DynamoAutoscale
281
279
 
282
280
  if $? != 0
283
281
  logger.error "[table] Failed to create graph."
282
+ return false
284
283
  else
285
- `open #{png_tmp}` if opts[:open]
284
+ if opts[:open]
285
+ `open #{png_tmp}`
286
+ if $? != 0
287
+ logger.error "[table] Failed to open graph."
288
+ return false
289
+ else
290
+ return png_tmp
291
+ end
292
+ end
286
293
  end
287
-
288
- png_tmp
289
294
  end
290
295
 
291
296
  def scatterplot_for! metric
@@ -314,16 +319,18 @@ module DynamoAutoscale
314
319
  puts " Lost w/units: #{lost_write_units.round(2)} (#{lost_write_percent.round(2)}%)"
315
320
  puts " Upscales: #{DynamoAutoscale.actioners[self].upscales}"
316
321
  puts " Downscales: #{DynamoAutoscale.actioners[self].downscales}"
317
- puts " Fitness: #{fitness}"
318
322
  end
319
323
 
320
- def fitness
321
- lost_weight = 100
322
- wasted_weight = 1
323
- lost = ((lost_read_percent + lost_write_percent) / 2)
324
- wasted = ((wasted_read_percent + wasted_write_percent) / 2)
324
+ private
325
325
 
326
- ((lost * lost_weight) + (wasted * wasted_weight)) / (lost_weight + wasted_weight)
326
+ # Helper function to remove data from an RBTree object keyed on a Time
327
+ # object where the key is outside of the time window defined by the
328
+ # TIME_WINDOW constant.
329
+ def remove_expired_data! data
330
+ # logger.debug "[table] Pruning data that may be outside of time window..."
331
+ now = Time.now.utc
332
+ to_delete = data.each.take_while { |key, _| key < (now - TIME_WINDOW) }
333
+ to_delete.each { |key, _| data.delete(key) }
327
334
  end
328
335
  end
329
336
  end
@@ -1,3 +1,3 @@
1
1
  module DynamoAutoscale
2
- VERSION = '0.1.4'
2
+ VERSION = '0.2'
3
3
  end
data/script/historic_data CHANGED
@@ -1,15 +1,24 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require_relative '../config/environment/common'
4
- require 'pp'
5
- require 'fileutils'
3
+ # This script will fetch the 6 days of previous data from all of the tables that
4
+ # you have specified in the config passed in as ARGV[0].
5
+ #
6
+ # It will store this data into the `data/` directory of this project in a format
7
+ # that the rest of the tool scripts understands.
6
8
 
9
+ require_relative '../config/environment/common'
7
10
  include DynamoAutoscale
8
11
 
9
- tables = ARGV
12
+ if ARGV[0]
13
+ DynamoAutoscale.setup_from_config(ARGV[0])
14
+ elsif ARGV[0].nil?
15
+ STDERR.puts "Usage: script/historic_data path/to/config.yml"
16
+
17
+ exit 1
18
+ elsif ARGV[0] and !File.exists?(ARGV[0])
19
+ STDERR.puts "Usage: script/historic_data path/to/config.yml"
20
+ STDERR.puts "Error: The path you specified is to a file that does not exist."
10
21
 
11
- if tables.empty?
12
- STDERR.puts "Usage: script/historic_data table_name [another_table_name ...]"
13
22
  exit 1
14
23
  end
15
24
 
@@ -18,7 +27,7 @@ range = (Date.today - 5.days).upto(Date.today)
18
27
  logger.info "Date range: #{range.to_a}"
19
28
 
20
29
  # Filter out tables that do not exist in Dynamo.
21
- tables.select! do |table|
30
+ DynamoAutoscale.poller.tables.select! do |table|
22
31
  if dynamo.tables[table].exists?
23
32
  true
24
33
  else
@@ -33,7 +42,7 @@ range.each do |start_day|
33
42
 
34
43
  FileUtils.mkdir(dir) unless Dir.exists?(dir)
35
44
 
36
- tables.each do |table|
45
+ DynamoAutoscale.poller_opts[:tables].each do |table|
37
46
  logger.info "Collecting data for #{table} on #{start_day}..."
38
47
  File.open(File.join(dir, "#{table}.json"), 'w') do |file|
39
48
  file.write(JSON.pretty_generate(Metrics.all_metrics(table, {