0. はじめに
SNSツールを作るにあたり、key-valueストアであるDynamoDBを選択した。これは、大量のデータの中から任意のキーで検索をかけることで、より高速にデータを取得できるDBになっている。今回はRailsアプリを使って実装した内容を書いていく。
1. Dynamo DBの設計
RailsのgemであるDyanmoidを利用して、DynamoDBにデータを保存する。
その際、hasy_keyをtimeline_id
に設定した。hasy_keyはデフォルトでユニークな文字列型となっている。
module Dynamo
class Timeline
include Dynamoid::Document
table name: :timelines, key: :timeline_id, capacity_mode: :on_demand, timestamps: false
field :body, :string
field :created_user_id, :integer
field :process_at, :integer, default: -> { Time.zone.now.to_i }
end
end
2. GSIの設定
DynamoDBではhash_keyに指定した属性以外で検索をかけると、SCAN操作(DB全体の検索)になってしまう。
そのため、QUERY操作(hash_keyでの検索)になるように、GSI(Global Secondary Index)
を設定する。
今回はTOPページで全ての投稿を表示するため、created_user_idなど、固定値のhash_keyが存在しない。そのため、固定値であるSORT_HASH_KEY
をhash_keyとして新たに設定することでQUERY操作を実現した。
なお、range_keyはItemの並び替えに使われる。
SORT_HASH_KEY = 1
field :sort_hash_key, :integer, default: SORT_HASH_KEY
global_secondary_index hash_key: :sort_hash_key, range_key: :process_at,
name: :sort_hash_key_process_at_index, projected_attributes: :all
3. 指定した値分の投稿を取得
まずは、初回リクエストでper_page分だけ、投稿を取得するメソッドを実装。
最後に評価した値(last_evaluated_key)をreturnする必要があるため、find_by_pages
メソッドを使う。
なお、scan_index_forward(false)
メソッドで、range_keyの降順に並び替えられる。
今回は最新の投稿から取得するように実装している。
class << self
def next_page_of_timelines(per_page)
paginate_items(by_sort_hash_key, per_page)
end
private
def by_sort_hash_key
where(sort_hash_key: SORT_HASH_KEY).scan_index_forward(false)
end
def paginate_items(chain, per_page)
chain.record_limit(per_page).find_by_pages.first
end
end
4. もっと見る機能の実装
続いて、2回目以降のリクエスト時には、last_evaluated_key
がparamsとして渡されるので、last_evaluated_key + 1
番目から、per_page分の投稿を取得する。
そのためstart_key
メソッドを活用して、取得する最初の値を特定する。
class << self
def next_page_of_timelines(timeline_id, process_at, per_page)
if timeline_id.blank? && process_at.zero?
paginate_items(by_sort_hash_key, per_page)
else
paginate_items(start_key(by_sort_hash_key, timeline_id, process_at), per_page)
end
end
private
def by_sort_hash_key
where(sort_hash_key: SORT_HASH_KEY).scan_index_forward(false)
end
def start_key(chain, timeline_id, process_at)
chain.start(timeline_id:, process_at:, sort_hash_key: SORT_HASH_KEY)
end
def paginate_items(chain, per_page)
chain.record_limit(per_page).find_by_pages.first
end
end
これで、DynamoDBから任意の値を適切に取得するRails側の実装ができる。
なお、created_user_id
など、固定のhash_keyがあれば、今回のようにSORT_HASH_KEY
のような固定値をわざわざ設ける必要性がなくなる。