⏲️

[PostgreSQL] pg_cronという拡張機能を使ってみた

に公開

最初に

マテビューのリフレッシュで当初バッチを想定していたところ、
pg_cronという拡張機能の存在を知り、初めて導入してみました。

今回はpg_cronの概要と、設定手順を備忘録として記事に残していきたいと思います。

では、始めます。

pg_cronとは

一言で表すと、「PostgreSQL自身が、定期実行してくれる仕組み」のことです。

名前の通りですね。

普段、定期処理というとLinuxcronやバッチを使用した実行を思い浮かべがちですが、
pg_cronを使うと「データベース自身がスケジューラになる」のが大きな特徴です。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/PostgreSQL_pg_cron.html

どんなことができるのか

スケジュールに基づいた定期実行の役割を担ってくれるので、

  • 毎日特定の時間に 集計用マテリアライズドビューをリフレッシュ
  • 1時間おきに古いデータを削除
  • 毎分ステータス更新用のSQLを実行

といったことが可能となります。

また、pg_cronは実行するSQLを指定する方式となるので、基本的にはクエリベースである点も魅力の一つです。

つまり、特定の操作しかできないよといった制約は現状ありません。

良い点だけではなさそう

実際に導入する過程で、権限周りの設定が少々面倒、導入までの設定が少々多い等、色々とデメリットとなりうることはいくつかありました。

しかし、導入まもないこともあり、運用上でのデメリットとなりうる経験はしておりませんので、AIくんに聞いたところ、特に気をつけなさいよと言われたことがありました。

以下で紹介します。

メジャーバージョンアップへの対応

これを聞いた時に、「RDSはマネージドサービスなんだから気にする必要なくない?」と考えましたが、
公式の記事を漁っていると、こんなのを発見しました。

A PostgreSQL engine upgrade doesn't upgrade most PostgreSQL extensions. To update an extension after a version upgrade, use the ALTER EXTENSION UPDATE command.

https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.ExtensionUpgrades.html?utm_source=chatgpt.com

つまり、エンジン自体のアップデートが行われても、自動で拡張機能もアップデートするわけではないよといった内容です。

pg_cron自体が含まれているかまでは特定できていませんが、少なくともメジャーバージョンのアップデートの際には、別途以下のコマンドを叩く必要がありそうです。

ALTER EXTENSION extension_name UPDATE TO 'new_version';

また、上記SQLを実行したとしても、実際に動作するかまでは確認しておく必要がありそうですね。
※ 実際の動作確認は、cron.job_run_detailsというテーブルを閲覧することで可能です。

設定

今回、ローカルではdocker、dev環境ではRDSを使用しているので、それぞれの設定方法を残しておこうと思います。

ローカル(compose.yml使用)

  • イメージにpg_cronをインストールする
FROM postgres:16.2

RUN apt update && apt install -y \
    postgresql-16-cron
  • compose.ymlのcommandpg_cronが使用できる下準備をする
build: docker/postgres
    command: postgres -c shared_preload_libraries=pg_cron -c cron.database_name=db_name
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_INITDB_ARGS: --encoding=UTF-8 --locale=C
      LANG: ja_JP.utf8
      TZ: Asia/Tokyo
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./docker/postgres/script:/docker-entrypoint-initdb.d
  • イメージを立ち上げる際に CREATE EXTENSION IF NOT EXISTS pg_cron;を適用させる
    • 私の場合、上記vloumes2つ目に定義してある通り、特定のフォルダに初期構築用のsqlファイルを用意し、マウントすることで実行するようにしています。
  • イメージ起動後、以下SQLを実行し、スケジューリングを行います。
    • 私の場合、ORMのmigrate機能を使用して適用するようにしています。
SELECT cron.schedule(
  'refresh_test_materialized_view',
  '0 18 * * *',
  $$REFRESH MATERIALIZED VIEW test_materialized_view ;$$
);
  • 登録したスケジュールの有無を確認
    • cronというスキーマが作成されているはずなので以下のsqlの実行で閲覧できます。
SELECT * FROM cron.job;
  • 上記確認後、時間通りに実行されていることの確認
SELECT * FROM cron.job_run_details;

RDS

RDSは基本的に拡張機能は全て備わった状態です。
そのため、ローカルのようにインストールの操作自体は省略できます。

  • pg_cronが使用できる下準備をする

    • ローカルの説明で、compose.ymlのcommandに定義した内容を、RDSでも適用させます。
    • 方法として、DBインスタンスのパラメータに以下の手順で設定します。
      • shared_preload_libraries=pg_cronを追加
        • すでに他のライブラリが定義されていたら以下のようにしてください。
          • shared_preload_libraries=other_library,pg_cron
      • cron.database_name=db_nameを追加
      • DB再起動
        • 今回編集するパラメータのタイプがStaticなので再起動必須となります。
  • 有効化・スケジューリングの適用

    • CI/CDで自動化している場合は、必要に応じて以下のsqlを適用させるようにしてください。
    • 私の場合は、ORMのmigrateに書いてマージしたら実行されるような仕組みにしてます。
# 有効化
CREATE EXTENSION IF NOT EXISTS pg_cron;

# スケジューリング
SELECT cron.schedule(
  'refresh_test_materialized_view',
  '0 18 * * *',
  $$REFRESH MATERIALIZED VIEW test_materialized_view ;$$
);
  • 登録したスケジュールの有無を確認(ローカルと同じ)
  • 上記確認後、時間通りに実行されていることの確認(ローカルと同じ)

まとめ

今回は、pg_cronという拡張機能について解説をしました。

実際に使ってみて、SQLのみで済むことならバッチを実装する必要がないのはとても便利だと感じました。

一方で、今回はマテビューのリフレッシュのみに適用するといった内容でしたが、デメリットにも挙げた通り、メジャーバージョンアップへの対応についてはある程度考慮する必要があるので、何でもかんでもpg_cronに全て任せてしまうというのは少し不安が残る印象も受けました。

今回の記事がお役にたてたら幸いです。

NCDC テックブログ

Discussion