TEMEL
Delayed Jobs
Delayed job'lar belirli bir süre sonra veya belirli bir tarihte çalıştırılan tek seferlik görevlerdir.
Karar Rehberi
| Durum | Öneri | Örnek veya gerekçe |
|---|---|---|
| Sipariş timeout (30 dk) | Uygun | Ödenmemiş siparişi iptal et |
| Hoşgeldin e-postası (1 saat) | Uygun | Kayıt sonrası onboarding |
| Token expiry temizliği | Uygun | Refresh token silme |
| Tam dakika hassasiyeti gereken | Uygun değil: polling interval etkiler | Borsa emir execution |
Kullanım
// TimeSpan ile — "şu kadar sonra"
BackgroundJob.Schedule<IOrderService>(
svc => svc.CancelUnpaidOrderAsync(orderId),
TimeSpan.FromMinutes(30));
// DateTimeOffset ile — "bu tarihte"
BackgroundJob.Schedule<IReminderService>(
svc => svc.SendReminderAsync(userId, "Toplantınız 1 saat sonra"),
new DateTimeOffset(2026, 6, 15, 9, 0, 0, TimeSpan.FromHours(3)));
// Job ID döner (string) — iptal için kullanılabilir
string jobId = BackgroundJob.Schedule<IOrderService>(
svc => svc.CancelUnpaidOrderAsync(orderId),
TimeSpan.FromMinutes(30));
// Sipariş ödendiyse job'ı iptal et
BackgroundJob.Delete(jobId);Sipariş Timeout Pattern
public class OrderService : IOrderService
{
private readonly IOrderRepository _repo;
public async Task CancelUnpaidOrderAsync(int orderId)
{
var order = await _repo.GetByIdAsync(orderId);
// İdempotent kontrol — zaten ödendiyse veya iptal edildiyse bir şey yapma
if (order is null || order.Status != OrderStatus.PendingPayment)
return;
order.Status = OrderStatus.Cancelled;
order.CancelReason = "Ödeme süresi doldu (30 dakika)";
order.CancelledAt = DateTimeOffset.UtcNow;
await _repo.UpdateAsync(order);
// Stok iade
BackgroundJob.Enqueue<IStockService>(
svc => svc.ReleaseReservedStockAsync(orderId));
}
}
// Sipariş oluşturulduğunda
public int CreateOrder(CreateOrderRequest request)
{
var order = _mapper.Map<Order>(request);
order.Status = OrderStatus.PendingPayment;
_repo.Add(order);
// 30 dk sonra kontrol et
var jobId = BackgroundJob.Schedule<IOrderService>(
svc => svc.CancelUnpaidOrderAsync(order.Id),
TimeSpan.FromMinutes(30));
// Job ID'yi sakla — ödeme gelirse iptal edeceğiz
order.CancelJobId = jobId;
_repo.Update(order);
return order.Id;
}
// Ödeme geldiğinde
public void MarkAsPaid(int orderId)
{
var order = _repo.GetById(orderId);
order.Status = OrderStatus.Paid;
// Timeout job'ını iptal et
if (!string.IsNullOrEmpty(order.CancelJobId))
BackgroundJob.Delete(order.CancelJobId);
_repo.Update(order);
}Scheduling hassasiyeti: Hangfire, scheduled job'ları polling ile kontrol eder. Varsayılan SchedulePollingInterval 15 saniyedir. Yani "30 dakika sonra" demek "30:00 ile 30:15 arası" anlamına gelir. Saniye hassasiyeti gerekiyorsa Hangfire doğru araç değildir.
Örnek: Bir fintech uygulamasında kredi başvurusu yapıldığında, 48 saat içinde belge yüklenmezse başvuru otomatik reddedilir. Delayed job ile bu süre yönetilir, belge yüklendiğinde job silinir.