Documentation Index

Fetch the complete documentation index at: https://guide.ncloud-docs.com/llms.txt

Use this file to discover all available pages before exploring further.

pg_cron 사용

Prev Next

The latest service changes have not yet been reflected in this content. We will update the content as soon as possible. Please refer to the Korean version for information on the latest updates.

VPC 환경에서 이용 가능합니다.

Cloud DB for PostgreSQL에서 pg_cron extension을 제공하며 아래 사용 가이드를 참고해 주십시오. pg_cron은 PostgreSQL 내부에서 cron 표현식 기반으로 SQL 작업을 예약 실행하는 extension입니다.

콘솔을 통한 extension 설치

Cloud DB for PostgreSQL에서는 superuser 권한을 제공하지 않으므로 콘솔을 통해 superuser 권한이 필요한 extension을 설치할 수 있습니다. 자세한 내용은 아래 가이드를 참조해 주십시오.

pg_cron을 설치할 때는 작업을 예약할 대상 데이터베이스를 선택해 주십시오. 설치 과정에서 DB Server가 재시작되며, HA(고가용성) 구성에서는 Failover 방식으로 서버가 순차적으로 재시작됩니다.

주의
  • pg_cron은 PostgreSQL 클러스터당 1개의 데이터베이스에만 설치할 수 있습니다. 이미 설치된 경우 다른 데이터베이스에 추가로 설치할 수 없습니다.
  • 설치 시 선택한 데이터베이스의 DB owner 계정에만 pg_cron 사용 권한이 부여됩니다. 다른 DB 사용자에게는 권한이 부여되지 않습니다.
  • 따라서 pg_cron은 해당 데이터베이스의 DB owner 계정으로 접속해야만 사용할 수 있습니다.
  • DB owner 계정에는 권한을 다시 부여할 수 있는 권한(GRANT OPTION)이 없으므로, DB owner 계정이 다른 DB 사용자에게 cron 스키마에 대한 권한을 부여(GRANT)할 수 없습니다.

pg_cron 개념

pg_cron을 설치하면 대상 데이터베이스에 cron 스키마가 생성되며, 다음 구성 요소를 통해 작업을 예약하고 관리합니다. Cloud DB for PostgreSQL은 superuser 권한을 제공하지 않으므로 아래 표의 DB owner 계정 사용 항목에 따라 일부 함수는 사용할 수 없습니다.

구분 이름 설명 DB owner 계정 사용
테이블 cron.job 예약된 작업 정의 조회 (본인 작업)
테이블 cron.job_run_details 작업 실행 이력 조회·삭제 (본인 이력)
함수 cron.schedule 작업 예약 사용 가능
함수 cron.unschedule 작업 삭제 사용 가능
함수 cron.schedule_in_database 다른 데이터베이스의 작업 예약 지원하지 않음
함수 cron.alter_job 예약된 작업 변경 지원하지 않음

예약된 작업은 작업을 등록한 사용자의 권한으로 실행됩니다. 등록한 사용자가 접근할 수 없는 테이블에는 작업을 수행할 수 없습니다.

참고
  • Cloud DB for PostgreSQL은 superuser 권한을 제공하지 않으므로, DB owner 계정으로는 cron.schedule(작업 예약), cron.unschedule(작업 삭제)과 cron.job·cron.job_run_details 조회를 사용할 수 있습니다.
  • cron.schedule_in_database(다른 데이터베이스의 작업 예약)와 cron.alter_job(예약된 작업 변경)은 DB owner 계정에 제공되지 않습니다. 호출하면 permission denied for function 오류가 발생합니다. 두 함수는 username 인자로 다른 계정의 권한으로 작업을 실행할 수 있어 보안을 위해 제한됩니다.
  • 예약된 작업의 스케줄이나 명령을 변경하려면 cron.unschedule로 삭제한 후 다시 예약합니다.
  • pg_cron 작업은 Primary 서버에서만 실행됩니다. Failover가 발생하면 새 Primary 서버에서 예약된 작업이 자동으로 이어서 실행됩니다.

작업 예약 및 관리

