type holyshared = Engineer<mixed>

技術的なことなど色々

TypeORMでPostgreSQLのコンネクションプールの状態を知る方法

TypeORMでは QueryRunner のプロパティからpgのPool インスタンスにアクセスすることができます。
pgのPool インスタンスからはプールの状態を確認できるプロパティがあります。

  • totalCount - プールにあるコネクションの総数
  • idleCount - コネクションプールにある空きコネクション数
  • waitingCount - 接続待ちのコネクション数

このプロパティをログに出力しておくことで、プールのサイズを調整する目安にすることができます。
QueryRunnerは Repository、EntityManagerなどから参照できるため、ログの出力特に困らないはずです。

import { DataSource } from "typeorm";
import { Pool } from "pg";

const dataSource = new DataSource({
  type: "postgres",
  host: "localhost",
  username: process.env.DB_USERNAME,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  poolSize: 10,
});

const connectionPool = async () => {
  await dataSource.initialize();

  const c1 = await dataSource.createQueryRunner();
  await c1.connect();

  const c2 = await dataSource.createQueryRunner();
  await c2.connect();

  // Dirverインターフェースからmasterプロパティが参照できないので型をanyにして扱う
  const driver = c2.connection.driver as any;
  const pool = driver.master as Pool;

  // pg.Poolのインスタンスからプールの使用状況を計算する
  // https://node-postgres.com/apis/pool
  console.info("totalCount", pool.totalCount);
  console.info("idleCount", pool.idleCount);
  console.info("waitingCount", pool.waitingCount);

  await c1.release();
  await c2.release();

  await dataSource.destroy();
}

connectionPool().then(() => {
  console.info("done");
}).catch(console.error);

おすすめはQueryRunnerをオブジェクトでラップして、コネクションプールに返すタイミングでログに出力する方法です。

オブジェクトでラップしたQueryRunnerのライフサイクルはDIコンテナなどで管理すると、リクエストのレスポンスを返すまでのコネクションを同一のコネクションの固定しやすくなり、利用できるコネクションの枯渇などを防ぐとことができますし、コネクションをプールに返し忘れることも減らすことができます。

class QueryRunnerWrapper {
  constructor(
    private queryRunner: QueryRunner,
    private logger: { info: (...messages: any[]) => void }
  ) {}

  async connect() {
    this.logger.info("[CONN] connect");
    return this.queryRunner.connect();
  }

  async release() {
    const driver = this.queryRunner.connection.driver as any;
    const pool = driver.master as Pool;

    this.logger.info("[CONN] release", 
      "totalCount", pool.totalCount,
      "idleCount", pool.idleCount,
      "waitingCount", pool.waitingCount
    );
    return this.queryRunner.release();
  }
}