aws / aws-record-ruby

Official repository for the aws-record gem, an abstraction for Amazon DynamoDB.
Apache License 2.0
321 stars 42 forks source link

feat(batch_write): Implement Batch Write #119

Closed jessedoyle closed 3 years ago

jessedoyle commented 3 years ago

Hi There!

We've been interested in the batch_write_item operation for a while now and noticed that this library doesn't yet offer support for batch write requests.

There's already been a PR that implements batch write operations in the past (#10), but I believe it was trying to be a bit too "smart", and therefore it was quite complex.

This PR takes an alternate approach - we want to provide a minimal wrapper around the underlying batch_write_item method.

I believe this provides developers the means to perform batch write operations while still granting full control over the handling of failed operations and custom retry strategies.

For simplicity, this implementation does not support dirty attribute tracking, nor does it provide safe write conditions (i.e. update vs. new record checks).

I don't believe that this PR breaks any existing behaviours throughout the library, therefore it should be a backwards-compatible change.

Example Usage

class Planet
  include(Aws::Record)
  integer_attr(:id, hash_key: true)
  string_attr(:name, range_key: true)
end

# setup
pluto = Planet.new(id: 9, name: 'pluto').save!

# perform batch operation
operation = Aws::Record::Batch.write(client: Planet.dynamodb_client) do |db|
  db.put(Planet.new(id: 1, name: 'mercury'))
  db.put(Planet.new(id: 2, name: 'venus'))
  db.put(Planet.new(id: 3, name: 'earth'))
  db.put(Planet.new(id: 4, name: 'mars'))
  db.put(Planet.new(id: 5, name: 'jupiter'))
  db.put(Planet.new(id: 6, name: 'saturn'))
  db.put(Planet.new(id: 7, name: 'uranus'))
  db.put(Planet.new(id: 8, name: 'neptune'))
  db.delete(pluto)
end

# (optional) - retry failed operations
# retry the operation with #execute! - only the unprocessed items will be attempted.
# note: it's a good idea to implement an exponential backoff strategy here
operation.execute! until operation.complete?

Issue #, if available:

resolves: #55

Description of changes:

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

alextwoods commented 3 years ago

Thanks for submitting this -I'll review today/this week!

mullermp commented 3 years ago

Thanks for opening a pull request! This needs some review. We've been kind of slammed for upcoming re-invent. We'll try to review this.

mullermp commented 3 years ago

Jinx... ^

jessedoyle commented 3 years ago

@alextwoods @mullermp - I pushed a commit that should address PR comments. Thanks for the review!

jessedoyle commented 3 years ago

@alextwoods - My last commit implements your suggestions!

I took the liberty of bumping the VERSION constant as it appears to have not been updated recently. I also added a CHANGELOG entry with a usage example.

jessedoyle commented 3 years ago

@alextwoods - Ready for final review! Thanks again.

alextwoods commented 3 years ago

This is looking almost good to go. I was just running the integration tests and got a few failures. I believe they are unrelated, but need to do an investigation. Should be able to dive into them tomorrow morning.

The failures:

Failing Scenarios:
cucumber features/items/item_default_values.feature:18 # Scenario: Write From Default Values
cucumber features/table_config/table_config.feature:260 # Scenario: Transition from Provisioned Billing to PPR
cucumber features/transactions/transactions.feature:61 # Scenario: Get two items in a transaction (global)
cucumber features/transactions/transactions.feature:87 # Scenario: Get two items in a transaction plus one missing (global)
cucumber features/transactions/transactions.feature:108 # Scenario: Get two items in a transaction plus one missing (class)
cucumber features/transactions/transactions.feature:129 # Scenario: Perform a transactional update (global)
cucumber features/transactions/transactions.feature:179 # Scenario: Perform a transactional set of puts and updates (global)
cucumber features/transactions/transactions.feature:236 # Scenario: Perform a transactional update with check (global)
cucumber features/transactions/transactions.feature:274 # Scenario: Perform a transactional update in error (global)

41 scenarios (9 failed, 32 passed)
323 steps (9 failed, 22 skipped, 292 passed)
jessedoyle commented 3 years ago

@alextwoods - I'm curious, is it failing the same way on master? I did try running integration tests a while ago, I'm pretty sure I saw some failures as well.

alextwoods commented 3 years ago

I was able to get all of the integration tests to pass - they're just a bit flakey.

The change looks good - I'm going to approve and merge! Will work on releasing a new version of the gem later today as well.

Thanks again for your great contribution!

jessedoyle commented 3 years ago

Thanks you so much @alextwoods and @mullermp!

mullermp commented 3 years ago

You're welcome, even though it was all @alextwoods !