🔒

DynamoDB の Scan/Query 関係をテストする時に他の DynamoDB に対する処理が走ってほしくない

2023/10/07に公開

ふとしです。

一覧関係のテストをしている時に他のテストでレコードが作成されてワヤになること、ありますよねえ。直列でテストを実行する環境なら大丈夫なんですが、最近は並列実行のものも多いので、大体大丈夫じゃありません。

Rust の cargo test も並列で走るので、同じテーブルに対して PutItem と Scan/Query のテストが同時に走ると、成功したり失敗したりマチマチな結果になって、困ってしまうわけです。

同時に一つのクライアントしかテストしないようにする

ごく単純にはクライアントが同時に一つしか存在しなければ、異なる文脈のテストからのアクセスがなくなり大丈夫になります。

というわけで、同時テストを制限するためにクライアント作成時にロックを取得して他のクライアントが作成されないようにして、クライアントドロップ時にロックを開放するようにします。

static LOCK: once_cell::sync::Lazy<async_std::sync::Mutex<()>> =
    once_cell::sync::Lazy::new(|| async_std::sync::Mutex::new(()));

pub struct DbForScanTest<'a> {
    mutex_guard: async_std::sync::MutexGuard<'a, ()>,
    dynamodb: aws_sdk_dynamodb::Client,
}

impl<'a> DbForScanTest<'a> {
    pub async fn new() -> DbForScanTest<'a> {
        Self {
            mutex_guard: LOCK.lock().await,
            dynamodb: new_test_dynamo_client().await,
        }
    }
}

// 以下はローカルの DynamoDB のクライアント作成なので今回は関係ないです

async fn create_dynamo_sdk_config() -> aws_config::SdkConfig {
    let region = aws_sdk_dynamodb::config::Region::new("us-west-2");
    let credentials = aws_credential_types::Credentials::new("dummy", "dummy", None, None, "test");
    let credentials_provider =
        aws_credential_types::provider::SharedCredentialsProvider::new(credentials);

    aws_config::SdkConfig::builder()
        .credentials_provider(credentials_provider)
        .region(region)
        .build()
}

async fn new_test_dynamo_client() -> aws_sdk_dynamodb::Client {
    let sdk_config = create_dynamo_sdk_config().await;
    let config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
        .endpoint_url("http://localhost:8080")
        .build();

    aws_sdk_dynamodb::Client::from_conf(config)
}

あとは Scan 関係のテストではこれ経由で DynamoDB のクライアントを使うだけです。

ただしすべてのテストをこれ経由で行うと全直列になって、テストの完了が遅くなります。これは嬉しくないので Scan テスト用とその他のテスト用に 2 重にテーブルを作るとよいでしょう。

注意

ロックの開放は DbForScanTest のドロップ時に行われるので、以下のようなことをするとデッドロックとなります。

let db_a = DbForScanTest::new().await;
let db_b = DbForScanTest::new().await;

Discussion