설치 시 선택한 데이터베이스에 DB owner 계정으로 접속한 후 작업을 예약합니다.

참고
  • Cloud DB for PostgreSQL은 보안을 위해 신규 데이터베이스의 public 스키마 사용을 권장하지 않습니다. 따라서 별도 스키마를 생성하여 사용합니다.
  • 예약 작업의 SQL에는 스키마를 포함한 이름(sales.orders)을 사용하는 것을 권장합니다. 작업은 search_path 설정과 무관하게 동작해야 안전합니다.

작업 예약

cron.schedule(작업이름, 스케줄, 실행할 SQL) 형식으로 예약합니다.

-- 매일 03:00에 sales.orders 테이블의 VACUUM ANALYZE 실행
SELECT cron.schedule('nightly-vacuum-orders', '0 3 * * *', 'VACUUM ANALYZE sales.orders');

-- 매주 일요일 05:00에 90일 이전 주문 삭제
SELECT cron.schedule('purge-old-orders', '0 5 * * 0', $$DELETE FROM sales.orders WHERE ordered_at < now() - interval '90 days'$$);
참고
  • 스케줄은 표준 cron 표현식(분 시 일 월 요일)을 사용합니다.
  • '10 seconds'와 같이 초 단위 간격도 지정할 수 있습니다.
참고
  • pg_cron은 예약된 작업을 백그라운드 워커로 실행합니다(cron.use_background_workerson으로 설정됩니다). 동시에 실행할 수 있는 작업 수는 서버의 백그라운드 워커 수로 제한됩니다.
  • 여러 작업의 실행 시각이 겹치면 동시 실행 한계에 도달할 수 있으므로, 무거운 작업이나 다수의 작업은 실행 시각을 분산하는 것을 권장합니다.
  • 동일한 작업의 이전 실행이 끝나지 않은 경우, 다음 실행은 중복 실행되지 않고 이전 실행이 끝날 때까지 대기합니다.

작업 조회 및 변경

예약된 작업은 cron.job에서 조회합니다. 스케줄이나 명령을 변경할 때는 cron.alter_job을 사용할 수 없으므로, cron.unschedule로 삭제한 후 같은 이름으로 다시 예약합니다.

-- 예약된 작업 조회 (본인이 등록한 작업만 표시)
SELECT jobid, jobname, schedule, command, active FROM cron.job;

-- 스케줄 변경: 기존 작업을 삭제한 후 새 스케줄로 다시 예약합니다.
SELECT cron.unschedule('purge-old-orders');
SELECT cron.schedule('purge-old-orders', '0 6 * * 0', $$DELETE FROM sales.orders WHERE ordered_at < now() - interval '90 days'$$);

-- 작업 삭제
SELECT cron.unschedule('nightly-vacuum-orders');

사용 예제: 일별 매출 집계 적재

매일 새벽에 전날 주문을 집계하여 요약 테이블에 적재하는 예제입니다. 아래 예제는 스키마 및 테이블 생성부터 검증까지 그대로 실행할 수 있습니다. 이 예제에서는 pg_cron을 설치한 데이터베이스를 maindb, 해당 데이터베이스의 DB owner를 mainowner로 가정합니다. 모든 작업은 maindbmainowner 계정으로 접속한 후 등록합니다.

-- maindb 에 mainowner 계정으로 접속합니다.
\c maindb mainowner
-- 1. 사용할 스키마를 생성합니다. (Cloud DB for PostgreSQL은 public 스키마 사용을 권장하지 않으므로 별도 스키마를 사용합니다.)
CREATE SCHEMA sales;

-- 2. 원본 주문 테이블을 생성하고 샘플 데이터를 입력합니다. (운영 중인 테이블이 있다면 생략합니다.)
CREATE TABLE sales.orders (
    order_id   bigserial     PRIMARY KEY,
    amount     numeric(12,2) NOT NULL,
    ordered_at timestamptz   NOT NULL DEFAULT now()
);

