Webhook Retry Mekanizması Nasıl Tasarlanır?
Webhook retry mekanizmaları, veri kaybını önlemek için eksponansiyel bekleme süresi, idempotency ve dead-letter queue mimarisi kullanılarak tasarlanır.

İÇİNDEKİLER
%0 okundu
- Webhook İletişiminde Karşılaşılan Kritik Hata Senaryoları
- Kurumsal Bir Webhook Retry Mekanizmasının Temel Bileşenleri
- Başarısız İsteklerin Nihai Yönetimi: Dead-Letter Queue (DLQ) Mimarisi
- Güvenli Webhook Retry Tasarımı İçin Adım Adım İş Akışı
- Risk Yönetimi: İzleme (Monitoring) ve Uyarı (Alerting) Sistemleri
Webhook retry mekanizmaları, dağıtık sistemlerde veri kaybını önlemek ve servisler arası veri tutarlılığını (data consistency) güvence altına almak için eksponansiyel bekleme süresi (exponential backoff), jitter optimizasyonu, idempotency ve dead-letter queue (DLQ) mimarisi kullanılarak tasarlanır.
Modern yazılım mimarilerinde sistemler arası asenkron veri iletimi sağlayan webhook altyapıları, harici ağ koşulları ve hedef sistem kesintileri nedeniyle sıkça iletişim başarısızlıklarıyla karşılaşır. Kurumsal ölçekte bir webhook retry mekanizması tasarlamak; yalnızca başarısız olan bir HTTP isteğini tekrar göndermekten ibaret değildir. Hedef sunucuyu aşırı yükten korumak, mükerrer veri işlenmesini (duplicate execution) engellemek ve kalıcı hatalarda veri kaybı yaşamadan kurtarma süreçlerini işletmek için uçtan uca dayanıklı (fault-tolerant) bir mimari gereklidir. Bu rehberde, hata toleransı yüksek bir webhook yeniden deneme mekanizmasının mimari adımlarını, matematiksel gecikme modellerini ve kurumsal en iyi pratiklerini teknik ayrıntılarıyla inceliyoruz.
Webhook İletişiminde Karşılaşılan Kritik Hata Senaryoları
Dağıtık ve olay güdümlü mimarilerde (event-driven architecture) webhook gönderici ve alıcı servisler tamamen bağımsız çalışır. Bu asenkron yapı ölçeklenebilirlik sağlarken, ağ katmanından veya hedef uç noktanın iç dinamiklerinden kaynaklanan birçok potansiyel arıza noktasını da beraberinde getirir. Bir yeniden deneme algoritması kurgulamadan önce, karşılaşılabilecek arıza tiplerinin doğru sınıflandırılması şarttır. Çünkü her hata tipi yeniden denemeye (retryable) uygun değildir; uygun olmayan hataların tekrar edilmesi kaynak israfına ve kaskat kesintilere (cascading failures) yol açar.
Aşağıdaki tablo, webhook iletişiminde karşılaşılan yaygın hata tiplerini ve bunların yeniden deneme uygunluğunu özetlemektedir:
Ağ Kaynaklı Kesintiler ve Zaman Aşımı (Timeout) Sorunları
Ağ kaynaklı kesintiler, istemcinin hedef sunucuya fiziksel veya mantıksal olarak ulaşamadığı durumlarda meydana gelir. DNS çözümleme hataları, TCP bağlantı kurulamaması (connection refused), TLS sertifika el sıkışması tıkanıklıkları ve paket kayıpları bu gruptadır. En sık karşılaşılan problem ise bağlantı veya okuma zaman aşımıdır (connection/read timeout).
Webhook mimarilerinde varsayılan HTTP istemci zaman aşımları genellikle 30 ila 60 saniye gibi agresif olmayan değerlerde bırakılır. Ancak kurumsal bir webhook gönderim motorunda, okuma zaman aşımı süresinin 5 ila 10 saniye aralığında sınırlandırılması önerilir. Aksi takdirde, yavaş yanıt veren bir alıcı, gönderici tarafındaki HTTP bağlantı havuzunu (connection pool) tüketerek diğer sağlıklı webhook bildirimlerinin de kuyrukta beklemesine ve sistem kaynaklarının kilitlenmesine yol açabilir.
Hedef Sunucu Hataları: HTTP 500 ve HTTP 503
Hedef sunucu hataları, alıcı uygulamanın isteği aldığını ancak içsel bir istisna (exception), veritabanı kilitlenmesi veya altyapı çökmesi nedeniyle işleyemediğini belirtir. Özellikle @@CODE0@@, @@CODE1@@ ve HTTP 503 Service Unavailable kodları, geçici bir arıza durumunu temsil eder.
Bu hata kodları alındığında sistem, hatayı geçici (transient) olarak kabul etmeli ve isteği derhal yeniden deneme havuzuna almalıdır. Ancak burada kritik bir mimari kural geçerlidir: Hedef sistem henüz toparlanmamışken anında (immediate) yapılan peş peşe yeniden denemeler, hedef sunucunun çöküşünü hızlandırır. Bu nedenle sunucu hatalarında araya mutlaka dinamik bir bekleme süresi konulmalıdır.
Kapasite Aşımı Durumları: HTTP 429 (Too Many Requests)
Alıcı sistemler kendilerini aşırı yükten korumak için oran sınırlama (rate limiting) algoritmaları (Token Bucket, Leaky Bucket vb.) uygular. Gönderici sistem ani bir toplu olay (burst traffic) ilettiğinde, alıcı uç nokta HTTP 429 Too Many Requests yanıtı döner.
Bu durumda alıcı sunucu genellikle yanıta bir @@CODE0@@ HTTP başlığı (header) ekler. Bu başlık, istemcinin bir sonraki isteği kaç saniye sonra veya hangi zaman damgasında (timestamp) göndermesi gerektiğini belirtir. Kurumsal bir webhook altyapısı, @@CODE1@@ yanıtı aldığında kendi dahili bekleme algoritmasını ezmeli ve alıcının talep ettiği Retry-After süresine tam uyum sağlayarak isteği planlanan zamana ertelemelidir.
---
Kurumsal Bir Webhook Retry Mekanizmasının Temel Bileşenleri
Güvenilir bir webhook retry sistemi tasarlanırken basit bir for döngüsü ile sabit aralıklı yeniden deneme (fixed delay) yaklaşımı tercih edilmemelidir. Sabit aralıklı denemeler, sistem arızası anında binlerce isteğin aynı saniyede tekrar gönderilmesine ve hedef sistem üzerinde yıkıcı bir yük dalgası oluşmasına yol açar. Dayanıklı bir kurumsal mimari üç temel ayağa dayanır: Eksponansiyel bekleme (exponential backoff), rastgele sapma (jitter) ve eşetkililik (idempotency).
[Olay Gerçekleşti]
│
▼
[Webhook Gönderimi] ──(Başarılı: 2xx)──► [Tamamlandı]
│
(Hata Aldı: 5xx / 429 / Timeout)
│
▼
[Kalan Deneme Var mı?] ──(Hayır)──► [Dead-Letter Queue (DLQ)]
│
(Evet)
│
▼
[Hesapla: Exponential Backoff + Full Jitter]
│
▼
[Gecikmeli Mesaj Kuyruğuna (Broker) Yaz]Eksponansiyel Bekleme Süresi (Exponential Backoff) Stratejisi
Eksponansiyel bekleme süresi, başarısız olan her ardışık denemede bekleme süresini katlanarak artıran matematiksel bir modeldir. Bu stratejinin amacı, geçici kesinti yaşayan hedef sisteme toparlanması için her adımda daha geniş bir zaman penceresi tanımaktır.
Algoritmanın temel formülü şu şekildedir:
$$Bekleme Süresi = \min(Maksimum Gecikme, Taban Süre \times 2^{deneme\_sayisi})$$
Örneğin, taban sürenin 2 saniye, maksimum sürenin ise 3600 saniye (1 saat) olarak belirlendiği bir senaryoda bekleme süreleri şu şekilde ilerler:
Deneme 1: $2 \times 2^0 = 2\text{ saniye}$
Deneme 2: $2 \times 2^1 = 4\text{ saniye}$
Deneme 3: $2 \times 2^2 = 8\text{ saniye}$
Deneme 4: $2 \times 2^3 = 16\text{ saniye}$
Deneme 5: $2 \times 2^4 = 32\text{ saniye}$
Deneme 6: $2 \times 2^5 = 64\text{ saniye}$
Bu model sayesinde kısa süreli kesintiler ilk birkaç saniyede çözülürken, uzun süreli altyapı arızalarında hedef sistem gereksiz trafik yüküyle boğulmaz.
Jitter (Sapma) ile "Thundering Herd" Problemini Önleme
Eksponansiyel bekleme süresi tek başına kullanıldığında büyük bir mimari zafiyet barındırır: Thundering Herd (Gürleyen Sürü) Problemi.
Hedef sistemde saat 14:00'te 1 dakikalık genel bir veritabanı kesintisi yaşandığını ve bu süre zarfında 10.000 webhook isteğinin aynı anda başarısız olduğunu varsayalım. Eğer saf eksponansiyel bekleme kullanılırsa, bu 10.000 isteğin tamamı tam 2 saniye sonra aynı anda tekrar tetiklenecektir. Başarısız olursa, tamamı tam 4 saniye sonra tekrar aynı anda hedef sunucuya hücum edecektir. Bu senkronize yük patlaması, toparlanmaya çalışan hedef sunucuyu anında tekrar çökertecektir.
Bu problemi çözmek için bekleme süresine rastgele bir sapma değeri (Jitter) eklenir. AWS Architecture ekibinin önerdiği en etkili yöntem Full Jitter yaklaşımıdır:
import random
import time
def calculate_backoff_with_full_jitter(attempt: int, base_delay: float = 2.0, max_delay: float = 3600.0) -> float:
"""
Exponential Backoff + Full Jitter hesaplama fonksiyonu.
attempt: 0 tabanlı mevcut yeniden deneme indeksi
"""
# Eksponansiyel tavan değeri hesapla
calculated_backoff = min(max_delay, base_delay * (2 ** attempt))
# 0 ile calculated_backoff arasında rastgele bir süre seç
sleep_time = random.uniform(0, calculated_backoff)
return sleep_timeFull Jitter sayesinde, başarısız olan 10.000 istek zamana homojen olarak dağıtılır ve tepe yük dalgaları tamamen sönümlenir.
Idempotency (Eşetkililik) ile Mükerrer Veri İşlemeyi Engelleme
Dağıtık sistemlerde "en az bir kez iletim" (at-least-once delivery) garantisi verilir. Ağ seviyesinde yaşanan bir gecikme nedeniyle hedef sunucu isteği başarıyla işlemiş olsa dahi HTTP yanıtı göndericiye ulaşamayabilir (okuma zaman aşımı). Bu durumda gönderici sistemi isteği başarısız sayıp tekrar iletecektir.
Eğer hedef uç nokta bir ödeme alma, fatura kesme veya stok düşme işlemi gerçekleştiriyorsa, aynı isteğin iki kez işlenmesi ciddi finansal ve operasyonel tutarsızlıklara yol açar. Bu riski ortadan kaldırmak için Idempotency mekanizması uygulanmalıdır.
Idempotency Key Üretimi: Gönderici, her özgün olay için benzersiz bir UUIDv4 veya deterministik bir karma (hash) üretir ve bunu HTTP başlığında (
X-Idempotency-Key: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d) alıcıya iletir.Kayıt ve Kontrol: Alıcı sunucu gelen isteği işlemeden önce bu anahtarı hızlı bir anahtar-değer deposunda (örneğin Redis) atomik olarak kontrol eder.
Mükerrer Yanıt: Eğer anahtar daha önce işlenmişse, işlem tekrarlanmaz; veritabanında saklanan önceki başarılı işlem sonucu doğrudan dönülür.
---
Başarısız İsteklerin Nihai Yönetimi: Dead-Letter Queue (DLQ) Mimarisi
Hiçbir yeniden deneme mekanizması sonsuza kadar çalışamaz. Belirli bir deneme sayısına (örneğin 5 veya 10 deneme) veya toplam süreye (örneğin 24 saat) ulaşıldığında başarısız olmaya devam eden iletiler, ana işleme hattından çıkarılmalıdır. Aksi halde kuyruklar tıkanır ve sistem kaynakları tükenir. İşte bu noktada Dead-Letter Queue (DLQ) devreye girer.
DLQ Nedir ve Sistem Tasarımına Nasıl Entegre Edilir?
Dead-Letter Queue (Ölü Mektup Kuyruğu), işleme döngüsü boyunca başarıyla tüketilemeyen veya teslim edilemeyen mesajların tutulduğu özel bir depolama ve kuyruk katmanıdır. RabbitMQ, Apache Kafka, AWS SQS veya Google Cloud Pub/Sub gibi mesaj yönlendiricilerde yerel (native) olarak desteklenir.
Bir webhook mesajı maksimum deneme sınırını aştığında, atılmaz veya silinmez. Bunun yerine aşağıdaki üst verilerle (metadata) zenginleştirilerek DLQ kuyruğuna yazılır:
Olay Kimliği (Event ID) ve Idempotency Key
Orijinal Yük (Payload): Değiştirilmemiş JSON verisi
Hedef URL: Webhook uç noktası
Toplam Deneme Sayısı: Gerçekleştirilen retry adedi
Son HTTP Durum Kodu ve Hata Mesajı: (Örn:
HTTP 500 - Database Connection Failed)İlk Gönderim ve Son Deneme Zaman Damgaları
Bu zenginleştirilmiş yapı, teknik ekiplerin ve sistem yöneticilerinin hatanın kök nedenini (root cause analysis) saniyeler içinde tespit etmesini mümkün kılar.
DLQ İçerisindeki Verilerin Manuel veya Otomatik Kurtarılma Süreçleri
DLQ kuyruğunda biriken iletilerin yaşam döngüsü kurumsal veri güvenliği ve SLA standartları açısından tanımlanmış prosedürlere bağlı olmalıdır. İşletmeler genelde iki ana kurtarma yöntemini bir arada kullanır:
Otomatik Yeniden Oynatma (Automatic Replay): Hedef sistemdeki arıza çözüldüğünde veya ilgili uç noktanın tekrar sağlıklı (healthy) olduğu health-check mekanizmaları ile doğrulandığında, DLQ'daki mesajlar kontrollü bir hız limitiyle (örneğin saniyede 10 istek) ana kuyruğa geri aktarılır.
Manuel Yönetim Paneli (Admin Portal Replay): Geliştiricilerin veya destek ekiplerinin hatalı webhook'ları tek tek veya toplu olarak inceleyebildiği, JSON yükünü gerekirse düzeltebildiği ve tek bir tıkla yeniden tetikleyebildiği kullanıcı dostu yönetim arayüzleridir.
---
Güvenli Webhook Retry Tasarımı İçin Adım Adım İş Akışı
Kurumsal bir backend mimarisinde webhook retry akışı senkron HTTP thread'leri üzerinden yürütülmemelidir. Bunun yerine dağıtık bir mesaj kuyruğu (message broker) üzerinden asenkron olarak işletilmelidir.
Adım 1: HTTP Durum Kodlarına Göre Aksiyon Belirleme (Filtreleme)
Alıcı uç noktadan gelen HTTP yanıtı anında bir durum filtresinden geçirilmelidir. Başarılı @@CODE0@@ yanıtları işlemi doğrudan sonlandırırken; @@CODE1@@ istemci hataları (örneğin @@CODE2@@ veya @@CODE3@@) kalıcı hata olarak işaretlenmeli ve gereksiz yeniden deneme döngülerine sokulmadan doğrudan loglanmalıdır. Yalnızca @@CODE4@@, @@CODE5@@ ve ağ zaman aşımları yeniden deneme kuyruğuna yönlendirilir.
Adım 2: Mesaj Kuyruğu (Message Broker) Üzerinden Asenkron İşleme
Başarısız olan istek doğrudan o anda bekletilmez. Bunun yerine, hesaplanan Full Jitter süresi kadar gecikme (delay) desteği olan bir mesaj kuyruğuna (örneğin RabbitMQ Delayed Message Plugin, SQS Delayed Queue veya Redis tabanlı bir gecikme kuyruğu) yazılır. Böylece gönderici sunucunun bellek ve işlemci kaynakları boşa harcanmaz; iş parçacıkları (threads) diğer canlı istekleri işlemeye devam eder.
Adım 3: Maksimum Retry Sınırının Belirlenmesi ve DLQ Yönlendirmesi
Kuyruktan mesajı tüketen işçi (worker) servis, mesaj üst verisindeki retry_count değerini kontrol eder. Eğer bu değer belirlenen tavan sınıra (örneğin 8 deneme) ulaşmışsa, mesaj artık normal gecikme kuyruğuna değil, Dead-Letter Queue'ya aktarılır ve sistem yöneticilerine uyarı fırlatılır.
Aşağıdaki Node.js/TypeScript örneği, kurumsal bir webhook worker servisinin yeniden deneme karar mantığını göstermektedir:
interface WebhookMessage {
eventId: string;
idempotencyKey: string;
targetUrl: string;
payload: Record<string, any>;
attempt: number;
}
const MAX_ATTEMPTS = 5;
const BASE_DELAY_SEC = 2;
const MAX_DELAY_SEC = 1800; // 30 dakika
async function processWebhook(message: WebhookMessage): Promise<void> {
try {
const response = await fetch(message.targetUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Idempotency-Key': message.idempotencyKey,
},
body: JSON.stringify(message.payload),
signal: AbortSignal.timeout(8000), // 8 saniye katı timeout
});
if (response.ok) {
// 2xx Başarılı: İşlemi tamamla
await markAsDelivered(message.eventId);
return;
}
// 4xx Hataları (429 hariç) kalıcıdır, tekrar denenmez
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
await logPermanentFailure(message.eventId, response.status);
return;
}
// Geçici hata: Retry sürecine al
await handleRetry(message, `HTTP Status: ${response.status}`);
} catch (error: any) {
// Ağ kesintisi veya Timeout
await handleRetry(message, error.message || 'Network Timeout');
}
}
async function handleRetry(message: WebhookMessage, reason: string): Promise<void> {
const nextAttempt = message.attempt + 1;
if (nextAttempt >= MAX_ATTEMPTS) {
await sendToDeadLetterQueue(message, reason);
return;
}
// Full Jitter ile bekleme süresi hesapla
const maxBackoff = Math.min(MAX_DELAY_SEC, BASE_DELAY_SEC * Math.pow(2, message.attempt));
const delaySec = Math.random() * maxBackoff;
message.attempt = nextAttempt;
await publishToDelayedQueue(message, delaySec);
}Kurumsal sistemlerde hatasız bir retry döngüsü oluşturmak için izlenmesi gereken sıra. Yanıtı analiz ederek hatanın geçici mi (5xx, 429, timeout) yoksa kalıcı mı (400, 401, 404) olduğunu belirleyin. Geçici hatalar için Exponential Backoff formülü ve Full Jitter kullanarak rastgele bekleme süresini hesaplayın. Hesaplanan süre kadar bekletilmek üzere mesajı asenkron gecikme kuyruğuna yönlendirin. Maksimum deneme sayısını aşan mesajları ana akıştan ayırıp Dead-Letter Queue'ya aktarın.Webhook Yeniden Deneme Uygulama Süreci
Durum Kodu Ayrıştırma
Gecikme ve Jitter Hesaplama
Mesajı Gecikmeli Kuyruğa Aktarma
Tavan Sınır Kontrolü ve DLQ Yalıtımı
---
Risk Yönetimi: İzleme (Monitoring) ve Uyarı (Alerting) Sistemleri
Webhook mimarileri doğası gereği "gönder ve unut" (fire and forget) tuzağına düşmeye oldukça müsaittir. Yeterli telemetri ve gözlemlenebilirlik (observability) katmanı kurulmadığında, yüz binlerce webhook iletisinin başarısız olduğu ve DLQ'da biriktiği günler sonra müşteri şikayetleriyle fark edilebilir. Bu nedenle kurumsal sistemlerde kapsamlı bir izleme ve uyarı altyapısı zorunludur.
Webhook Metriklerinin İzlenmesi ve Analizi
Sistemin sağlığını ve genel ağ dinamiklerini anlamak için Prometheus, Datadog veya OpenTelemetry standartları üzerinden şu temel metrikler toplanmalıdır:
Webhook Teslimat Başarı Oranı (Delivery Success Rate): Başarılı
2xxyanıtlarının toplam isteklere oranı (Hedef: $\ge \%99.9$).Gecikme Süresi (p95 ve p99 Latency): Alıcı sunucuların yanıt verme sürelerinin dağılımı.
Yeniden Deneme Yoğunluğu (Retry Attempt Distribution): İsteklerin kaçıncı denemede başarıya ulaştığının analizi. (Eğer isteklerin büyük kısmı 3. veya 4. denemede çözülüyorsa taban gecikme süreleri optimize edilmelidir).
DLQ Birikim Hızı (DLQ Growth Rate): Ölü mektup kuyruğuna saniyede/dakikada düşen ileti sayısı.
Otomatik Uyarı (Alerting) Mekanizmaları ve Eşik Değerler
Metriklerin yalnızca toplanması yeterli değildir; anomaliler anında ilgili operasyon ve geliştirme ekiplerine bildirilmelidir. Kurumsal düzeyde tanımlanması gereken kritik alarm kuralları şunlardır:
DLQ Giriş Alarmı (Kritik): DLQ kuyruğuna 5 dakika içinde 10'dan fazla mesaj düşerse derhal PagerDuty / Opsgenie üzerinden nöbetçi ekibe sesli çağrı düşürülmelidir.
Global Hata Oranı Alarmı (Yüksek): Gönderilen webhook'ların genel hata oranı 10 dakikalık periyotta $\%5$'i aşarsa Slack/Teams kanallarına acil durum bildirimi iletilmelidir.
Müşteri/Uç Nokta Bazlı İzolasyon Alarmı (Orta): Belirli bir müşterinin uç noktası 1 saat boyunca sürekli
HTTP 500dönüyorsa, sistem o müşterinin webhook gönderimlerini geçici olarak askıya almalı (Circuit Breaker deseni) ve müşteriye otomatik teknik bilgilendirme e-postası göndermelidir.
---
Sıkça Sorulan Sorular
Webhook isteği başarısız olursa veri kaybolur mu?
Doğru kurgulanmış bir mimaride veri kaybı yaşanmaz; başarısız olan istekler mesaj kuyruklarına aktarılır ve maksimum deneme sınırını aştığında Dead-Letter Queue (DLQ) üzerinde güvenle saklanır.
Sabit aralıklı yeniden deneme (Fixed Delay) neden tercih edilmemelidir?
Sabit aralıklar, kesinti anında tüm başarısız isteklerin aynı saniyede tekrar iletilmesine yol açarak Thundering Herd problemi yaratır ve hedef sunucunun toparlanmasını engeller.
Webhook retry işlemleri için ideal deneme sayısı kaçtır?
Kurumsal standartlarda genellikle 5 ile 8 deneme arası tercih edilir; bu denemelerin toplam süresi Exponential Backoff ile 24 ila 72 saate yayılarak hedef sunucuya toparlanma süresi tanınır.
Idempotency mekanizması hedef sunucuda mı, kaynak sunucuda mı tasarlanır?
Idempotency anahtarı gönderici (kaynak) sistem tarafından üretilip HTTP başlığında iletilir; bu anahtarın kontrolü ve mükerrer işlemin engellenmesi ise alıcı (hedef) sunucu tarafında gerçekleştirilir.
HTTP 429 Too Many Requests hatası alındığında sistem nasıl davranmalıdır?
Sistem dahili bekleme süresini durdurmalı, alıcı sunucunun HTTP yanıt başlığında gönderdiği Retry-After parametresine tam uyum sağlayarak isteği belirtilen süre kadar ertelemelidir.
Webhook yeniden denemeleri senkron olarak çalıştırılabilir mi?
Hayır; senkron retry işlemleri gönderici tarafındaki HTTP bağlantı havuzlarını ve sunucu thread'lerini kilitler, bu nedenle tüm retry akışları asenkron mesaj kuyrukları üzerinden yürütülmelidir.
Dead-Letter Queue (DLQ) içindeki mesajlar ne kadar süre saklanmalıdır?
Olası uzun süreli altyapı kesintilerinde ekiplerin inceleme ve manuel düzeltme yapabilmesi için DLQ mesajlarının saklama süresi en az 14 ile 30 gün olarak yapılandırılmalıdır.
Circuit Breaker deseni webhook mimarilerinde ne işe yarar?
Sürekli hata veren veya yanıt vermeyen bir hedef uç noktaya giden istekleri geçici olarak durdurarak sistem kaynaklarının tükenmesini önler ve alıcı tarafı gereksiz yükten korur.