dynamo-autoscale 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +58 -0
  4. data/LICENSE +21 -0
  5. data/README.md +400 -0
  6. data/Rakefile +9 -0
  7. data/aws.sample.yml +16 -0
  8. data/bin/dynamo-autoscale +131 -0
  9. data/config/environment/common.rb +114 -0
  10. data/config/environment/console.rb +2 -0
  11. data/config/environment/test.rb +3 -0
  12. data/config/logger.yml +11 -0
  13. data/config/services/aws.rb +20 -0
  14. data/config/services/logger.rb +35 -0
  15. data/data/.gitkeep +0 -0
  16. data/dynamo-autoscale.gemspec +29 -0
  17. data/lib/dynamo-autoscale/actioner.rb +265 -0
  18. data/lib/dynamo-autoscale/cw_poller.rb +49 -0
  19. data/lib/dynamo-autoscale/dispatcher.rb +39 -0
  20. data/lib/dynamo-autoscale/dynamo_actioner.rb +59 -0
  21. data/lib/dynamo-autoscale/ext/active_support/duration.rb +7 -0
  22. data/lib/dynamo-autoscale/local_actioner.rb +39 -0
  23. data/lib/dynamo-autoscale/local_data_poll.rb +51 -0
  24. data/lib/dynamo-autoscale/logger.rb +15 -0
  25. data/lib/dynamo-autoscale/metrics.rb +192 -0
  26. data/lib/dynamo-autoscale/poller.rb +41 -0
  27. data/lib/dynamo-autoscale/pretty_formatter.rb +27 -0
  28. data/lib/dynamo-autoscale/rule.rb +180 -0
  29. data/lib/dynamo-autoscale/rule_set.rb +69 -0
  30. data/lib/dynamo-autoscale/table_tracker.rb +329 -0
  31. data/lib/dynamo-autoscale/unit_cost.rb +41 -0
  32. data/lib/dynamo-autoscale/version.rb +3 -0
  33. data/lib/dynamo-autoscale.rb +1 -0
  34. data/rlib/dynamodb_graph.r +15 -0
  35. data/rlib/dynamodb_scatterplot.r +13 -0
  36. data/rulesets/default.rb +5 -0
  37. data/rulesets/erroneous.rb +1 -0
  38. data/rulesets/gradual_tail.rb +11 -0
  39. data/rulesets/none.rb +0 -0
  40. data/script/console +3 -0
  41. data/script/historic_data +46 -0
  42. data/script/hourly_wastage +40 -0
  43. data/script/monitor +55 -0
  44. data/script/simulator +40 -0
  45. data/script/test +52 -0
  46. data/script/validate_ruleset +20 -0
  47. data/spec/actioner_spec.rb +244 -0
  48. data/spec/rule_set_spec.rb +89 -0
  49. data/spec/rule_spec.rb +491 -0
  50. data/spec/spec_helper.rb +4 -0
  51. data/spec/table_tracker_spec.rb +256 -0
  52. metadata +178 -0
