1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NestJSによるLambdaとRDS Proxyのピン留め回避とプロキシ分割

Posted at

問題

Lambda + RDS Proxy + Aurora構成のアプリケーションで、Lambdaの同時接続数がたびたび上限(1000)を突破することがありました。サービスクォータで同時接続数の上限を引き上げてみたものの、Auroraの接続上限(db.r6g.xlargeで2000)を超える設定をしても意味がないので、根本的な解決にはなりませんでした。

RDS Proxyのメトリクスをみたところ、DB接続上限の80 %(= 1600)に設定しているはずなのに4000を超えるほどのDatabaseConnectionsがありました。あきらかにおかしい挙動だったので、ピン留めを疑いました。

ピン留めによる接続数増加の原因

RDS Proxyのドキュメントをみたら以下の記載がありました。

ステートメントのテキストサイズが 16 KB を超える場合、プロキシはセッションを現在の接続に固定します。

直近で追加したAPIが怪しいと感じ、内部のDB処理を調べたらほとんどのクエリのテキストサイズが100 KBを超えていました。これがピン留めの原因でした。

改善案

最近のトラフィック増加によってLambdaの使用に限界を感じてはいたのですが、置き換え先のECS on Fargateのスペックやタスク数のチューニングがまだ完了していないため、すぐに移行するのは難しい状況でした。そのため、問題のAPIが使用するDB接続のみを別のRDS Proxyに振り分けることで、ECS on Fargate移行までのつなぎとしての短期的な改善を図ることにしました。

実装手順

1. 新しいRDS Proxyの作成

問題のAPI用に新しいRDS Proxyを作成し、別の接続情報として設定します。これにより、特定のリポジトリでのみ新しいプロキシを使用し、他の処理には既存のプロキシを使用できるようにします。

2. NestJSの設定変更

NestJSのTypeOrmModule.forRootAsyncを利用し、2つのデータソースを設定します。既存のデータソースをデフォルトのまま使用し、新しいRDS Proxy用のデータソースをsecondDataSourceとして追加します。

// NOTE: 既存のDB接続
TypeOrmModule.forRootAsync({
  imports: [ConfigModule],
  dataSourceFactory: async (options: DataSourceOptions) => {
    const AppDataSource = new DataSource(options);
    return await AppDataSource.initialize();
  },
  useFactory: async (configService: ConfigService) => {
    const env = configService.get<string>('ENV') ?? 'local';
    const dbConfig = { ...dbConfigs[env] };
    return dbConfig;
  },
  inject: [ConfigService]
}),
// NOTE: 新RDS Proxy用のデータソースを追加
TypeOrmModule.forRootAsync({
  name: 'secondDataSource', // 新プロキシ用の名前を設定
  imports: [ConfigModule],
  dataSourceFactory: async (options: DataSourceOptions) => {
    const SecondDataSource = new DataSource(options);
    return await SecondDataSource.initialize();
  },
  useFactory: async (configService: ConfigService) => {
    const env = configService.get<string>('ENV') ?? 'local';
    const dbConfig = { ...secondDbConfigs[env] };
    return dbConfig;
  },
  inject: [ConfigService]
}),

3. DB設定の追加

2つのプロキシ用の接続設定をそれぞれ用意し、dbConfigssecondDbConfigsとして保持します。

export const dbConfigs: DBConfigs = {
  dev: {
    type: 'mysql',
    host: '<endpoint>',
    port: 3306,
    username: '<username>',
    password: '<password>',
    database: '<database>',
    entities,
    synchronize: false
  }
};

export const secondDbConfigs: DBConfigs = {
  dev: {
    type: 'mysql',
    host: '<endpoint>',
    port: 3306,
    username: '<username>',
    password: '<password>',
    database: '<database>',
    entities,
    synchronize: false
  }
};

4. 特定のリポジトリで新プロキシを使用

特定のリポジトリプロバイダで新しいプロキシを使用するため、getDataSourceToken('secondDataSource')を使って新しいデータソースを指定します。既存のプロキシを使うリポジトリでは、getDataSourceToken()のままにすることでデフォルト設定が使用されます。

export const SecondRepositoryProvider: FactoryProvider<ISecondRepository> = {
  provide: SecondRepositoryToken,
  useFactory: factory,
  inject: [getDataSourceToken('secondDataSource')]
}

結果

この変更により、ピン留めが発生していたクエリは新しいプロキシにルーティングされるため、既存のRDS ProxyでのDatabaseConnectionsが適切に抑えられるようになりました。Lambdaの同時接続数も安定し、Auroraの接続上限を超えることなく一旦運用ができるようになりました。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?