この記事について
この記事は、local環境でRails・DynamoDB・ORMとしてDynamoidを使用してみた際のメモです。
さいごにでも書いてありますが、この記事はこれからも追記予定です。
前提
- Docker/docker-composeを使用します
- macOSでのみ動作確認を行なっています
環境構築
RailsとDynamoDBをdocker-composeで起動する
RailsとDynamoDBをlocal環境に構築するスクリプトを作成しました。このスクリプトを実行すると開発環境が立ち上がります。スクリプトの中では、docker-compose.ymlなどのファイルの生成、コンテナの立ち上げ、rails new
などを記述しています。
docker-compose.ymlの中身は、以下のとおりです。
services:
dynamo-demo-db:
command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
image: amazon/dynamodb-local:latest
container_name: dynamo-local
ports:
- "8000:8000"
volumes:
- "./docker/dynamodb:/home/dynamodblocal/data"
working_dir: /home/dynamodblocal
app:
build:
context: .
dockerfile: Dockerfile
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -b 0.0.0.0"
volumes:
- .:/app
ports:
- 3000:3000
volumes:
dynamodb-data:
networks:
default:
name: dynamo-demo-network
DynamoDBをDockerで動かすにあたって、こちらを参考にしています。
Dynamoidを導入する
開発環境の準備ができたら、Gemfileに"aws-sdk-dynamodb"
と"dynamoid"
を追加します。
gem "aws-sdk-dynamodb"
gem "dynamoid"
bundle install
を実行します。
% docker-compose exec app bundle install
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Fetching aws-eventstream 1.3.0
Fetching aws-partitions 1.1050.0
Fetching jmespath 1.6.2
Installing aws-eventstream 1.3.0
Fetching aws-sigv4 1.11.0
Installing aws-partitions 1.1050.0
Installing jmespath 1.6.2
Installing aws-sigv4 1.11.0
Fetching aws-sdk-core 3.218.1
Installing aws-sdk-core 3.218.1
Fetching aws-sdk-dynamodb 1.136.0
Installing aws-sdk-dynamodb 1.136.0
Fetching dynamoid 3.11.0
Installing dynamoid 3.11.0
Bundle complete! 20 Gemfile dependencies, 114 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
1 installed gem you directly depend on is looking for funding.
Run `bundle fund` for details
initializerを定義します。
Dynamoid.configure do |config|
config.namespace = "dynamoid_demo"
config.region = "us-east-1"
config.access_key = "Dummy"
config.secret_key = "Dummy"
config.endpoint = "http://dynamo-demo-db:8000"
end
User modelを定義する
次に、DynamoDBに記録したいmodelを定義します。今回は、Userモデルを定義します。通常のRailsアプリケーションと異なり、ApplicationRecord
を継承せず、Dynamoid::Document
をinclude
します。また、tableやfieldについても定義します。
class User
include Dynamoid::Document
table name: :users, key: :id
field :name, :string
end
rails consoleからUserモデルを操作してみる
User.create!
rails consoleでUser.create!
をしてみます。実行すると以下のようにログが出力されました。
app(dev)> User.create!(name: 'first user')
[Aws::DynamoDB::Client 200 0.317321 0 retries] list_tables(exclusive_start_table_name:nil)
(399.08 ms) LIST TABLES
(399.64 ms) CACHE TABLES
Creating dynamoid_demo_users table. This could take a while.
[Aws::DynamoDB::Client 200 0.059265 0 retries] create_table(table_name:"dynamoid_demo_users",key_schema:[{attribute_name:"id",key_type:"HASH"}],attribute_definitions:[{attribute_name:"id",attribute_type:"S"}],billing_mode:"PROVISIONED",provisioned_throughput:{read_capacity_units:100,write_capacity_units:20})
(62.79 ms) CREATE TABLE
[Aws::DynamoDB::Client 200 0.03618 0 retries] put_item(table_name:"dynamoid_demo_users",item:{"name"=>{s:"first user"},"id"=>{s:"824bf1cd-5da9-42d0-87e6-aff658152fe6"},"created_at"=>{n:"1739450865.216913975"},"updated_at"=>{n:"1739450865.217141442"}},expected:{"id"=>{exists:false}})
(39.91 ms) PUT ITEM - ["dynamoid_demo_users", {name: "first user", id: "824bf1cd-5da9-42d0-87e6-aff658152fe6", created_at: 0.1739450865216913975e10, updated_at: 0.1739450865217141442e10}, {}]
=> #<User id: "824bf1cd-5da9-42d0-87e6-aff658152fe6", name: "first user", created_at: Thu, 13 Feb 2025 12:47:45 +0000, updated_at: Thu, 13 Feb 2025 12:47:45 +0000>
Creating dynamoid_demo_users table
と書かれたログがあります。このとき、create_table
が実行されており、存在しないtableを自動で作ってくれているようです。
また、CREATE TABLE
が完了したあとにはput_item
が実行されています。
User.first
app(dev)> User.first
(0.19 ms) SCAN - ["dynamoid_demo_users", [{}]]
[Aws::DynamoDB::Client 200 0.077663 0 retries] describe_table(table_name:"dynamoid_demo_users")
[Aws::DynamoDB::Client 200 0.033125 0 retries] scan(table_name:"dynamoid_demo_users",limit:1)
=> #<User id: "824bf1cd-5da9-42d0-87e6-aff658152fe6", name: "first user", created_at: Thu, 13 Feb 2025 12:47:45 +0000, updated_at: Thu, 13 Feb 2025 12:47:45 +0000>
ActiveRecordと同様にUser.first
で先ほど作成したUserを取得できました。
field定義を追加する
Userモデルにage fieldを追加します。
class User
include Dynamoid::Document
table name: :users, key: :id
field :name, :string
+ field :age, :integer
end
fieldを追加したあと、User.craete!
を実行するとage
も一緒に記録できました。RDBみたいにmigrateを実行しなくてもスキーマを変更できるのは非常に便利ですね!
app(dev)> User.create!(name: 'second user', age: 10)
[Aws::DynamoDB::Client 200 0.03165 0 retries] put_item(table_name:"dynamoid_demo_users",item:{"name"=>{s:"second user"},"age"=>{n:"10"},"id"=>{s:"c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd"},"created_at"=>{n:"1739460583.365408009"},"updated_at"=>{n:"1739460583.366512404"}},expected:{"id"=>{exists:false}})
(33.71 ms) PUT ITEM - ["dynamoid_demo_users", {name: "second user", age: 10, id: "c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd", created_at: 0.1739460583365408009e10, updated_at: 0.1739460583366512404e10}, {}]
=> #<User id: "c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd", name: "second user", age: 10, created_at: Thu, 13 Feb 2025 15:29:43 +0000, updated_at: Thu, 13 Feb 2025 15:29:43 +0000>
field定義を削除する
今度は逆に、Userモデルからage fieldを削除します。
class User
include Dynamoid::Document
table name: :users, key: :id
field :name, :string
- field :age, :integer
end
field定義を削除すると、以下のように、Userインスタンスの属性からage
が消えていました。
app(dev)> User.find_by_id("c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd")
DEPRECATION WARNING: [Dynamoid] .find_by_id is deprecated! Call .find instead of (called from <main> at (app):60)
[Aws::DynamoDB::Client 200 0.033229 0 retries] get_item(table_name:"dynamoid_demo_users",key:{"id"=>{s:"c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd"}},consistent_read:nil)
(36.62 ms) GET ITEM - ["dynamoid_demo_users", "c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd", {}]
=> #<User id: "c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd", name: "second user", created_at: Thu, 13 Feb 2025 15:29:43 +0000, updated_at: Thu, 13 Feb 2025 15:29:43 +0000>
また、age
を呼び出すとNoMethodError
が発生しました。
app(dev)> User.find_by_id("c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd").age
DEPRECATION WARNING: [Dynamoid] .find_by_id is deprecated! Call .find instead of (called from <main> at (app):61)
[Aws::DynamoDB::Client 200 0.011879 0 retries] get_item(table_name:"dynamoid_demo_users",key:{"id"=>{s:"c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd"}},consistent_read:nil)
(15.01 ms) GET ITEM - ["dynamoid_demo_users", "c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd", {}]
(app):61:in '<main>': undefined method 'age' for an instance of User (NoMethodError)
「field定義を削除 = テーブルからも削除されたのか?」と気になったので、再度field定義を追加します。
class User
include Dynamoid::Document
table name: :users, key: :id
field :name, :string
+ field :age, :integer
end
reload!
してからUserインスタンスに対してage
を呼び出すと、create!
で指定していた10が返ってきました。
app(dev)> reload!
Reloading...
=> nil
app(dev)> User.find_by_id("c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd").age
DEPRECATION WARNING: [Dynamoid] .find_by_id is deprecated! Call .find instead of (called from <main> at (app):63)
[Aws::DynamoDB::Client 200 0.010293 0 retries] get_item(table_name:"dynamoid_demo_users",key:{"id"=>{s:"c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd"}},consistent_read:nil)
(11.86 ms) GET ITEM - ["dynamoid_demo_users", "c0b4b075-7f7a-4ea5-8de0-f44d0de87dbd", {}]
=> 10
さいごに
ここまで、Dynamoidをつかってみたメモを残しました。もっとDynamoidを使う予定なので、わかったことがあればこの記事に追記します。