Django Signals vs Celery Tasks 2026: Hangisini Ne Zaman Kullanmalı ve Mülakat Soruları
2026'da Django Signals ve Celery Tasks karşılaştırması. Sinyalleri ne zaman, asenkron görevleri ne zaman kullanmalı, kod örnekleri ve teknik mülakat soruları.

Django Signals ve Celery Tasks, Django uygulamalarında olayları farklı şekillerde yönetir. Sinyaller istek-yanıt döngüsü içinde senkron olarak çalışırken, Celery işleri arka plan worker'larına devreder. Yanlış aracı seçmek yavaş yanıtlara, yarış koşullarına veya gereksiz karmaşık mimarilere yol açar.
Django Signals, yanıt dönmeden önce tamamlanması gereken hafif, senkron yan etkiler için kullanılmalıdır. Celery ise 100ms'den uzun süren, harici servisleri içeren veya ana istekten bağımsız olarak başarısız olabilecek her şey için uygundur.
Django Signals Nasıl Çalışır
Django Signals, gözlemci kalıbını uygular. Bir model kaydedildiğinde, silindiğinde veya bir istek başladığında ya da sona erdiğinde, Django bir sinyal gönderir. Bu sinyale bağlı her fonksiyon anında, aynı veritabanı transaction'ı ve aynı thread içinde çalışır.
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.cache import cache
from .models import Product
@receiver(post_save, sender=Product)
def invalidate_product_cache(sender, instance, **kwargs):
# Product.save() commit'inden sonra senkron olarak çalışır
cache_key = f"product:{instance.id}"
cache.delete(cache_key)
# Kategori listesini de geçersiz kıl
cache.delete(f"category:{instance.category_id}:products")Sinyal alıcısı aynı veritabanı transaction'ı içinde çalışır. Transaction geri alınırsa, sinyal handler'ının etkileri kalır. Bu, cache geçersiz kılma için önemlidir: cache temizlenir, ancak veritabanı değişikliği hiçbir zaman kalıcı olmaz. Sonuç, eski verileri yeniden yükleyen bir cache miss'tir.
Django 5.2, bunu çözmek için transaction.on_commit() özelliğini tanıttı. Sinyal mantığını on_commit içine sarmak, yalnızca başarılı bir commit'ten sonra çalışmasını sağlar:
# signals.py
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Product
from .tasks import reindex_product
@receiver(post_save, sender=Product)
def handle_product_saved(sender, instance, **kwargs):
# Transaction başarılı commit olana kadar ertele
transaction.on_commit(
lambda: reindex_product.delay(instance.id)
)Bu desen sinyalleri ve Celery'yi birleştirir: sinyal senkron olarak tetiklenir, ancak asıl iş transaction commit'inden sonra asenkron olarak gerçekleşir.
Celery Task Çalışma Modeli
Celery görevleri ayrı worker süreçlerinde çalıştırır. Bir Django view'ı, argümanları bir mesaj broker'ına (Redis veya RabbitMQ) serileştirerek bir görevi kuyruğa alır. Bir worker mesajı alır ve görevi orijinal HTTP isteğinden bağımsız olarak çalıştırır.
# tasks.py
from celery import shared_task
from django.core.mail import send_mail
from .models import Order
@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def send_order_confirmation(self, order_id: int):
"""Sipariş verildikten sonra onay e-postası gönderir."""
try:
order = Order.objects.select_related('user').get(id=order_id)
send_mail(
subject=f"Sipariş #{order.id} Onaylandı",
message=f"{order.total} tutarındaki siparişiniz alındı.",
from_email="orders@example.com",
recipient_list=[order.user.email],
)
except Order.DoesNotExist:
# Görev çalışmadan önce sipariş silindi
return
except Exception as exc:
# Geçici hatalarda yeniden dene (SMTP timeout, vb.)
raise self.retry(exc=exc)Görev farklı bir süreçte, potansiyel olarak farklı bir makinede çalışır. Orijinal istek bağlamına erişimi yoktur. Sipariş kuyruğa alma ile çalıştırma arasında silinirse, görev bunu uygun şekilde ele almalıdır.
Celery 5.4 (Eylül 2026 itibarıyla güncel kararlı sürüm), gelişmiş görev tipleme ve görev sonuçlarını veritabanında depolamak için django-celery-results aracılığıyla daha iyi Django entegrasyonu ekledi.
Performans Özellikleri Karşılaştırması
Sinyaller isteğe gecikme ekler. Bağlı her alıcı, yanıt dönmeden önce çalışır. Ortalama 50ms'lik üç alıcı ile istek 150ms daha uzun sürer.
Celery minimum gecikme ekler (tipik olarak kuyruğa almak için 1-5ms), ancak nihai tutarlılık getirir. E-posta yanıttan önce değil, "sonunda" gönderilir.
| Faktör | Django Signals | Celery Tasks |
|---|---|---|
| Çalışma | Senkron, aynı süreç | Asenkron, worker süreci |
| Gecikme etkisi | İstek süresine eklenir | ~1-5ms kuyruğa alma overhead'i |
| Hata yönetimi | İsteği bozar | Bağımsız yeniden dener |
| Transaction kapsamı | Transaction içinde | Transaction dışında |
| Harici servis çağrıları | Yanıtı engeller | Arka planda çalışır |
| Karmaşıklık | Minimum kurulum | Broker + worker gerektirir |
Sinyaller Ne Zaman Doğru Seçimdir
Sinyaller, yan etkinin yanıt dönmeden önce tamamlanması gereken ve hesaplama açısından hafif olduğu senaryolara uygundur.
Cache Geçersiz Kılma
Bir model değiştiğinde, ilişkili cache geçersiz kılınmalıdır. Veritabanı cache backend'i veya basit silme işlemleri kullanılıyorsa, sinyaller iyi çalışır:
# signals.py
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.cache import cache
from .models import Article
@receiver([post_save, post_delete], sender=Article)
def clear_article_cache(sender, instance, **kwargs):
cache.delete(f"article:{instance.slug}")
cache.delete("article:list")
cache.delete(f"author:{instance.author_id}:articles")Türetilmiş Alan Güncellemeleri
Türetilmiş değerleri hesaplayıp saklamak, ek veritabanı sorguları olmadan bunları senkronize tutar:
# signals.py
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.db.models import Sum
from .models import OrderItem, Order
@receiver([post_save, post_delete], sender=OrderItem)
def update_order_total(sender, instance, **kwargs):
order = instance.order
total = OrderItem.objects.filter(
order=order
).aggregate(
total=Sum('price')
)['total'] or 0
Order.objects.filter(id=order.id).update(total=total)Denetim Günlüğü
Log kaydının aynı transaction'ın parçası olması gereken basit denetim günlüğü için:
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import SensitiveData, AuditLog
@receiver(post_save, sender=SensitiveData)
def log_sensitive_data_change(sender, instance, created, **kwargs):
AuditLog.objects.create(
model_name='SensitiveData',
object_id=instance.id,
action='created' if created else 'updated',
timestamp=timezone.now()
)Celery Ne Zaman Doğru Seçimdir
Celery, kullanıcı yanıtını engellemeyecek veya bağımsız olarak başarısız olabilecek her şey için uygundur.
E-posta Gönderimi
SMTP sunucuları yavaş veya erişilemez olabilir. Bunu Celery'ye devretmek, kullanıcının anında yanıt alması anlamına gelir:
# tasks.py
from celery import shared_task
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
@shared_task(bind=True, max_retries=5, default_retry_delay=120)
def send_welcome_email(self, user_id: int):
from .models import User
try:
user = User.objects.get(id=user_id)
html_content = render_to_string(
'emails/welcome.html',
{'user': user}
)
msg = EmailMultiAlternatives(
subject="Servise Hoş Geldiniz",
body="Hoş geldiniz...",
to=[user.email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
except User.DoesNotExist:
return # Kullanıcı silindi, atla
except Exception as exc:
raise self.retry(exc=exc)Görüntü İşleme
Görüntüleri yeniden boyutlandırma, optimize etme veya dönüştürme hesaplama açısından yoğundur:
# tasks.py
from celery import shared_task
from PIL import Image
import io
@shared_task
def process_uploaded_image(image_id: int):
from .models import UploadedImage
img_record = UploadedImage.objects.get(id=image_id)
with Image.open(img_record.original.path) as img:
# Küçük resim oluştur
img.thumbnail((300, 300))
thumb_io = io.BytesIO()
img.save(thumb_io, format='WEBP', quality=85)
thumb_io.seek(0)
# Küçük resmi kaydet
img_record.thumbnail.save(
f"{img_record.id}_thumb.webp",
thumb_io
)Harici API Entegrasyonları
Verileri harici servislerle senkronize etmek gerektiğinde:
# tasks.py
from celery import shared_task
import httpx
@shared_task(bind=True, max_retries=3, default_retry_delay=300)
def sync_to_crm(self, customer_id: int):
from .models import Customer
try:
customer = Customer.objects.get(id=customer_id)
with httpx.Client(timeout=30) as client:
response = client.post(
"https://api.crm.example.com/contacts",
json={
"email": customer.email,
"name": customer.name,
"source": "webapp"
},
headers={"Authorization": f"Bearer {settings.CRM_API_KEY}"}
)
response.raise_for_status()
customer.crm_id = response.json()['id']
customer.save(update_fields=['crm_id'])
except httpx.HTTPStatusError as exc:
if exc.response.status_code >= 500:
raise self.retry(exc=exc)
raise # 4xx hataları yeniden denenmemeliSinyalleri Celery ile Birleştirmek
En güçlü desen, sinyalleri Celery görevlerini kuyruğa almak için tetikleyici olarak kullanır. Sinyal, bir model değiştiğinde görevin tutarlı bir şekilde kuyruğa alınmasını sağlar:
# signals.py
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import User
from .tasks import send_welcome_email, sync_to_crm
@receiver(post_save, sender=User)
def handle_new_user(sender, instance, created, **kwargs):
if created:
# Görevleri yalnızca başarılı commit'ten sonra kuyruğa al
transaction.on_commit(lambda: (
send_welcome_email.delay(instance.id),
sync_to_crm.delay(instance.id)
))Bu desen şunları sağlar:
- Görevler yalnızca kullanıcı gerçekten kaydedildiğinde kuyruğa alınır
- Transaction geri alınırsa hiçbir görev kuyruğa alınmaz
- Ana istek hemen tamamlanır
- Arka plan işi uzun süren operasyonları yönetir
Hata Yönetimi ve Güvenilirlik
Sinyal Hataları
Bir sinyal handler'ı exception fırlattığında, çağırana yayılır. Sinyal post_save'e bağlıysa, model kaydı teknik olarak başarılı olmuştur, ancak istek başarısız olur:
# Kötü desen: sinyal kaydetme işlemini bozabilir
@receiver(post_save, sender=Order)
def notify_warehouse(sender, instance, created, **kwargs):
if created:
# Bu başarısız olursa, tüm view başarısız olur
requests.post("https://warehouse.example.com/orders", json={...})Bunun yerine exception handling kullanın veya Celery'ye devredin:
# Daha iyi desen: hataları yönet veya devret
@receiver(post_save, sender=Order)
def notify_warehouse(sender, instance, created, **kwargs):
if created:
transaction.on_commit(
lambda: notify_warehouse_task.delay(instance.id)
)Celery Yeniden Deneme ve Backoff
Celery, üstel backoff ile yerleşik yeniden deneme mekanizmaları sağlar:
# tasks.py
from celery import shared_task
from celery.exceptions import MaxRetriesExceededError
@shared_task(
bind=True,
max_retries=5,
autoretry_for=(ConnectionError, TimeoutError),
retry_backoff=True, # Üstel backoff
retry_backoff_max=600, # Maksimum 10 dakika
retry_jitter=True # Sürü etkisinden kaçınmak için rastgelelik ekler
)
def resilient_task(self, data):
try:
process_data(data)
except PermanentError:
# Kalıcı hataları yeniden deneme
return
except TransientError as exc:
raise self.retry(exc=exc)Sinyalleri vs Celery Görevlerini Test Etme
Sinyalleri Test Etme
Sinyaller, factory_boy context manager veya manuel bağlantı kesme kullanılarak testler sırasında izole edilebilir:
# tests/test_signals.py
from django.test import TestCase
from django.db.models.signals import post_save
from unittest.mock import patch
from .models import Product
from .signals import invalidate_product_cache
class ProductSignalTest(TestCase):
def test_cache_invalidation_on_save(self):
with patch('django.core.cache.cache.delete') as mock_delete:
product = Product.objects.create(name="Test", category_id=1)
mock_delete.assert_any_call(f"product:{product.id}")
mock_delete.assert_any_call(f"category:1:products")Celery Görevlerini Test Etme
Celery, testler için @override_settings decorator'ü ve eager modu sağlar:
# tests/test_tasks.py
from django.test import TestCase, override_settings
from unittest.mock import patch
from .tasks import send_welcome_email
from .models import User
@override_settings(CELERY_TASK_ALWAYS_EAGER=True)
class EmailTaskTest(TestCase):
@patch('django.core.mail.EmailMultiAlternatives.send')
def test_welcome_email_sent(self, mock_send):
user = User.objects.create(email="test@example.com")
send_welcome_email.delay(user.id)
mock_send.assert_called_once()Django mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Mülakat Soruları: Django Signals
S: pre_save ve post_save arasındaki fark nedir?
C: pre_save, veriler veritabanına kaydedilmeden önce tetiklenir ve kalıcı olmadan önce verilerin değiştirilmesine olanak tanır. post_save, kaydetme işleminden sonra, instans zaten birincil anahtara sahip olduğunda tetiklenir. Veri doğrulama veya dönüşüm için pre_save, cache geçersiz kılma gibi yan etkiler için post_save kullanılır.
S: Sinyallerde sonsuz döngüleri nasıl önlersiniz?
C: Sonsuz döngüler, bir sinyal handler'ı aynı sinyali tetiklediğinde oluşur. Çözümler şunları içerir: save()'de update_fields kullanmak ve handler'da kontrol etmek, save() yerine Model.objects.filter().update() kullanmak veya yeniden girişi önlemek için instans üzerinde bir bayrak ayarlamak.
S: Sinyal bağlamında transaction.on_commit() ne işe yarar?
C: transaction.on_commit(), mevcut veritabanı transaction'ı başarıyla commit olana kadar çalışmayı erteler. Bu, harici efektler tetikleyen sinyaller (e-posta gönderme veya görev kuyruğa alma gibi) için kritiktir, çünkü yan etkinin yalnızca veriler gerçekten kalıcı olduğunda gerçekleşmesini sağlar.
S: save() override etmek yerine sinyalleri ne zaman kullanırsınız?
C: Sinyaller şu durumlarda daha iyidir: davranış birden fazla model için genel olmalı, mantık test amaçları için modelden ayrılmalı veya üçüncü taraf bileşenlerinin orijinal kodu değiştirmeden model değişikliklerine tepki vermesi gerektiğinde.
Mülakat Soruları: Celery Tasks
S: Bir Celery görevinin tam olarak bir kez çalıştırılmasını nasıl sağlarsınız?
C: Celery "exactly-once" değil, "at-least-once" teslimat garantisi sağlar. İdempotans için: task_id ile benzersiz görev tanımlayıcıları kullanın, çalıştırmadan önce işin zaten yapılıp yapılmadığını kontrol edin veya idempotent işlemlerle acks_late=True kullanın.
S: delay() ve apply_async() arasındaki farkı açıklayın.
C: delay(), varsayılan seçeneklerle apply_async() için bir kısayoldur. apply_async() ayrıntılı kontrol sağlar: gecikmeli çalıştırma için countdown, belirli bir zaman için eta, yönlendirme için queue, görev TTL'si için expires.
S: Üretimde Celery görevlerini nasıl izlersiniz?
C: Flower gerçek zamanlı izleme ve web arayüzü sağlar. django-celery-results sonuçları veritabanında depolar. Özel görev sinyalleri hook'lama (task_success, task_failure) metrikleri Prometheus/Datadog'a gönderebilir. Broker üzerinden kuyruk izleme (Redis kuyruk uzunluğu) backlog'u gösterir.
S: Bir Celery worker görev sırasında ölürse ne olur?
C: Varsayılan olarak görev kaybolur. acks_late=True ile görev tamamlanana kadar onaylanmaz, böylece başka bir worker onu alabilir. Ancak bu, idempotent görevler gerektirir, çünkü yeniden başlatılmadan önce kısmen çalıştırılmış olabilirler.
S: Bir görevin diğerinin sonucuna bağlı olduğu bir görev zincirini nasıl yönetirsiniz?
C: Celery canvas primitive'leri sağlar: sıralı çalıştırma için chain(), paralel çalıştırma için group(), callback ile paralel için chord(). Örneğin: chain(task1.s(arg), task2.s(), task3.s())() her sonucu bir sonraki göreve geçirir.
Karar Ağacı: Sinyaller vs Celery
Sinyaller ve Celery arasında karar verirken aşağıdaki kriterleri göz önünde bulundurmak gerekir:
- İşlem hafif mi (< 100ms)? Evetse, sinyaller uygundur.
- Harici API/servis çağrıları gerektiriyor mu? Evetse, Celery kullanın.
- Ana istekten bağımsız olarak başarısız olabilir mi? Evetse, Celery kullanın.
- Yanıttan önce tamamlanması gerekiyor mu? Evetse, sinyaller kullanın.
- Karmaşık yeniden deneme/backoff mantığı gerektiriyor mu? Evetse, Celery kullanın.
En iyi uygulama genellikle her ikisini birleştirmektir: ağır iş için Celery görevlerini kuyruğa alan güvenilir tetikleyiciler olarak sinyaller.
Sonuç
Django Signals ve Celery Tasks, uygulama mimarisinde farklı amaçlara hizmet eder. Sinyaller, veritabanı transaction'ının parçası olması gereken hafif, senkron işlemler için idealdir. Celery, uzun süren işlemler, harici servis entegrasyonları ve güvenilir yeniden denemeler gerektiren her şey için vazgeçilmezdir. En güçlü Django uygulamaları her ikisini de kullanır: sinyalleri Celery görevlerini tetikleyen kancalar olarak, sinyallerin basitliğini Celery'nin arka plan işleme gücüyle birleştirir.
Django kodundaki hatayı bulabilir misin?
Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Yazan:
Anthony Fillion-MailletSharpSkill kurucusu
10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.
2 Eylül 2026 tarihinde güncellendi
Etiketler
Paylaş
İlgili makaleler

Django 5.2 Middleware ve Signal Mekanizmalari: Teknik Mulakat Rehberi
Django 5.2'de custom middleware yazimi, signal handling, post_save, pre_save, async middleware ve domain olaylari. Teknik mulakat sorularina yonelik kapsamli rehber ve Python kod ornekleri.

Django ve Celery: Asenkron Görev İşleme ve Mülakat Soruları 2026
Django ve Celery entegrasyonu, asenkron görev işleme, kuyruk yönetimi, Celery Beat zamanlayıcı ve üretim yapılandırması. 2026 mülakat soruları dahil.

Django Async View'ler ve ASGI (2026): Performans ve Mülakat Soruları
2026'da Django async view'ler ve ASGI üzerine derinlemesine bir inceleme: perde arkasında nasıl çalıştıkları, hangi sunucunun tercih edileceği, async ORM ile SynchronousOnlyOperation tuzağı ve mülakat soruları.