dynamo-record 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 40c3a218185cf95ac6577f5e2954740ce8691bd4
4
- data.tar.gz: d129868fe0ddc2eee7487dec4e59f4252ad7d0f7
3
+ metadata.gz: e0e65c5e7c2ecaac423640b5872a8c40d58a539a
4
+ data.tar.gz: 807766fdecdccdd5029a11a1f0fcbad8a2e61569
5
5
  SHA512:
6
- metadata.gz: 27c3831739a5c405d87100ac1a8c4bbf4292e349ff0b24c6e1215fe19360867cb58917d6db92f7ff58175e20adcf7330c794748a2fbda8b90eaf85caa31b45bb
7
- data.tar.gz: 697e619b45192d0e22e1afc77707014541a41680a51032e833dc7c88e9e65b728740fa88b53263416d637a69fd7febfac52dd146f63fb3381e2a96f564f1b704
6
+ metadata.gz: 5213186128c25aca4a3a37a27ee8534f7054f9bac3e9908c930a857d43811af1628a66f5ff76a551190a3144af6d98c464678c8becf6859d8cd8fa61e78d67d7
7
+ data.tar.gz: efefa9924786b3068896273ebbe3711d1eb2a7872e5270b15e735e81fc8cf9051b304f1fd3d036c17a7707cf252bdf6717bb48416c81f0c03025204c46aefc48
@@ -3,6 +3,7 @@ require 'aws-record'
3
3
  require 'rails/railtie'
4
4
 
5
5
  require 'dynamo/record/marshalers'
6
+ require 'dynamo/record/batch_get'
6
7
  require 'dynamo/record/batch_write'
7
8
  require 'dynamo/record/batch_request'
8
9
  require 'dynamo/record/model'
@@ -17,3 +18,15 @@ require 'dynamo/record/task_helpers/drop_table'
17
18
  require 'dynamo/record/task_helpers/list_tables'
18
19
  require 'dynamo/record/task_helpers/migration_runner'
19
20
  require 'dynamo/record/task_helpers/scale'
21
+
22
+ module Dynamo
23
+ module Record
24
+ class << self
25
+ attr_writer :logger
26
+
27
+ def logger
28
+ @logger ||= Logger.new($stdout).tap { |log| log.progname = self.name }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,81 @@
1
+ require 'json'
2
+
3
+ module Dynamo
4
+ module Record
5
+ module BatchGet
6
+ BATCH_SIZE = 100
7
+
8
+ module ClassMethods
9
+ def batch_get(keys)
10
+ process_batch_get_requests(keys)
11
+ end
12
+
13
+ private
14
+
15
+ def process_batch_get_requests(keys)
16
+ attempt = 1
17
+ results = []
18
+ unprocessed_keys = keys.dup.map(&:stringify_keys)
19
+ while !unprocessed_keys.empty? && attempt <= max_retries
20
+ batch = unprocessed_keys.shift(BATCH_SIZE)
21
+ Dynamo::Record.logger.debug "#{name} batch_get_item #{batch.count} request_items"
22
+ processed, unprocessed = get_batch_from_dynamo(batch)
23
+ results.concat(processed)
24
+
25
+ unless unprocessed.empty?
26
+ Dynamo::Record.logger.debug "#{name} batch_get_item #{unprocessed.count} unprocessed_keys"
27
+ unprocessed_keys.unshift(*unprocessed)
28
+ sleep(rand(1 << attempt) + 1)
29
+ attempt += 1
30
+ end
31
+ end
32
+
33
+ if !unprocessed_keys.empty? && attempt > max_retries
34
+ raise NumberOfRetriesExceeded.new(keys), 'Number of retries exceeded'
35
+ end
36
+
37
+ results.map { |r| build_item_from_hash(r) }
38
+ end
39
+
40
+ # BatchGetItem will return a partial result if provisioned throughput is
41
+ # exceeded or an internal error occurs. If all items cannot be processed
42
+ # ProvisionedThroughputExceededException is raised.
43
+ # https://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#batch_get_item-instance_method
44
+ def get_batch_from_dynamo(batch)
45
+ response = dynamodb_client.batch_get_item(
46
+ request_items: {
47
+ table_name => { keys: batch }
48
+ }
49
+ )
50
+ [
51
+ response.responses[table_name],
52
+ response.unprocessed_keys[table_name]&.keys || []
53
+ ]
54
+ rescue Aws::DynamoDB::Errors::ProvisionedThroughputExceededException
55
+ [[], batch]
56
+ end
57
+
58
+ # Convert response hashes to Record instances
59
+ # Adapted from `build_item_from_resp` used by `find` but that method is closely tied to get_item response format
60
+ # https://github.com/aws/aws-sdk-ruby-record/blob/v2.3.0/lib/aws-record/record/item_operations.rb#L593
61
+ def build_item_from_hash(resp)
62
+ record = new
63
+ data = record.instance_variable_get('@data')
64
+ attributes.attributes.each do |name, attr|
65
+ data.set_attribute(name, attr.extract(resp))
66
+ data.new_record = false
67
+ end
68
+ record
69
+ end
70
+ end
71
+
72
+ class NumberOfRetriesExceeded < RuntimeError
73
+ attr_reader :keys
74
+
75
+ def initialize(keys)
76
+ @keys = keys
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -2,7 +2,6 @@ module Dynamo
2
2
  module Record
