LoginSignup
9
7

More than 3 years have passed since last update.

Aurora Serverless が起きるまで待つ

Last updated at Posted at 2020-03-23

例えば AWS Lambda + Amazon CloudWatch Events で定期的にどこかからデータをひっこ抜いてきて適当に Amazon RDS にでも突っ込んでおくかという場合に、普通の Provisioned インスタンスだと少なくとも数千円/月はかかってしまうが Aurora Serverless にすれば使っていないときは勝手に寝てくれるので格安で済むのではないか。1 2

ただしこのアイデアにはひとつ問題があって、Aurora Serverless が起きるのが遅いということ。

DB クライアントから接続を試みると寝ていた Aurora Serverless は起動し始めるが、接続を受け入れられるようになるまでの間にクライアントがタイムアウトしてしまうなんてこともある。

そこで、使うときはあらかじめ Aurora Serverless を起動 (Capacity を増加) させるリクエストを送っておいて、起動した (Capacity が 0 以上になった) タイミングで DB クライアントから接続すれば良いのではないか。

Lambda Function の実装

Aurora Serveless が起きるまで待って後は何もしないという Lambda Function を実装してみる。

ランタイムは Python 3.8 を使った。

import asyncio
import boto3


def is_aurora_serverless_up(client, identifier: str) -> bool:
    """Aurora Serverless が起動中かどうかを返す"""
    response = client.describe_db_clusters(DBClusterIdentifier=identifier)
    assert response['ResponseMetadata']['HTTPStatusCode'] == 200
    assert len(response['DBClusters']) > 0
    assert response['DBClusters'][0]['EngineMode'] == 'serverless'
    return response['DBClusters'][0]['Capacity'] > 0


async def wake_aurora_serverless_up(client, identifier: str, capacity: int = 2):
    """Aurora Serverless を起動する"""
    if is_aurora_serverless_up(client, identifier):
        return
    response = client.modify_current_db_cluster_capacity(DBClusterIdentifier=identifier, Capacity=capacity)
    assert response['ResponseMetadata']['HTTPStatusCode'] == 200
    for i in range(10):
        await asyncio.sleep(i ** 2)
        if is_aurora_serverless_up(client, identifier):
            return
    raise TimeoutError()


async def main():
    client = boto3.client('rds')
    await wake_aurora_serverless_up(client, 'mycluster')


def lambda_handler(event, context):
    asyncio.get_event_loop().run_until_complete(main())

あとやる必要があること。

  • 実行するために rds:DescribeDBClustersrds:ModifyCurrentDBClusterCapacity の権限が必要なのでそれっぽい IAM Role を作成して Lambda Function に割り当てておく
  • Lambda Function のタイムアウト設定のデフォルト値 3秒 だと絶対に終わらないので 3分 とかに設定しておく

今回は asyncio で実装しているが asyncio.sleep で使っているだけなのであまり意味はない。普通に time.sleep でもいい。asyncio で実装しておくと PostgreSQL クライアントとして asyncpg が選べるというメリットはある。

実行してみる

試しに実行してみると以下のようなことがわかる。

  • Aurora Serverless が起動するまでに 15秒 くらいかかる
  • 起動した (capacity > 0 になった) としてもすぐに DB クライアントから接続できるわけではなく、そこから更に 10 秒くらい待つ。

結局待つので前とあまり変わっていないが、DB クライアントがタイムアウトしなくなったのでこれはこれでよし。

最後に

ここまでやってから「これ普通に DB クライアントの接続タイムアウトを長めに設定しておくだけで解決したのでは?????」ということに気づいてしまった。

import psycopg2

with psycopg2.connect('postgresql://...', connect_timeout=120) as conn:
    ...

(psycopg2 の場合) これで問題なく動いてしまった。

エッ

あっ、はい・・・。

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7