HHangfire Handbook

İLERİ

Storage Seçimi

Storage, Hangfire'ın kalbidir — tüm job verileri, state'ler ve queue'lar burada saklanır.

Karar Rehberi

Durum Öneri Örnek veya gerekçe
SQL Server zaten altyapıda Uygun: SQL Server storage (ücretsiz) Kurumsal .NET projelerinin %80'i
Günlük 100K+ job, <1s latency Uygun: Redis ( Pro lisansı gerekli) E-ticaret, fintech real-time
Sadece PostgreSQL var Uygun: PG community paketi (ücretsiz, resmi değil) Startup, PG-native stack
Basit PoC / prototype Uygun: InMemory (ücretsiz) Storage kararını ertele
Startup production'ı, bütçe yok Uygun değil: Redis ( Pro lisansı) SQL Server yeterli
Mission-critical, SLA gerekli Uygun değil: PG community (test coverage düşük) Resmi storage tercih et
SQL Server Artı: Resmi, stabil, auto-migration Artı: Transaction desteği Artı: Zaten altyapıda varsa bedava Eksi: Polling-based (15s default) Eksi: Yüksek throughput'ta yavaş Throughput: ~1K job/s Redis (Pro) Artı: Push-based (düşük latency) Artı: Çok yüksek throughput Artı: Düşük resource kullanımı Eksi: Ücretli (Hangfire Pro) Eksi: Redis altyapısı gerekir Throughput: ~10K+ job/s PostgreSQL Artı: Bedava, community paketi Artı: LISTEN/NOTIFY (no polling) Artı: Zaten PG kullanıyorsan ideal Eksi: Resmi değil (community) Eksi: Daha az test edilmiş Throughput: ~2-3K job/s

Karar Matrisi

Kriter SQL Server Redis (Pro) PostgreSQL
Maliyet Varsa bedava Ücretli Hangfire Pro planı gerekir Bedava
Latency 15s polling <100ms push ~1s NOTIFY
Throughput ~1K/s ~10K+/s ~2-3K/s
Reliability Çok yüksek Yüksek Orta
Resmi destek Evet Evet Hayır (community)
Kurulum kolaylığı Kolay Orta Kolay
Ne zaman Başlangıç, kurumsal Yüksek hacim, düşük latency PG-only altyapı
builder.Services.AddHangfire(config => config
    .UseSqlServerStorage(connectionString, new SqlServerStorageOptions
    {
        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
        QueuePollInterval = TimeSpan.Zero,                  // Long-polling (SlidingInvisibility ile birlikte)
        UseRecommendedIsolationLevel = true,                // ReadCommitted
        DisableGlobalLocks = true,                          // Schema 7+ gerektirir
        TryAutoDetectSchemaDependentOptions = true,         // 1.8: Schema'ya göre otomatik optimizasyon
        SchemaName = "hangfire",                            // İzole schema
        PrepareSchemaIfNecessary = true                     // Auto-migration
    }));

SqlClient seçimi: Hangfire 1.8, varsayılan olarak Microsoft.Data.SqlClient kullanır. Eski projelerde System.Data.SqlClient referansı varsa her ikisi de desteklenir — Hangfire otomatik algılar. Explicit seçim gerekiyorsa: SqlClientFactory = Microsoft.Data.SqlClient.SqlClientFactory.Instance

Ayrı DB mi, Aynı DB + Schema mı?

Hangfire tablolarının nerede yaşayacağı, production'da en çok tartışılan altyapı kararlarından biridir.

Ayrı Database Artı: Bağımsız backup/restore Artı: Connection pool izolasyonu Artı: Disk I/O ayrı — app DB etkilenmez Artı: Farklı retention policy uygulayabilirsin Eksi: Ek DB bakımı (monitoring, patching) Eksi: Cross-DB transaction yapılamaz Eksi: Ek connection string yönetimi Aynı DB + Ayrı Schema Artı: Tek connection string, basit ops Artı: Küçük ekip/proje için ideal Artı: Tek backup'ta her şey gelir Artı: Transaction scope mümkün Eksi: Hangfire I/O app query'leri etkiler Eksi: Connection pool paylaşımı (tükenme riski) Eksi: DB boyutu şişer (job history)
Kriter Ayrı DB Aynı DB + Schema Neden Önemli
Backup/Restore Bağımsız App ile birlikte Hangfire DB restore'u app data'yı etkilemez
Connection Pool İzole Paylaşımlı Worker spike'ı app pool'u tüketebilir
Disk I/O Farklı disk/volume Aynı disk Job polling + state update yoğun write
Operasyonel basitlik 2 DB yönetimi Tek DB Küçük ekiplerde overhead
Compliance / audit Data ayrımı net Karışık GDPR: job arg'larda PII olabilir
Transaction scope Distributed TX gerekir Aynı TX'te enqueue "Kaydet + job at" atomic olsun
// Aynı DB, farklı schema — basit ama riskli
builder.Services.AddHangfire(config => config
    .UseSqlServerStorage(
        builder.Configuration.GetConnectionString("DefaultConnection"),
        new SqlServerStorageOptions
        {
            SchemaName = "hangfire",          // Default "HangFire" schema
            PrepareSchemaIfNecessary = true
        }));
// Connection pool tükenmesini önlemek için pool size artırın
// Hangfire worker'lar + app request'leri aynı pool'u paylaşır
// Connection string'e ekleyin: "Max Pool Size=200"

Anti-Pattern: Hangfire tablolarını app schema'sında (dbo) bırakmak. EF Core migration'ları Hangfire tablolarını "unknown table" olarak algılar ve silmeye çalışabilir. Mutlaka SchemaName belirtin.

Örnek: Bir fintech'te Hangfire aynı DB'deydi. Black Friday'de 50K job enqueue edilince connection pool tükendi (default 100), app 503 vermeye başladı. Çözüm: Hangfire'ı ayrı DB'ye taşıyıp dedicated pool (Max Pool Size=150) tanımladılar. App pool'u 100'de kaldı, sıfır downtime.

Örnek: Bir e-ticaret sitesi başlangıçta SQL Server storage ile başladı (zaten MSSQL kullanıyor). Günlük 10K job sorunsuz. Black Friday'de 100K+ job/gün gerekince Redis storage'a geçiş yaptılar — zero downtime migration: eski job'lar SQL'de tamamlandı, yeniler Redis'e yazıldı.