3
3
  module BatchWrite
4
4
  BATCH_SIZE = 25
5
- DEFAULT_MAX_RETRIES = 5
6
5
  MAX_PAYLOAD_SIZE = 16_000_000 # 16 MB
7
6
 
8
7
  module ClassMethods
@@ -78,11 +77,6 @@ module Dynamo
78
77
  current_batch_size += request.request_size
79
78
  end
80
79
  end
81
-
82
- # Override in model if you want it to be different.
83
- def max_retries
84
- DEFAULT_MAX_RETRIES
85
- end
86
80
  end
87
81
 
88
82
  class NumberOfRetriesExceeded < RuntimeError
@@ -2,6 +2,7 @@ module Dynamo
2
2
  module Record
3
3
  module Model
4
4
  COMPOSITE_DELIMITER = '|'.freeze
5
+ DEFAULT_MAX_RETRIES = 5
5
6
 
6
7
  def self.included(klass)
7
8
  klass.include(Aws::Record)
@@ -9,6 +10,7 @@ module Dynamo
9
10
 
10
11
  klass.extend ClassMethods
11
12
  klass.extend Dynamo::Record::BatchWrite::ClassMethods
13
+ klass.extend Dynamo::Record::BatchGet::ClassMethods
12
14
  klass.send :prepend, InstanceMethods
13
15
  end
14
16
 
@@ -104,7 +106,9 @@ module Dynamo
104
106
  string.split(COMPOSITE_DELIMITER)
105
107
  end
106
108
 
107
- # TODO: Create a batch save method.
109
+ def max_retries
110
+ DEFAULT_MAX_RETRIES
111
+ end
108
112
  end
109
113
 
110
114
  module InstanceMethods
@@ -3,6 +3,10 @@ module Dynamo
3
3
  class Railtie < Rails::Railtie
4
4
  railtie_name :dynamo_record
5
5
 
6
+ initializer 'logger' do
7
+ Dynamo::Record.logger = Rails.logger
8
+ end
9
+
6
10
  rake_tasks do
7
11
  load 'tasks/dynamo.rake'
8
12
  end
@@ -1,5 +1,5 @@
1
1
  module Dynamo
2
2
  module Record
3
- VERSION = '1.1.0'.freeze
3
+ VERSION = '1.2.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamo-record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davis McClellan
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2018-07-30 00:00:00.000000000 Z
17
+ date: 2019-03-01 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activemodel
@@ -243,6 +243,7 @@ files:
243
243
  - docker-compose.yml
244
244
  - dynamo-record.gemspec
245
245
  - lib/dynamo/record.rb
246
+ - lib/dynamo/record/batch_get.rb
246
247
  - lib/dynamo/record/batch_request.rb
247
248
  - lib/dynamo/record/batch_write.rb
248
249
  - lib/dynamo/record/marshalers.rb
@@ -278,7 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
278
279
  version: '0'
279
280
  requirements: []
280
281
  rubyforge_project:
281
- rubygems_version: 2.6.14.1
282
+ rubygems_version: 2.5.1
282
283
  signing_key:
283
284
  specification_version: 4
284
285
  summary: Extensions to Aws::Record for working with DynamoDB.