@@ -0,0 +1,256 @@
1
+ require 'spec_helper'
2
+
3
+ describe DynamoAutoscale::TableTracker do
4
+ let(:table_name) { "test_table" }
5
+ let(:table) { DynamoAutoscale::TableTracker.new(table_name) }
6
+ subject { table }
7
+
8
+ before do
9
+ table.tick(5.seconds.ago, {
10
+ provisioned_reads: 600.0,
11
+ provisioned_writes: 800.0,
12
+ consumed_reads: 20.0,
13
+ consumed_writes: 30.0,
14
+ })
15
+
16
+ table.tick(5.minutes.ago, {
17
+ provisioned_reads: 600.0,
18
+ provisioned_writes: 800.0,
19
+ consumed_reads: 20.0,
20
+ consumed_writes: 30.0,
21
+ })
22
+
23
+ table.tick(15.seconds.ago, {
24
+ provisioned_reads: 600.0,
25
+ provisioned_writes: 800.0,
26
+ consumed_reads: 20.0,
27
+ consumed_writes: 30.0,
28
+ })
29
+ end
30
+
31
+ describe 'storing data' do
32
+ specify "should be done in order" do
33
+ table.data.keys.should == table.data.keys.sort
34
+ end
35
+ end
36
+
37
+ describe 'retrieving data' do
38
+ let(:now) { Time.now }
39
+
40
+ before do
41
+ table.tick(now, {
42
+ provisioned_reads: 100.0,
43
+ provisioned_writes: 200.0,
44
+ consumed_reads: 20.0,
45
+ consumed_writes: 30.0,
46
+ })
47
+ end
48
+
49
+ describe "#name" do
50
+ subject { table.name }
51
+ it { should == table_name }
52
+ end
53
+
54
+ describe "#last 3.seconds, :consumed_reads" do
55
+ subject { table.last 3.seconds, :consumed_reads }
56
+ it { should == [20.0] }
57
+ end
58
+
59
+ describe "#last 1, :consumed_writes" do
60
+ subject { table.last 1, :consumed_writes }
61
+ it { should == [30.0] }
62
+ end
63
+
64
+ describe "#last_provisioned_for :reads" do
65
+ subject { table.last_provisioned_for :reads }
66
+ it { should == 100.0 }
67
+ end
68
+
69
+ describe "#last_provisioned_for :writes, at: now" do
70
+ subject { table.last_provisioned_for :writes, at: now }
71
+ it { should == 200.0 }
72
+ end
73
+
74
+ describe "#last_provisioned_for :writes, at: 3.minutes.ago" do
75
+ subject { table.last_provisioned_for :writes, at: 3.minutes.ago }
76
+ it { should == 800.0 }
77
+ end
78
+
79
+ describe "#all_times" do
80
+ subject { table.all_times }
81
+ its(:length) { should == 4 }
82
+
83
+ specify("is ordered") { subject.should == subject.sort }
84
+ end
85
+ end
86
+
87
+ describe 'clearing data' do
88
+ before { table.clear_data }
89
+
90
+ specify "table.data should be totally empty" do
91
+ table.data.keys.each do |key|
92
+ table.data[key].should be_empty
93
+ end
94
+ end
95
+ end
96
+
97
+ describe 'stats' do
98
+ before do
99
+ table.clear_data
100
+
101
+ table.tick(3.seconds.ago, {
102
+ provisioned_reads: 100.0,
103
+ consumed_reads: 99.0,
104
+
105
+ provisioned_writes: 200.0,
106
+ consumed_writes: 198.0,
107
+ })
108
+
109
+ table.tick(12.seconds.ago, {
110
+ provisioned_reads: 100.0,
111
+ consumed_reads: 99.0,
112
+
113
+ provisioned_writes: 200.0,
114
+ consumed_writes: 198.0,
115
+ })
116
+ end
117
+
118
+ describe 'wasted_read_units' do
119
+ subject { table.wasted_read_units }
120
+ it { should == 2.0 }
121
+ end
122
+
123
+ describe 'wasted_write_units' do
124
+ subject { table.wasted_write_units }
125
+ it { should == 4.0 }
126
+ end
127
+
128
+ describe 'lost_read_units' do
129
+ before do
130
+ table.clear_data
131
+ table.tick(12.seconds.ago, {
132
+ provisioned_reads: 100.0,
133
+ consumed_reads: 102.0,
134
+ })
135
+ end
136
+
137
+ subject { table.lost_read_units }
138
+ it { should == 2.0 }
139
+ end
140
+
141
+ describe 'lost_write_units' do
142
+ before do
143
+ table.clear_data
144
+ table.tick(12.seconds.ago, {
145
+ provisioned_writes: 100.0,
146
+ consumed_writes: 105.0,
147
+ })
148
+ end
149
+
150
+ subject { table.lost_write_units }
151
+ it { should == 5.0 }
152
+ end
153
+ end
154
+
155
+ describe 'no data' do
156
+ before { table.clear_data }
157
+
158
+ describe 'lost_write_units' do
159
+ subject { table.lost_write_units }
160
+ it { should == 0.0 }
161
+ end
162
+
163
+ describe 'lost_read_units' do
164
+ subject { table.lost_read_units }
165
+ it { should == 0.0 }
166
+ end
167
+
168
+ describe 'wasted_read_units' do
169
+ subject { table.wasted_read_units }
170
+ it { should == 0.0 }
171
+ end
172
+
173
+ describe 'wasted_write_units' do
174
+ subject { table.wasted_write_units }
175
+ it { should == 0.0 }
176
+ end
177
+
178
+ describe "#all_times" do
179
+ subject { table.all_times }
180
+ its(:length) { should == 0 }
181
+ end
182
+
183
+ describe "#last 3.seconds, :consumed_reads" do
184
+ subject { table.last 3.seconds, :consumed_reads }
185
+ it { should == [] }
186
+ end
187
+
188
+ describe "#last 1, :consumed_writes" do
189
+ subject { table.last 1, :consumed_writes }
190
+ it { should == [] }
191
+ end
192
+
193
+ describe "#last_provisioned_for :reads" do
194
+ subject { table.last_provisioned_for :reads }
195
+ it { should be_nil }
196
+ end
197
+
198
+ describe "#last_provisioned_for :writes" do
199
+ subject { table.last_provisioned_for :writes }
200
+ it { should be_nil }
201
+ end
202
+ end
203
+
204
+ describe 'time window' do
205
+ describe 'inserting data outside of time window' do
206
+ before do
207
+ table.clear_data
208
+ table.tick(12.weeks.ago, {
209
+ provisioned_reads: 600.0,
210
+ provisioned_writes: 800.0,
211
+ consumed_reads: 20.0,
212
+ consumed_writes: 30.0,
213
+ })
214
+ end
215
+
216
+ it 'should not work' do
217
+ table.all_times.should be_empty
218
+ end
219
+ end
220
+
221
+ describe 'data time based cleanup' do
222
+ before do
223
+ table.clear_data
224
+ Timecop.travel(2.weeks.ago)
225
+
226
+ table.tick(Time.now, {
227
+ provisioned_reads: 600.0,
228
+ provisioned_writes: 800.0,
229
+ consumed_reads: 20.0,
230
+ consumed_writes: 30.0,
231
+ })
232
+
233
+ to_the_future = Time.now + DynamoAutoscale::TableTracker::TIME_WINDOW +
234
+ 2.minutes
235
+
236
+ Timecop.travel(to_the_future)
237
+
238
+ table.tick(Time.now, {
239
+ provisioned_reads: 600.0,
240
+ provisioned_writes: 800.0,
241
+ consumed_reads: 20.0,
242
+ consumed_writes: 30.0,
243
+ })
244
+ end
245
+
246
+ it 'should remove data outside of the time window' do
247
+ table.all_times.length.should == 1
248
+ end
249
+
250
+ it 'should not remove data inside of the time window' do
251
+ table.tick(2.seconds.from_now, {})
252
+ table.all_times.length.should == 2
253
+ end
254
+ end
255
+ end
256
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dynamo-autoscale
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - InvisibleHand
9
+ autorequire:
10
+ bindir:
11
+ - bin
12
+ cert_chain: []
13
+ date: 2013-07-02 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: aws-sdk
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rbtree
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: ruby-prof
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: colored
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: activesupport
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ description: Will automatically monitor DynamoDB tables and scale them based on rules.
96
+ email: developers@getinvisiblehand.com
97
+ executables:
98
+ - dynamo-autoscale
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - .gitignore
103
+ - Gemfile
104
+ - Gemfile.lock
105
+ - LICENSE
106
+ - README.md
107
+ - Rakefile
108
+ - aws.sample.yml
109
+ - bin/dynamo-autoscale
110
+ - config/environment/common.rb
111
+ - config/environment/console.rb
112
+ - config/environment/test.rb
113
+ - config/logger.yml
114
+ - config/services/aws.rb
115
+ - config/services/logger.rb
116
+ - data/.gitkeep
117
+ - dynamo-autoscale.gemspec
118
+ - lib/dynamo-autoscale.rb
119
+ - lib/dynamo-autoscale/actioner.rb
120
+ - lib/dynamo-autoscale/cw_poller.rb
121
+ - lib/dynamo-autoscale/dispatcher.rb
122
+ - lib/dynamo-autoscale/dynamo_actioner.rb
123
+ - lib/dynamo-autoscale/ext/active_support/duration.rb
124
+ - lib/dynamo-autoscale/local_actioner.rb
125
+ - lib/dynamo-autoscale/local_data_poll.rb
126
+ - lib/dynamo-autoscale/logger.rb
127
+ - lib/dynamo-autoscale/metrics.rb
128
+ - lib/dynamo-autoscale/poller.rb
129
+ - lib/dynamo-autoscale/pretty_formatter.rb
130
+ - lib/dynamo-autoscale/rule.rb
131
+ - lib/dynamo-autoscale/rule_set.rb
132
+ - lib/dynamo-autoscale/table_tracker.rb
133
+ - lib/dynamo-autoscale/unit_cost.rb
134
+ - lib/dynamo-autoscale/version.rb
135
+ - rlib/dynamodb_graph.r
136
+ - rlib/dynamodb_scatterplot.r
137
+ - rulesets/default.rb
138
+ - rulesets/erroneous.rb
139
+ - rulesets/gradual_tail.rb
140
+ - rulesets/none.rb
141
+ - script/console
142
+ - script/historic_data
143
+ - script/hourly_wastage
144
+ - script/monitor
145
+ - script/simulator
146
+ - script/test
147
+ - script/validate_ruleset
148
+ - spec/actioner_spec.rb
149
+ - spec/rule_set_spec.rb
150
+ - spec/rule_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/table_tracker_spec.rb
153
+ homepage: http://github.com/invisiblehand/dynamo-autoscale
154
+ licenses:
155
+ - MIT
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 1.8.23
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: Autoscaling for DynamoDB provisioned throughputs.
178
+ test_files: []