dynamodb_framework 0.1.4 → 1.0.0
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.
- checksums.yaml +4 -4
- data/.idea/workspace.xml +306 -137
- data/README.md +13 -13
- data/dynamodb_framework.gemspec +1 -1
- data/lib/dynamodb_framework.rb +11 -7
- data/lib/dynamodb_framework/dynamodb_attributes_builder.rb +60 -16
- data/lib/dynamodb_framework/dynamodb_logger.rb +7 -0
- data/lib/dynamodb_framework/dynamodb_migration_manager.rb +63 -61
- data/lib/dynamodb_framework/dynamodb_migration_script.rb +13 -12
- data/lib/dynamodb_framework/dynamodb_repository.rb +158 -131
- data/lib/dynamodb_framework/dynamodb_store.rb +13 -13
- data/lib/dynamodb_framework/dynamodb_table_manager.rb +267 -198
- data/lib/dynamodb_framework/version.rb +2 -2
- data/script/cleanup.sh +6 -0
- data/script/container_loop.sh +6 -0
- data/script/docker-compose.yml +5 -0
- data/script/restart.sh +3 -0
- data/script/start.sh +4 -0
- data/script/stop.sh +2 -0
- metadata +32 -26
@@ -1,16 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module DynamoDbFramework
|
2
|
+
class Store
|
3
|
+
attr_reader :client
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
|
7
|
+
if options.has_key?(:endpoint)
|
8
|
+
@client = Aws::DynamoDB::Client.new(region: options[:aws_region], endpoint: options[:endpoint])
|
9
|
+
else
|
10
|
+
@client = Aws::DynamoDB::Client.new
|
11
|
+
end
|
3
12
|
|
4
|
-
def initialize
|
5
|
-
region = ENV['DYNAMODB_REGION']
|
6
|
-
endpoint = ENV['DYNAMODB_ENDPOINT']
|
7
|
-
secret = ENV['DYNAMODB_SECRET']
|
8
|
-
key = ENV['DYNAMODB_ACCESS_KEY']
|
9
|
-
if endpoint != nil
|
10
|
-
@client = Aws::DynamoDB::Client.new(region: region, endpoint: endpoint, secret_access_key: secret, access_key_id: key)
|
11
|
-
else
|
12
|
-
@client = Aws::DynamoDB::Client.new(region: region, secret_access_key: secret, access_key_id: key)
|
13
13
|
end
|
14
|
-
end
|
15
14
|
|
16
|
-
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,283 +1,352 @@
|
|
1
|
-
|
1
|
+
module DynamoDbFramework
|
2
|
+
class TableManager
|
2
3
|
|
3
|
-
|
4
|
+
attr_reader :dynamodb
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
def initialize(store)
|
7
|
+
@dynamodb = store
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
+
def exists?(table_name)
|
10
11
|
|
11
|
-
|
12
|
+
exists = true
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
begin
|
15
|
+
dynamodb.client.describe_table(:table_name => table_name)
|
16
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
17
|
+
exists = false
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
+
return exists
|
20
21
|
|
21
|
-
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
def has_index?(table_name, index_name)
|
25
|
+
exists = true
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
begin
|
28
|
+
result = dynamodb.client.describe_table(:table_name => table_name)
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
if result.table[:global_secondary_indexes] == nil
|
31
|
+
return false
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
if result.table[:global_secondary_indexes].select { |i| i[:index_name] == index_name }.length > 0
|
35
|
+
exists = true
|
36
|
+
else
|
37
|
+
exists = false
|
38
|
+
end
|
39
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
36
40
|
exists = false
|
37
41
|
end
|
38
|
-
|
39
|
-
exists
|
42
|
+
|
43
|
+
return exists
|
40
44
|
end
|
41
45
|
|
42
|
-
|
43
|
-
end
|
46
|
+
def update_throughput(table_name, read_capacity, write_capacity)
|
44
47
|
|
45
|
-
|
48
|
+
if !exists?(table_name)
|
49
|
+
raise "table: #{table_name}, does not exist."
|
50
|
+
end
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
table = {
|
53
|
+
:table_name => table_name,
|
54
|
+
:provisioned_throughput => {
|
55
|
+
:read_capacity_units => read_capacity,
|
56
|
+
:write_capacity_units => write_capacity
|
57
|
+
}
|
58
|
+
}
|
50
59
|
|
51
|
-
|
52
|
-
:table_name => table_name,
|
53
|
-
:provisioned_throughput => {
|
54
|
-
:read_capacity_units => read_capacity,
|
55
|
-
:write_capacity_units => write_capacity
|
56
|
-
}
|
57
|
-
}
|
60
|
+
dynamodb.client.update_table(table)
|
58
61
|
|
59
|
-
|
62
|
+
# wait for table to be updated
|
63
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Waiting for table: [#{table_name}] to be updated."
|
64
|
+
wait_until_active(table_name)
|
65
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Table: [#{table_name}] updated."
|
60
66
|
|
61
|
-
|
62
|
-
puts "waiting for table: [#{table_name}] to be updated..."
|
63
|
-
wait_until_active(table_name)
|
64
|
-
puts "table: [#{table_name}] updated!"
|
67
|
+
end
|
65
68
|
|
66
|
-
|
69
|
+
def add_index(table_name, attributes, global_index)
|
70
|
+
|
71
|
+
attribute_definitions = []
|
72
|
+
|
73
|
+
attributes.each do |a|
|
74
|
+
attribute_definitions.push({ :attribute_name => a[:name], :attribute_type => a[:type] })
|
75
|
+
end
|
67
76
|
|
68
|
-
|
77
|
+
table = {
|
78
|
+
:table_name => table_name,
|
79
|
+
:attribute_definitions => attribute_definitions,
|
80
|
+
:global_secondary_index_updates => [
|
81
|
+
:create => global_index
|
82
|
+
]
|
83
|
+
}
|
69
84
|
|
70
|
-
|
85
|
+
dynamodb.client.update_table(table)
|
71
86
|
|
72
|
-
|
73
|
-
|
87
|
+
# wait for table to be updated
|
88
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Adding global index: #{global_index[:index_name]}."
|
89
|
+
wait_until_index_active(table_name, global_index[:index_name])
|
90
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Index added."
|
74
91
|
end
|
75
92
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
93
|
+
def update_index_throughput(table_name, index_name, read_capacity, write_capacity)
|
94
|
+
table = {
|
95
|
+
:table_name => table_name,
|
96
|
+
:global_secondary_index_updates => [
|
97
|
+
:update => {
|
98
|
+
:index_name => index_name,
|
99
|
+
:provisioned_throughput => {
|
100
|
+
:read_capacity_units => read_capacity,
|
101
|
+
:write_capacity_units => write_capacity
|
102
|
+
}
|
103
|
+
}
|
104
|
+
]
|
105
|
+
}
|
106
|
+
|
107
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Updating throughput for global index: #{index_name}."
|
108
|
+
|
109
|
+
dynamodb.client.update_table(table)
|
110
|
+
|
111
|
+
# wait for table to be updated
|
112
|
+
|
113
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Waiting for table: [#{table_name}] to be updated."
|
114
|
+
wait_until_active(table_name)
|
115
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Table: [#{table_name}] updated."
|
116
|
+
end
|
83
117
|
|
84
|
-
|
118
|
+
def drop_index(table_name, index_name)
|
119
|
+
table = {
|
120
|
+
:table_name => table_name,
|
121
|
+
:global_secondary_index_updates => [
|
122
|
+
:delete => {
|
123
|
+
:index_name => index_name
|
124
|
+
}
|
125
|
+
]
|
126
|
+
}
|
127
|
+
|
128
|
+
dynamodb.client.update_table(table)
|
129
|
+
|
130
|
+
# wait for table to be updated
|
131
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Deleting global index: #{index_name}."
|
132
|
+
wait_until_index_dropped(table_name, index_name)
|
133
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Index: [#{index_name}] dropped."
|
134
|
+
end
|
85
135
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
136
|
+
def get_status(table_name)
|
137
|
+
result = dynamodb.client.describe_table(:table_name => table_name)
|
138
|
+
return result.table[:table_status]
|
139
|
+
end
|
91
140
|
|
92
|
-
|
93
|
-
|
94
|
-
:table_name => table_name,
|
95
|
-
:global_secondary_index_updates => [
|
96
|
-
:update => {
|
97
|
-
:index_name => index_name,
|
98
|
-
:provisioned_throughput => {
|
99
|
-
:read_capacity_units => read_capacity,
|
100
|
-
:write_capacity_units => write_capacity
|
101
|
-
}
|
102
|
-
}
|
103
|
-
]
|
104
|
-
}
|
105
|
-
|
106
|
-
dynamodb.client.update_table(table)
|
107
|
-
|
108
|
-
# wait for table to be updated
|
109
|
-
puts "Updating throughput for global index: #{index_name}..."
|
110
|
-
puts "waiting for table: [#{table_name}] to be updated..."
|
111
|
-
wait_until_active(table_name)
|
112
|
-
puts "table: [#{table_name}] updated!"
|
113
|
-
end
|
141
|
+
def get_index_status(table_name, index_name)
|
142
|
+
result = dynamodb.client.describe_table(:table_name => table_name)
|
114
143
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
:global_secondary_index_updates => [
|
119
|
-
:delete => {
|
120
|
-
:index_name => index_name
|
121
|
-
}
|
122
|
-
]
|
123
|
-
}
|
124
|
-
|
125
|
-
dynamodb.client.update_table(table)
|
126
|
-
|
127
|
-
# wait for table to be updated
|
128
|
-
puts "Deleting global index: #{index_name}..."
|
129
|
-
wait_until_index_dropped(table_name, index_name)
|
130
|
-
puts "Index: [#{index_name}] dropped!"
|
131
|
-
end
|
144
|
+
if result.table[:global_secondary_indexes] == nil
|
145
|
+
return nil
|
146
|
+
end
|
132
147
|
|
133
|
-
|
134
|
-
result = dynamodb.client.describe_table(:table_name => table_name)
|
135
|
-
return result.table[:table_status]
|
136
|
-
end
|
148
|
+
index = result.table[:global_secondary_indexes].select { |i| i[:index_name] == index_name }
|
137
149
|
|
138
|
-
|
139
|
-
|
150
|
+
if index.length > 0
|
151
|
+
return index[0][:index_status]
|
152
|
+
end
|
140
153
|
|
141
|
-
if result.table[:global_secondary_indexes] == nil
|
142
154
|
return nil
|
143
155
|
end
|
144
156
|
|
145
|
-
|
157
|
+
def wait_until_active(table_name)
|
158
|
+
|
159
|
+
end_time = Time.now + 300
|
160
|
+
while Time.now < end_time do
|
161
|
+
|
162
|
+
status = get_status(table_name)
|
163
|
+
|
164
|
+
if status == 'ACTIVE'
|
165
|
+
return
|
166
|
+
end
|
167
|
+
|
168
|
+
sleep(5)
|
169
|
+
end
|
170
|
+
|
171
|
+
raise "Timeout occured while waiting for table: #{table_name}, to become active."
|
146
172
|
|
147
|
-
if index.length > 0
|
148
|
-
return index[0][:index_status]
|
149
173
|
end
|
150
174
|
|
151
|
-
|
152
|
-
end
|
175
|
+
def wait_until_index_active(table_name, index_name)
|
153
176
|
|
154
|
-
|
177
|
+
end_time = Time.now + 300
|
178
|
+
while Time.now < end_time do
|
155
179
|
|
156
|
-
|
157
|
-
while Time.now < end_time do
|
180
|
+
status = get_index_status(table_name, index_name)
|
158
181
|
|
159
|
-
|
182
|
+
if status == 'ACTIVE'
|
183
|
+
return
|
184
|
+
end
|
160
185
|
|
161
|
-
|
162
|
-
return
|
186
|
+
sleep(5)
|
163
187
|
end
|
164
188
|
|
165
|
-
|
189
|
+
raise "Timeout occured while waiting for table: #{table_name}, index: #{index_name}, to become active."
|
190
|
+
|
166
191
|
end
|
167
192
|
|
168
|
-
|
193
|
+
def wait_until_index_dropped(table_name, index_name)
|
169
194
|
|
170
|
-
|
195
|
+
end_time = Time.now + 300
|
196
|
+
while Time.now < end_time do
|
197
|
+
|
198
|
+
status = get_index_status(table_name, index_name)
|
199
|
+
|
200
|
+
if status == nil
|
201
|
+
return
|
202
|
+
end
|
171
203
|
|
172
|
-
|
204
|
+
sleep(5)
|
205
|
+
end
|
206
|
+
|
207
|
+
raise "Timeout occured while waiting for table: #{table_name}, index: #{index_name}, to be dropped."
|
173
208
|
|
174
|
-
|
175
|
-
while Time.now < end_time do
|
209
|
+
end
|
176
210
|
|
177
|
-
|
211
|
+
def create(table_name, attributes, partition_key, range_key = nil, read_capacity = 20, write_capacity = 10, global_indexes = nil)
|
178
212
|
|
179
|
-
if
|
213
|
+
if exists?(table_name)
|
180
214
|
return
|
181
215
|
end
|
182
216
|
|
183
|
-
|
184
|
-
end
|
217
|
+
attribute_definitions = []
|
185
218
|
|
186
|
-
|
219
|
+
attributes.each do |a|
|
220
|
+
attribute_definitions.push({ :attribute_name => a[:name], :attribute_type => a[:type] })
|
221
|
+
end
|
187
222
|
|
188
|
-
|
223
|
+
key_schema = []
|
224
|
+
key_schema.push({ :attribute_name => partition_key, :key_type => :HASH })
|
225
|
+
if range_key != nil
|
226
|
+
key_schema.push({ :attribute_name => range_key, :key_type => :RANGE })
|
227
|
+
end
|
189
228
|
|
190
|
-
|
229
|
+
table = {
|
230
|
+
:table_name => table_name,
|
231
|
+
:attribute_definitions => attribute_definitions,
|
232
|
+
:key_schema => key_schema,
|
233
|
+
:provisioned_throughput => {
|
234
|
+
:read_capacity_units => read_capacity,
|
235
|
+
:write_capacity_units => write_capacity
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
if global_indexes != nil
|
240
|
+
table[:global_secondary_indexes] = global_indexes
|
241
|
+
end
|
191
242
|
|
192
|
-
|
193
|
-
|
243
|
+
dynamodb.client.create_table(table)
|
244
|
+
|
245
|
+
# wait for table to be created
|
246
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Waiting for table: [#{table_name}] to be created."
|
247
|
+
dynamodb.client.wait_until(:table_exists, table_name: table_name)
|
248
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Table: [#{table_name}] created."
|
249
|
+
end
|
194
250
|
|
195
|
-
|
251
|
+
def create_table(options = {})
|
196
252
|
|
197
|
-
if
|
253
|
+
if options[:name] == nil
|
254
|
+
raise 'A valid table name must be specified.'
|
255
|
+
end
|
256
|
+
|
257
|
+
if exists?(options[:name])
|
198
258
|
return
|
199
259
|
end
|
200
260
|
|
201
|
-
|
202
|
-
|
261
|
+
options[:read_capacity] ||= 20
|
262
|
+
options[:write_capacity] ||= 20
|
203
263
|
|
204
|
-
|
264
|
+
if !options[:attributes].is_a?(Array)
|
265
|
+
raise 'A valid :attributes array must be specified.'
|
266
|
+
end
|
205
267
|
|
206
|
-
|
268
|
+
attribute_definitions = options[:attributes].map { |a| { :attribute_name => a[:name], :attribute_type => a[:type] } }
|
207
269
|
|
208
|
-
|
270
|
+
hash_key = options[:attributes].detect { |a| a[:key] == :hash }
|
271
|
+
if hash_key == nil
|
272
|
+
raise 'No Hash Key attribute has been specified.'
|
273
|
+
end
|
209
274
|
|
210
|
-
|
211
|
-
return
|
212
|
-
end
|
275
|
+
range_key = options[:attributes].detect { |a| a[:key] == :range }
|
213
276
|
|
214
|
-
|
277
|
+
key_schema = []
|
278
|
+
key_schema.push({ :attribute_name => hash_key[:name], :key_type => :HASH })
|
279
|
+
if range_key != nil
|
280
|
+
key_schema.push({ :attribute_name => range_key[:name], :key_type => :RANGE })
|
281
|
+
end
|
215
282
|
|
216
|
-
|
217
|
-
|
218
|
-
|
283
|
+
table = {
|
284
|
+
:table_name => options[:name],
|
285
|
+
:attribute_definitions => attribute_definitions,
|
286
|
+
:key_schema => key_schema,
|
287
|
+
:provisioned_throughput => {
|
288
|
+
:read_capacity_units => options[:read_capacity],
|
289
|
+
:write_capacity_units => options[:write_capacity]
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
if options[:global_indexes] != nil
|
294
|
+
table[:global_secondary_indexes] = options[:global_indexes]
|
295
|
+
end
|
219
296
|
|
220
|
-
|
221
|
-
key_schema.push({ :attribute_name => partition_key, :key_type => :HASH })
|
222
|
-
if range_key != nil
|
223
|
-
key_schema.push({ :attribute_name => range_key, :key_type => :RANGE })
|
224
|
-
end
|
297
|
+
dynamodb.client.create_table(table)
|
225
298
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
:provisioned_throughput => {
|
231
|
-
:read_capacity_units => read_capacity,
|
232
|
-
:write_capacity_units => write_capacity
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
236
|
-
if global_indexes != nil
|
237
|
-
table[:global_secondary_indexes] = global_indexes
|
299
|
+
# wait for table to be created
|
300
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Waiting for table: [#{options[:name]}] to be created."
|
301
|
+
dynamodb.client.wait_until(:table_exists, table_name: options[:name])
|
302
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Table: [#{options[:name]}] created."
|
238
303
|
end
|
239
304
|
|
240
|
-
|
305
|
+
def create_global_index(name, partition_key, range_key = nil, read_capacity = 20, write_capacity = 10)
|
241
306
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
307
|
+
key_schema = []
|
308
|
+
|
309
|
+
key_schema.push({ :attribute_name => partition_key, :key_type => :HASH })
|
310
|
+
if range_key != nil
|
311
|
+
key_schema.push({ :attribute_name => range_key, :key_type => :RANGE })
|
312
|
+
end
|
313
|
+
|
314
|
+
index = {
|
315
|
+
:index_name => name,
|
316
|
+
:key_schema => key_schema,
|
317
|
+
:projection => {
|
318
|
+
:projection_type => :ALL
|
319
|
+
},
|
320
|
+
:provisioned_throughput => {
|
321
|
+
:read_capacity_units => read_capacity,
|
322
|
+
:write_capacity_units => write_capacity,
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
return index
|
327
|
+
end
|
247
328
|
|
248
|
-
|
329
|
+
def drop(table_name)
|
249
330
|
|
250
|
-
|
331
|
+
if !exists?(table_name)
|
332
|
+
return
|
333
|
+
end
|
251
334
|
|
252
|
-
|
253
|
-
|
254
|
-
|
335
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Dropping table: [#{table_name}]."
|
336
|
+
dynamodb.client.delete_table({ table_name: table_name })
|
337
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Table: [#{table_name}] dropped."
|
255
338
|
end
|
256
339
|
|
257
|
-
|
258
|
-
:index_name => name,
|
259
|
-
:key_schema => key_schema,
|
260
|
-
:projection => {
|
261
|
-
:projection_type => :ALL
|
262
|
-
},
|
263
|
-
:provisioned_throughput => {
|
264
|
-
:read_capacity_units => read_capacity,
|
265
|
-
:write_capacity_units => write_capacity,
|
266
|
-
}
|
267
|
-
}
|
268
|
-
|
269
|
-
return index
|
270
|
-
end
|
340
|
+
def drop_table(table_name)
|
271
341
|
|
272
|
-
|
342
|
+
if !exists?(table_name)
|
343
|
+
return
|
344
|
+
end
|
273
345
|
|
274
|
-
|
275
|
-
|
346
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Dropping table: [#{table_name}]."
|
347
|
+
dynamodb.client.delete_table({ table_name: table_name })
|
348
|
+
DynamoDbFramework::LOGGER.info "[DynamoDbFramework] - Table: [#{table_name}] dropped."
|
276
349
|
end
|
277
350
|
|
278
|
-
puts "dropping table: [#{table_name}] ..."
|
279
|
-
dynamodb.client.delete_table({ table_name: table_name })
|
280
|
-
puts "table: [#{table_name}] dropped!"
|
281
351
|
end
|
282
|
-
|
283
|
-
end
|
352
|
+
end
|