Amazon ElastiCacheのハンズオンをServerlessに変更してみる

記事タイトルとURLをコピーする

この記事は約2分で読めます。

この記事は1年以上前に書かれたものです。
内容が古い可能性がありますのでご注意ください。

はじめに

こんにちは、荒堀です。

ElastiCacheの公式のハンズオンがあります。今回はこれをAmazon ElastiCache Serverlessでやってみます。

aws.amazon.com

このハンズオンは、RDSのデータをキャッシュするというシナリオになっています。 しかし少し試したいだけでRDSを作るのは大仰なので、今回はCloud9の中にSQLiteでデータベースを作ってそれを使います。

概要

  • 利用するサービスは以下です
    • Amazon ElastiCache Serverless
    • AWS Cloud9
      • この中にSQLiteでデータベースを作ります
  • VPCは、デフォルトVPCを使います
    • ElastiCacheにデフォルトのセキュリティグループを宛がうので、インバウンドルールを追加します

参考

blog.serverworks.co.jp

やってみた

環境作成

以前の記事と同じ環境を作ります。

blog.serverworks.co.jp

  1. Cloud9の起動
  2. ElastiCache Serverlessを作成
  3. セキュリティグループにインバウンドルールを追加

Cloud9にデータベース作成

ハンズオンでRDSに作成しているデータベースを、Cloud9上のSQLiteで作成します。 ディレクトリを作成し、seed.sqlで空のSQLファイルを作成します。

mkdir demo-elasticacheserverless && cd demo-elasticacheserverless

touch seed.sql

seed.sqlに、テーブルを作成・データを登録するSQLを記述します。

CREATE TABLE planet (
id integer primary key autoincrement,
name VARCHAR(30)
);
INSERT INTO planet (name) VALUES ("Mercury");
INSERT INTO planet (name) VALUES ("Venus");
INSERT INTO planet (name) VALUES ("Earth");
INSERT INTO planet (name) VALUES ("Mars");
INSERT INTO planet (name) VALUES ("Jupiter");
INSERT INTO planet (name) VALUES ("Saturn");
INSERT INTO planet (name) VALUES ("Uranus");
INSERT INTO planet (name) VALUES ("Neptune");

SQLiteを実行し、SQLファイルを実行します。

sqlite3 tutorial.sqlite

sqlite> .read seed.sql

sqlite> SELECT * FROM planet;

Python実行

以下のサンプルコードを参考にしました。

github.com

redisをインストールして、Pythonを実行します。

pip install redis

python

まずは必要なパッケージをインポートします。

import json
import redis
import sqlite3

データベースクラスを作ります。dict_factoryは、SQLiteの戻り値を辞書型にする関数です。指定しないとtuple型になり、Redisの.hmsetで扱えなくなります。

# 戻り値を辞書型にする関数
def dict_factory(cursor, row):
d = {}
for idx,col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
# SQLiteを扱うクラス
class DB:
def __init__(self, dbname):
self.sqlite = sqlite3.connect(dbname)
self.sqlite.row_factory = dict_factory
def query(self, sql):
cursor = self.sqlite.cursor()
try:
cursor.execute(sql)
return cursor.fetchall()
finally:
cursor.close()
def record(self, sql, values):
cursor = self.sqlite.cursor()
try:
cursor.execute(sql, values)
return cursor.fetchone()
finally:
cursor.close()

次に、SQLiteとElasticacheに接続します。

# Time to live for cached data
TTL = 10
# Initialize the database
Database = DB('tutorial.sqlite')
# Initialize the cache
Cache = redis.RedisCluster(
host='xxx.cache.amazonaws.com'(ElastiCacheのエンドポイント)
, ssl=True
, decode_responses=True
)

redis.RedisClusterdecode_responses=Trueは、.hgetallの戻り値をバイト型にしないため指定します。

fetch関数を定義して試してみます。

def fetch(sql):
"""Retrieve records from the cache, or else from the database."""
res = Cache.get(sql)
if res:
print("キャッシュあり")
return json.loads(res)
print("キャッシュなし")
res = Database.query(sql)
Cache.setex(sql, TTL, json.dumps(res))
return res

何回か実行してみます。

>>> print(fetch("SELECT * FROM planet"))
キャッシュなし
[{'id': 1, 'name': 'Mercury'}, {'id': 2, 'name': 'Venus'}, {'id': 3, 'name': 'Earth'}, {'id': 4, 'name': 'Mars'}, {'id': 5, 'name': 'Jupiter'}, {'id': 6, 'name': 'Saturn'}, {'id': 7, 'name': 'Uranus'}, {'id': 8, 'name': 'Neptune'}]
>>> print(fetch("SELECT * FROM planet"))
キャッシュあり
[{'id': 1, 'name': 'Mercury'}, {'id': 2, 'name': 'Venus'}, {'id': 3, 'name': 'Earth'}, {'id': 4, 'name': 'Mars'}, {'id': 5, 'name': 'Jupiter'}, {'id': 6, 'name': 'Saturn'}, {'id': 7, 'name': 'Uranus'}, {'id': 8, 'name': 'Neptune'}]
>>> print(fetch("SELECT * FROM planet"))
キャッシュあり
[{'id': 1, 'name': 'Mercury'}, {'id': 2, 'name': 'Venus'}, {'id': 3, 'name': 'Earth'}, {'id': 4, 'name': 'Mars'}, {'id': 5, 'name': 'Jupiter'}, {'id': 6, 'name': 'Saturn'}, {'id': 7, 'name': 'Uranus'}, {'id': 8, 'name': 'Neptune'}]

次はplanet関数を定義します。

def planet(id):
"""Retrieve a record from the cache, or else from the database."""
key = f"planet:{id}"
res = Cache.hgetall(key)
if res:
print("キャッシュあり")
return res
print("キャッシュなし")
sql = "SELECT `id`, `name` FROM `planet` WHERE `id`=?"
res = Database.record(sql, (id,))
if res:
Cache.hmset(key, res)
Cache.expire(key, TTL)
return res

何回か実行してみます。idが文字列型になってしまっているのは残念ですが、キャッシュに入っていることが確認できます。

>>> print(planet(3))
キャッシュなし
{'id': 3, 'name': 'Earth'}
>>> print(planet(3))
キャッシュあり
{'name': 'Earth', 'id': '3'}
>>> print(planet(3))
キャッシュあり
{'name': 'Earth', 'id': '3'}

おわりに

実装されたAmazon ElastiCache Serverlessを、ハンズオンで使ってみました。 サーバレスでも元のElastiCacheでも、使用感は変わらないと感じました。すぐに起動する分、サーバレスの方がいいかもしれません。

この記事がどなたかのお役に立てれば幸いです。