Skip to content

Commit

Permalink
Add Batch Read support
Browse files Browse the repository at this point in the history
* Implemented the `BatchGetItem` operation (#122).

Co-authored-by: Juli Tera <terajul@amazon.com>
  • Loading branch information
jterapin and jterapin authored Jan 13, 2023
1 parent b16e7a5 commit 2c13263
Show file tree
Hide file tree
Showing 13 changed files with 779 additions and 102 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', jruby-9.1, jruby-9.2]
ruby: ['2.0', 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2, jruby-9.1, jruby-9.2, jruby-9.3, jruby-9.4]
env: [NEW_RAILS, OLD_RAILS]
exclude:
- ruby: 2.0
- ruby: '2.0'
env: NEW_RAILS
- ruby: 2.1
env: NEW_RAILS
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Implement the `BatchGetItem` operation (#122)

2.9.0 (2022-11-16)
------------------

Expand Down
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,72 @@ item.active = false
item.save
```

### `BatchGetItem` and `BatchWriteItem`
Aws Record provides [BatchGetItem](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_get_item-instance_method)
and [BatchWriteItem](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method)
support for aws-record models.

More info under the following documentation:
* [Batch](https://docs.aws.amazon.com/sdk-for-ruby/aws-record/api/Aws/Record/Batch.html)
* [BatchWrite](https://docs.aws.amazon.com/sdk-for-ruby/aws-record/api/Aws/Record/BatchWrite.html)
* [BatchRead](https://docs.aws.amazon.com/sdk-for-ruby/aws-record/api/Aws/Record/BatchRead.html)

See examples below to see the feature in action.

**`BatchGetItem` Example:**
```ruby
class Lunch
include Aws::Record
integer_attr :id, hash_key: true
string_attr :name, range_key: true
end

class Dessert
include Aws::Record
integer_attr :id, hash_key: true
string_attr :name, range_key: true
end

# batch operations
operation = Aws::Record::Batch.read do |db|
db.find(Lunch, id: 1, name: 'Papaya Salad')
db.find(Lunch, id: 2, name: 'BLT Sandwich')
db.find(Dessert, id: 1, name: 'Apple Pie')
end

# BatchRead is enumerable and handles pagination
operation.each { |item| item.id }

# Alternatively, BatchRead provides a lower level interface through: execute!, complete? and items.
# Unprocessed items can be processed by calling:
operation.execute! until operation.complete?
```

**`BatchWriteItem` Example:**
```ruby
class Breakfast
include Aws::Record
integer_attr :id, hash_key: true
string_attr :name, range_key: true
string_attr :body
end

# setup
eggs = Breakfast.new(id: 1, name: "eggs").save!
waffles = Breakfast.new(id: 2, name: "waffles")
pancakes = Breakfast.new(id: 3, name: "pancakes")

# batch operations
operation = Aws::Record::Batch.write(client: Breakfast.dynamodb_client) do |db|
db.put(waffles)
db.delete(eggs)
db.put(pancakes)
end

# unprocessed items can be retried by calling Aws::Record::BatchWrite#execute!
operation.execute! until operation.complete?
```

### Inheritance Support
Aws Record models can be extended using standard ruby inheritance. The child model must
include `Aws::Record` in their model and the following will be inherited:
Expand Down
73 changes: 73 additions & 0 deletions features/batch/batch.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# language: en

@dynamodb @batch
Feature: Amazon DynamoDB Batch
This feature tests the ability to use the batch read and write item APIs via
aws-record. To run these tests, you will need to have valid AWS credentials
that are accessible with the AWS SDK for Ruby's standard credential provider
chain. In practice, this means a shared credential file or environment
variables with your credentials. These tests may have some AWS costs associated
with running them since AWS resources are created and destroyed within
these tests.

Background:
Given a Parent model with definition:
"""
set_table_name('FoodTable')
integer_attr :id, hash_key: true, database_attribute_name: 'Food ID'
string_attr :dish, range_key: true
boolean_attr :spicy
"""
And a Parent model with TableConfig of:
"""
Aws::Record::TableConfig.define do |t|
t.model_class(ParentTableModel)
t.read_capacity_units(2)
t.write_capacity_units(2)
t.client_options(region: "us-east-1")
end
"""
When we migrate the TableConfig
Then eventually the table should exist in DynamoDB
And a Child model with definition:
"""
set_table_name('DessertTable')
boolean_attr :gluten_free
"""
And a Child model with TableConfig of:
"""
Aws::Record::TableConfig.define do |t|
t.model_class(ChildTableModel)
t.read_capacity_units(2)
t.write_capacity_units(2)
t.client_options(region: "us-east-1")
end
"""
When we migrate the TableConfig
Then eventually the table should exist in DynamoDB

Scenario: Perform a batch set of writes and read
When we make a batch write call with following Parent and Child model items:
"""
[
{ "model": "Parent", "id": 1, "dish": "Papaya Salad", "spicy": true },
{ "model": "Parent", "id": 2, "dish": "Hamburger", "spicy": false },
{ "model": "Child", "id": 1, "dish": "Apple Pie", "spicy": false, "gluten_free": false }
]
"""
And we make a batch read call for the following Parent and Child model item keys:
"""
[
{ "model": "Parent", "id": 1, "dish": "Papaya Salad" },
{ "model": "Parent", "id": 2, "dish": "Hamburger" },
{ "model": "Child", "id": 1, "dish": "Apple Pie" }
]
"""
Then we expect the batch read result to include the following items:
"""
[
{ "id": 1, "dish": "Papaya Salad", "spicy": true },
{ "id": 2, "dish": "Hamburger", "spicy": false },
{ "id": 1, "dish": "Apple Pie", "spicy": false, "gluten_free": false }
]
"""
62 changes: 62 additions & 0 deletions features/batch/step_definitions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

And(/^a (Parent|Child) model with TableConfig of:$/) do |model, code_block|
case model
when 'Parent'
ParentTableModel = @parent
when 'Child'
ChildTableModel = @model
else
raise 'Model must be either a Parent or Child'
end

@table_config = eval(code_block)
end

When(/^we make a batch write call with following Parent and Child model items:$/) do |string|
item_data = JSON.parse(string, symbolize_names: true)

Aws::Record::Batch.write do |db|
item_data.each do |item|
case item[:model]
when 'Parent'
db.put(@parent.new(remove_model_key(item)))
when 'Child'
db.put(@model.new(remove_model_key(item)))
else
raise 'Model must be either a Parent or Child'
end
end
end
end

And(/^we make a batch read call for the following Parent and Child model item keys:$/) do |string|
key_batch = JSON.parse(string, symbolize_names: true)

@batch_read_result = Aws::Record::Batch.read do |db|
key_batch.each do |item_key|
case item_key[:model]
when 'Parent'
db.find(@parent, remove_model_key(item_key))
when 'Child'
db.find(@model, remove_model_key(item_key))
else
raise 'Model must be either a Parent or Child'
end
end
end
end

Then(/^we expect the batch read result to include the following items:$/) do |string|
expected = JSON.parse(string, symbolize_names: true)
actual = @batch_read_result.items.map(&:to_h)
expect(expected.count).to eq(actual.count)
expect(expected.all? { |e| actual.include?(e) }).to be_truthy
end

private

def remove_model_key(item)
item.delete(:model)
item
end
14 changes: 2 additions & 12 deletions lib/aws-record.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
# Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
# use this file except in compliance with the License. A copy of the License is
# located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions
# and limitations under the License.
# frozen_string_literal: true

require 'aws-sdk-dynamodb'
require_relative 'aws-record/record/client_configuration'
Expand All @@ -30,6 +19,7 @@
require_relative 'aws-record/record/version'
require_relative 'aws-record/record/transactions'
require_relative 'aws-record/record/buildable_search'
require_relative 'aws-record/record/batch_read'
require_relative 'aws-record/record/batch_write'
require_relative 'aws-record/record/batch'
require_relative 'aws-record/record/marshalers/string_marshaler'
Expand Down
Loading

0 comments on commit 2c13263

Please sign in to comment.