Birkaç yılda bir, takvim Türkiye'de bir yerlerde bir BES pipeline'ını sessizce bozar. Çökme yok, 500 hatası yok, sabahın üçünde çağrılan bir nöbetçi mühendis yok. Koda göre hukuken doğru, EGM'ye göre hukuken yanlış bir katılımcı bakiyesiyle bozulur. İşin ilginç kısmı da bu boşluktur.
Hata gerçekte nasıl görünür
Standart bir BES pipeline'ının üç zamansal çıpası vardır: katkı payı takas tarihi (çoğu fon yapısında T+2), NAV fiyatlama günü ve EGM raporlama kesim tarihi. Üçü de bir iş günü takvimi üzerinden hesaplanır. Takvimin kendisi genellikle şunlardan biridir:
- Bir yapılandırma dosyasında sabit kodlanmış bir
2025_HOLIDAYSlistesi - En son güncellemeyi hatırlayan kişi tarafından tutulan bir veritabanı tablosu
- Artık kimsenin sahiplenmediği ortak bir dahili servise yapılan çağrı
- Kurulumdan bu yana güncellenmemiş bir tarih kütüphanesindeki Türkiye yerel ayar kuralları
Hükümet iki hafta kala yarım gün, köprü günü veya dini bayram düzenlemesi ilan ettiğinde bu dördü de aynı şekilde başarısız olur. Ve bu ilanlar oluyor. Ramazan Bayramı ve Kurban Bayramı resmi düzenlemelere tabi tutuluyor. Ulusal bayramlar öncesindeki yarım günler cumhurbaşkanlığı kararnamesiyle ilan ediliyor. Perşembe günü olan bir tatil ile hafta sonu arasına denk gelen sıradan bir Cuma, idari tatil oluveriyor.
Pipeline Çarşamba günü çalışır. Cuma'nın iş günü olduğunu hesaplar. Katkıları Cuma'nın NAV'ıyla takas eder. Cuma'nın tatil olduğu ortaya çıkar. Kullanılan NAV artık yanlış NAV'dır. O hafta katkı yapan her katılımcının bakiyesi, resmi kayıtta var olmayan bir fiyat üzerinden hesaplanmış olur.
Kimse neden fark etmez
BES pipeline'larının hisse senedi işlem sistemlerinden ayrıldığı nokta burasıdır. İşlemlerde, takas tarihi uyumsuzluğu anında bir mutabakat kopukluğu üretir. Biri o öğleden sonra fark eder. Emeklilik pipeline'larında yanlış NAV, matematiksel olarak geçerli bir bakiye üretir. Dahili olarak mutabık kalır. Saklamacı aynı yanlış takvimi kullandıysa, saklamacı ile de mutabık kalır. Yalnızca EGM'nin — gerçek resmi takvimi kullanan — kendi raporlama altyapısı, gönderilen dosyaya karşı bir delta ürettiğinde bozulur.
O noktada katılımcı ekstreleri çoktan üretilmiştir. Bir kısmı postalanmıştır. Bir kaçı kısmi çekimleri gerekçelendirmek için kullanılmıştır.
Yanlış çözüm
İlk içgüdü takvimi daha güncel tutmaktır. Bir feed'e abone olun. Diyanet'ten çekin. Resmi Gazete'yi kontrol eden bir gecelik iş ekleyin. Bu, semptomu tedavi etmektir.
Asıl sorun, iş günü mantığının, takvimin sabit olduğunu varsayan saf fonksiyonların içinde oturuyor olmasıdır. next_settlement_date(trade_date) gibi bir fonksiyonun "bunu hesaplarken kullandığım takvim artık kayıt takvimi olmayabilir" gibi bir kavramı yoktur. Bir tarih döndürür. Tarih kullanılır. Tarih geriye dönük olarak yanlıştır ve sistemde bunu bilen hiçbir şey yoktur.
Olay modeli çözümü
Bir tatil takvimi yapılandırma değildir. Yapılandırma, semantiği değiştirmeden hot-swap edebileceğiniz şeydir. Bir tatil takvimi değişikliği, pipeline'daki takas edilmemiş her pozisyon için semantik bir değişikliktir.
Prodüksiyonda gerçekten işe yarayan şey:
- Her tarih hesaplaması bir tuple olarak saklanır:
(computed_date, calendar_version_id). Takvimin kendisi, herhangi bir domain olayı gibi versiyonlanır; yürürlük zaman damgası ve kaynak referansıyla (Resmi Gazete numarası, cumhurbaşkanlığı kararnamesi, EGM genelgesi) birlikte. - Yeni bir takvim versiyonu geldiğinde pipeline
CalendarAmendedolayları yayar. Aşağı akış servisleri abone olur. Takas edilmemiş her katkı, bekleyen her NAV ataması, henüz onaylanmamış her EGM gönderimi yeni takvime göre yeniden hesaplanır. Eski hesaplama üzerine yazılmaz — yerini alınmış bir olay haline gelir. - Takas ve NAV fiyatlama, açık durum makinelerine dönüşür. Bir katkı "2025-06-13'te takas edilmiş" değildir. Takas tarihi, bir takvim değişikliği tarafından geçersiz kılınmadan gerçekten geçene kadar
PendingSettlement(target=2025-06-13, calendar=v47)durumundadır. - EGM gönderimleri, hesaplandıkları takvim versiyonunu taşır. Geç bir tatil değişikliği raporlama dönemindeki fiili iş günlerini değiştirirse, gönderim onay son tarihinden önce yeniden üretilir, sonra değil.
Bunun maliyeti
Bariz olarak daha fazla depolama. Daha fazla olay hacmi. Anlamlı miktarda mühendislik rahatsızlığı, çünkü çoğu Türk finans geliştiricisi tarihleri skaler olarak ele almak üzere eğitildi. Birine bir takas pipeline'ında today() çağırmanın güvenli bir fonksiyon olmadığını ilk söylediğinizde ciddi bir direnç olur.
Karşılığı şudur: Hükümet bir Salı günü Cuma'nın artık tatil olduğunu ilan ettiğinde pipeline'ın kahramanlığa ihtiyacı olmaz. Bir olay yayar, etkilenen pozisyonları yeniden hesaplar ve katılımcı bakiyeleri, kod çalıştığında var olan takvimi değil, fiili resmi takvimi yansıtır.
Genel ders
Zamansal kuralları kısa sürede değişebilecek bir düzenleyici altında çalışan her sistemin, zamanın kendisini duvar saatinin bir özelliği olarak değil, bir olay akışı olarak modellemesi gerekir. Bu BES için, bireysel emeklilik ürünleri için, sigortanın yasal raporlamaya değen kısımları için ve EGM, SEDDK veya MASAK son tarihlerine dokunan her şey için geçerlidir.
Test basittir. Pipeline'a sorun: Bu takas tarihini hesaplamak için hangi takvimi kullandın ve takas anında kayıt takviminin bu olduğunu kanıtlayabilir misin? Cevap "çalışma zamanındaki yapılandırma dosyasındaki" ise, pipeline geç bir Resmi Gazete ilanı kadar hukuken yanlış bir bilançodan uzaktadır.
Tatil takvimleri yapılandırma değildir. Onlar zamansal olgulardır ve zamansal olguların yeri olay modelidir.