From 3b606bf89ee0f8b1845afd9684dd0e5a44b22070 Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Sun, 3 Nov 2024 14:52:39 +0200 Subject: [PATCH] Move the documentation for transactions into the main README.md --- README.md | 134 ++++++++++++++++++++++++++++++++++++++++++++- README_transact.md | 133 -------------------------------------------- 2 files changed, 131 insertions(+), 136 deletions(-) delete mode 100644 README_transact.md diff --git a/README.md b/README.md index d8e937ee..029e8458 100644 --- a/README.md +++ b/README.md @@ -1046,10 +1046,138 @@ resolving the fields with a second query against the table since a query against GSI then a query on base table is still likely faster than scan on the base table* -### Transaction Writes +### Transactions in Dynamoid -Multiple write actions can be grouped together and submitted as an all-or-nothing operation. -See the [transation documentation](README_transact.md). +Multiple modifying actions can be grouped together and submitted as an +all-or-nothing operation. Atomic modifying operations are supported in +Dynamoid using transactions. If any action in the transaction fails they +all fail. + +The following actions are supported: + +* `#create` - add a new model if it does not already exist +* `#save` - create or update model +* `#update_attributes` - modifies one or more attributes from an existig + model +* `#delete` - remove an model without callbacks nor validations +* `#destroy` - remove an model +* `#upsert` - add a new model or update an existing one, no callbacks +* `#update_fields` - update a model without its instantiation + +These methods are supposed to behave exactly like their +non-transactional counterparts. + + +#### Create models + +Models can be created inside of a transaction. The partition and sort +keys, if applicable, are used to determine uniqueness. Creating will +fail with `Aws::DynamoDB::Errors::TransactionCanceledException` if a +model already exists. + +This example creates a user with a unique id and unique email address by +creating 2 models. An additional model is upserted in the same +transaction. Upsert will update `updated_at` but will not create +`created_at`. + +```ruby +user_id = SecureRandom.uuid +email = 'bob@bob.bob' + +Dynamoid::TransactionWrite.execute do |txn| + txn.create(User, id: user_id) + txn.create(UserEmail, id: "UserEmail##{email}", user_id: user_id) + txn.create(Address, id: 'A#2', street: '456') + txn.upsert(Address, id: 'A#1', street: '123') +end +``` + +#### Save models + +Models can be saved in a transaction. New records are created otherwise +the model is updated. Save, create, update, validate and destroy +callbacks are called around the transaction as appropriate. Validation +failures will throw `Dynamoid::Errors::DocumentNotValid`. + +```ruby +user = User.find(1) +article = Article.new(body: 'New article text', user_id: user.id) + +Dynamoid::TransactionWrite.execute do |txn| + txn.save(article) + + user.last_article_id = article.id + txn.save(user) +end +``` + +#### Update models + +A model can be updated by providing a model or primary key, and the fields to update. + +```ruby +Dynamoid::TransactionWrite.execute do |txn| + # change name and title for a user + txn.update_attributes(user, name: 'bob', title: 'mister') + + # sets the name and title for a user + # The user is found by id (that equals 1) + txn.update_fields(User, '1', name: 'bob', title: 'mister') +end +``` + +#### Destroy or delete models + +Models can be used or the model class and key can be specified. +`#destroy` uses callbacks and validations. Use `#delete` to skip +callbacks and validations. + +```ruby +article = Article.find('1') +tag = article.tag + +Dynamoid::TransactionWrite.execute do |txn| + txn.destroy(article) + txn.delete(tag) + + txn.delete(Tag, '2') # delete record with hash key '2' if it exists + txn.delete(Tag, 'key#abcd', 'range#1') # when sort key is required +end +``` + +#### Validation failures that don't raise + +All of the transaction methods can be called without the `!` which +results in `false` instead of a raised exception when validation fails. +Ignoring validation failures can lead to confusion or bugs so always +check return status when not using a method with `!`. + +```ruby +user = User.find('1') +user.red = true + +Dynamoid::TransactionWrite.execute do |txn| + if txn.save(user) # won't raise validation exception + txn.update(UserCount, id: 'UserCount#Red', count: 5) + else + puts 'ALERT: user not valid, skipping' + end +end +``` + +#### Incrementally building a transaction + +Transactions can also be built without a block. + +```ruby +transaction = Dynamoid::TransactionWrite.new + +transaction.create(User, id: user_id) +transaction.create(UserEmail, id: "UserEmail##{email}", user_id: user_id) +transaction.upsert(Address, id: 'A#1', street: '123') + +transaction.commit # changes are persisted in this moment +``` ### PartiQL diff --git a/README_transact.md b/README_transact.md deleted file mode 100644 index b983d4d0..00000000 --- a/README_transact.md +++ /dev/null @@ -1,133 +0,0 @@ -# Transactions in Dynamoid - -Multiple modifying actions can be grouped together and submitted as an -all-or-nothing operation. Atomic modifying operations are supported in -Dynamoid using transactions. If any action in the transaction fails they -all fail. - -The following actions are supported: - -* `#create` - add a new model if it does not already exist -* `#save` - create or update model -* `#update_attributes` - modifies one or more attributes from an existig - model -* `#delete` - remove an model without callbacks nor validations -* `#destroy` - remove an model -* `#upsert` - add a new model or update an existing one, no callbacks -* `#update_fields` - update a model without its instantiation - -These methods are supposed to behave exactly like their -non-transactional counterparts. - -## Examples - -### Create models - -Models can be created inside of a transaction. The partition and sort -keys, if applicable, are used to determine uniqueness. Creating will -fail with `Aws::DynamoDB::Errors::TransactionCanceledException` if a -model already exists. - -This example creates a user with a unique id and unique email address by -creating 2 models. An additional model is upserted in the same -transaction. Upsert will update `updated_at` but will not create -`created_at`. - -```ruby -user_id = SecureRandom.uuid -email = 'bob@bob.bob' - -Dynamoid::TransactionWrite.execute do |txn| - txn.create(User, id: user_id) - txn.create(UserEmail, id: "UserEmail##{email}", user_id: user_id) - txn.create(Address, id: 'A#2', street: '456') - txn.upsert(Address, id: 'A#1', street: '123') -end -``` - -### Save models - -Models can be saved in a transaction. New records are created otherwise -the model is updated. Save, create, update, validate and destroy -callbacks are called around the transaction as appropriate. Validation -failures will throw `Dynamoid::Errors::DocumentNotValid`. - -```ruby -user = User.find(1) -article = Article.new(body: 'New article text', user_id: user.id) - -Dynamoid::TransactionWrite.execute do |txn| - txn.save(article) - - user.last_article_id = article.id - txn.save(user) -end -``` - -### Update models - -A model can be updated by providing a model or primary key, and the fields to update. - -```ruby -Dynamoid::TransactionWrite.execute do |txn| - # change name and title for a user - txn.update_attributes(user, name: 'bob', title: 'mister') - - # sets the name and title for a user - # The user is found by id (that equals 1) - txn.update_fields(User, '1', name: 'bob', title: 'mister') -end -``` - -### Destroy or delete models - -Models can be used or the model class and key can be specified. -`#destroy` uses callbacks and validations. Use `#delete` to skip -callbacks and validations. - -```ruby -article = Article.find('1') -tag = article.tag - -Dynamoid::TransactionWrite.execute do |txn| - txn.destroy(article) - txn.delete(tag) - - txn.delete(Tag, '2') # delete record with hash key '2' if it exists - txn.delete(Tag, 'key#abcd', 'range#1') # when sort key is required -end -``` - -## Validation failures that don't raise - -All of the transaction methods can be called without the `!` which -results in `false` instead of a raised exception when validation fails. -Ignoring validation failures can lead to confusion or bugs so always -check return status when not using a method with `!`. - -```ruby -user = User.find('1') -user.red = true - -Dynamoid::TransactionWrite.execute do |txn| - if txn.save(user) # won't raise validation exception - txn.update(UserCount, id: 'UserCount#Red', count: 5) - else - puts 'ALERT: user not valid, skipping' - end -end -``` - -## Incrementally building a transaction - -Transactions can also be built without a block. - -```ruby -transaction = Dynamoid::TransactionWrite.new - -transaction.create(User, id: user_id) -transaction.create(UserEmail, id: "UserEmail##{email}", user_id: user_id) -transaction.upsert(Address, id: 'A#1', street: '123') - -transaction.commit # changes are persisted in this moment -```