Pg_cron ile PostgreSQL İçinde Zamanlanmış Görevler (Cron Jobs)

PostgreSQL'de pg_cron eklentisi ile zamanlanmış görevler nasıl kurulur? Supabase'de cron job, materialized view refresh, eski veri temizleme ve cron...

Neden Veritabanı İçinde Cron Job?

Genellikle zamanlanmış görevler application layer’da (Node.js, Python) çalıştırılır. Ama bazı görevler veritabanının sorumluluğundadır:

  • Materialized view’ları yenilemek
  • Eski log kayıtlarını silmek
  • İstatistik tablolarını güncellemek
  • Geçici verileri temizlemek

Bu görevler için uygulama sunucusu ayakta olmak zorunda olmamalı. pg_cron bunu çözer.

pg_cron Nedir?

pg_cron bir PostgreSQL eklentisidir (extension). Veritabanı içinde Unix cron syntax’ı ile SQL komutlarını zamanlamanızı sağlar.

Supabase’de varsayılan olarak aktiftir — Dashboard’dan doğrudan kullanabilirsiniz.

Cron Expression Syntax

┌───────────── dakika (0-59)
│ ┌───────────── saat (0-23)
│ │ ┌───────────── ayın günü (1-31)
│ │ │ ┌───────────── ay (1-12)
│ │ │ │ ┌───────────── haftanın günü (0-7, 0 ve 7 = Pazar)
│ │ │ │ │
* * * * *

Sık kullanılan örnekler:

ExpressionAnlamı
* * * * *Her dakika
*/5 * * * *Her 5 dakikada
0 * * * *Her saat başı
0 3 * * *Her gece saat 03:00
0 0 * * 0Her Pazar gece yarısı
0 9 * * 1-5Hafta içi her gün 09:00
0 0 1 * *Her ayın 1’i gece yarısı

Kurulum ve Kullanım

Self-hosted PostgreSQL

-- Extension'ı etkinleştir
CREATE EXTENSION IF NOT EXISTS pg_cron;

-- Cron job zamanlama
SELECT cron.schedule(
  'nightly-cleanup',           -- job adı
  '0 3 * * *',                 -- her gece 03:00
  'DELETE FROM logs WHERE created_at < NOW() - INTERVAL ''30 days'''
);

Supabase (Dashboard)

SQL Editor’da doğrudan çalıştırın:

-- Extension zaten aktif, doğrudan zamanlayabilirsiniz
SELECT cron.schedule(
  'refresh-dashboard-stats',
  '*/15 * * * *',  -- her 15 dakikada
  $$
    REFRESH MATERIALIZED VIEW CONCURRENTLY mv_sales_summary;
  $$
);

Pratik Örnekler

1. Materialized View Yenileme

-- Her 15 dakikada satış özet view'ını güncelle
SELECT cron.schedule(
  'refresh-sales-mv',
  '*/15 * * * *',
  'REFRESH MATERIALIZED VIEW CONCURRENTLY mv_sales_summary'
);

2. Eski Logları Temizleme

-- Her gece 02:00'de 90 günden eski logları sil
SELECT cron.schedule(
  'cleanup-old-logs',
  '0 2 * * *',
  $$
    DELETE FROM audit_log 
    WHERE created_at < NOW() - INTERVAL '90 days';
  $$
);

3. Günlük İstatistik Hesaplama

-- Her gece 01:00'de günlük istatistikleri hesapla
SELECT cron.schedule(
  'daily-stats',
  '0 1 * * *',
  $$
    INSERT INTO daily_stats (date, total_orders, total_revenue)
    SELECT 
      CURRENT_DATE - 1,
      COUNT(*),
      SUM(total_amount)
    FROM orders
    WHERE created_at::date = CURRENT_DATE - 1
    ON CONFLICT (date) DO UPDATE SET
      total_orders = EXCLUDED.total_orders,
      total_revenue = EXCLUDED.total_revenue;
  $$
);

4. Süresi Dolmuş Token Temizleme

-- Her saat başı expired session'ları temizle
SELECT cron.schedule(
  'cleanup-expired-sessions',
  '0 * * * *',
  $$
    DELETE FROM user_sessions 
    WHERE expires_at < NOW();
  $$
);

Job Yönetimi

-- Tüm job'ları listele
SELECT * FROM cron.job;

-- Job'ı kaldır (adıyla)
SELECT cron.unschedule('nightly-cleanup');

-- Job'ı kaldır (ID ile)
SELECT cron.unschedule(42);

-- Çalışma geçmişini görüntüle
SELECT * FROM cron.job_run_details 
ORDER BY start_time DESC 
LIMIT 20;

Monitoring: Job’lar Çalışıyor mu?

-- Son 24 saatteki tüm job çalışmalarını kontrol et
SELECT 
  j.jobname,
  jrd.status,
  jrd.start_time,
  jrd.end_time,
  jrd.return_message
FROM cron.job_run_details jrd
JOIN cron.job j ON j.jobid = jrd.jobid
WHERE jrd.start_time > NOW() - INTERVAL '24 hours'
ORDER BY jrd.start_time DESC;

Status değerleri:

  • succeeded — Başarılı
  • failed — Hata oluştu
  • running — Çalışıyor

Dikkat Edilecekler

  1. Timezone: pg_cron UTC kullanır. Türkiye saati (UTC+3) hesaplamayı unutmayın.
  2. Uzun süren job’lar: Bir job tamamlanmadan yenisi başlayabilir. Lock mekanizması kullanın.
  3. Transaction: Her job tek bir SQL statement çalıştırır. Birden fazla komut için function kullanın.
  4. Error handling: cron.job_run_details tablosunu düzenli kontrol edin.
-- Uzun süren job'lar için function kullanımı
CREATE OR REPLACE FUNCTION maintenance_tasks()
RETURNS void AS $$
BEGIN
  -- Adım 1: Eski logları sil
  DELETE FROM audit_log WHERE created_at < NOW() - INTERVAL '90 days';
  
  -- Adım 2: View'ı yenile
  REFRESH MATERIALIZED VIEW CONCURRENTLY mv_dashboard;
  
  -- Adım 3: Vacuum
  -- NOT: VACUUM doğrudan fonksiyon içinde çalışmaz, 
  -- bunun için ayrı bir cron job gerekir
END;
$$ LANGUAGE plpgsql;

SELECT cron.schedule('nightly-maintenance', '0 3 * * *', 'SELECT maintenance_tasks()');

İlgili Yazılar


Bu rehber pg_cron GitHub ve Supabase Cron Docs dokümantasyonuna dayanarak hazırlanmıştır.

📚 İlgili Yazılar