INSERT INTO sales.orders (amount, ordered_at) VALUES
    (15000, current_date - 1 + time '09:30'),  -- 어제 주문
    (32000, current_date - 1 + time '13:10'),  -- 어제 주문
    ( 8900, current_date - 1 + time '20:05'),  -- 어제 주문
    (47000, current_date     + time '08:00');  -- 오늘 주문 (어제 집계에는 미포함)

-- 3. 집계 결과를 저장할 요약 테이블을 생성합니다.
CREATE TABLE sales.daily_sales_summary (
    sales_date   date          PRIMARY KEY,
    order_cnt    integer       NOT NULL,
    total_amount numeric(14,2) NOT NULL
);

-- 4. 먼저 동작을 빠르게 검증하기 위해 매분 실행으로 작업을 예약합니다.
--    스키마를 포함한 이름을 사용하며, 같은 날짜로 다시 실행되어도 중복되지 않도록 ON CONFLICT 로 갱신합니다.
SELECT cron.schedule(
    'rollup-daily-sales',
    '* * * * *',
    $$INSERT INTO sales.daily_sales_summary (sales_date, order_cnt, total_amount)
      SELECT current_date - 1, count(*), coalesce(sum(amount), 0)
      FROM sales.orders
      WHERE ordered_at >= current_date - 1
        AND ordered_at <  current_date
      ON CONFLICT (sales_date)
      DO UPDATE SET order_cnt    = EXCLUDED.order_cnt,
                    total_amount = EXCLUDED.total_amount$$
);

-- 5. 1~2분 정도 기다린 후 적재 결과와 작업 실행 이력을 확인합니다.
SELECT sales_date, order_cnt, total_amount
FROM sales.daily_sales_summary
ORDER BY sales_date DESC
LIMIT 10;

SELECT jobid, status, return_message, start_time
FROM cron.job_run_details
WHERE jobid = (SELECT jobid FROM cron.job WHERE jobname = 'rollup-daily-sales')
ORDER BY start_time DESC
LIMIT 5;

-- 6. 정상 동작을 확인했으면 테스트 작업을 삭제하고 운영 스케줄(매일 00:05)로 다시 예약합니다.
--    (cron.alter_job 은 사용할 수 없으므로 unschedule 후 재예약합니다.)
SELECT cron.unschedule('rollup-daily-sales');
SELECT cron.schedule(
    'rollup-daily-sales',
    '5 0 * * *',
    $$INSERT INTO sales.daily_sales_summary (sales_date, order_cnt, total_amount)
      SELECT current_date - 1, count(*), coalesce(sum(amount), 0)
      FROM sales.orders
      WHERE ordered_at >= current_date - 1
        AND ordered_at <  current_date
      ON CONFLICT (sales_date)
      DO UPDATE SET order_cnt    = EXCLUDED.order_cnt,
                    total_amount = EXCLUDED.total_amount$$
);
참고
  • 집계 기준일(current_date)은 세션 및 서버 타임존에 따라 결정됩니다. 타임존에 따라 집계 대상 날짜가 달라질 수 있으므로, 의도한 기간이 집계되는지 확인합니다.
  • 테스트 시에는 매분(* * * * *) 실행으로 동작을 빠르게 확인한 후, cron.unschedule 후 운영 스케줄로 다시 예약합니다. (cron.alter_job은 DB owner 계정으로 사용할 수 없습니다.)

실행 이력 모니터링

cron.job_run_details에서 작업 실행 이력을 확인할 수 있습니다. 본인이 등록한 작업의 이력만 조회됩니다.

-- 최근 실행 이력 조회
SELECT jobid, jobname, status, return_message, start_time, end_time
FROM cron.job_run_details
ORDER BY start_time DESC
LIMIT 20;

cron.job_run_details는 자동으로 정리되지 않으므로, 다음과 같이 정리 작업을 예약하는 것을 권장합니다.

-- 매주 일요일 05:00에 30일 이전 실행 이력 삭제
SELECT cron.schedule('cleanup-cron-history', '0 5 * * 0', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '30 days'$$);
참고
  • cron.job_run_details는 작업 실행마다 누적되므로, 정리 작업을 예약하지 않으면 테이블 크기가 계속 증가합니다.