Sorun: L1 her node’da ayrı
HybridCache’in L2’si (Redis) paylaşımlıdır — tüm node’lar aynı Redis’i görür, tag mark’ı herkesçe okunur. Ancak L1 (in-process memory) her node’da ayrıdır. Bir node tag’i düşürüp kendi L1’ini temizlese de, diğer node’ların L1’inde aynı veri TTL dolana kadar bayat kalır.
Çözüm: bir node tag düşürdüğünde EventBus üzerinden diğer node’lara haber verip onların da sadece L1’lerini düşürmesini sağlamak.
EventBusRemoteTagBroadcaster
IRemoteTagBroadcaster’ın EventBus (MassTransit + RabbitMQ) backend’li implementasyonu (src/.../Application/SeedWork/Caching/EventBusRemoteTagBroadcaster.cs). IHybridRequestCache.RemoveByTagAsync içinden, BroadcastTagInvalidation açıksa tetiklenir.
Lifetime deseni:
IHybridRequestCache Singleton, dolayısıyla broadcaster da Singleton; ama IEventBus Scoped. Bu yüzden ctor injection yerine IServiceScopeFactory tutulup her broadcast’te scope açılır — standart “Singleton consumes Scoped” pattern’i. Tag invalidation seyrek olduğu için maliyet düşük.NodeId bilerek static’tir: aynı process içinde hem broadcaster (publish) hem handler (consume) aynı değeri okur — echo tespiti bunun üzerine kuruludur.
CacheInvalidationIntegrationEvent
Yayınlanan event (src/BuildingBlocks/.../Contracts.Events/Cache/CacheInvalidationIntegrationEvent.cs):
Echo guard + cascade önleme
ReceiverCacheInvalidationIntegrationEventHandler (src/.../Application/IntegrationEventHandlers/CacheInvalidation/) iki önlem taşır:
| Önlem | Neyi engeller |
|---|---|
Echo guard (SourceNodeId == NodeId) | Origin node kendi broadcast’ini tüketip L1’ini iki kez düşürmesin. |
RemoveByTagLocallyAsync | Receiver tekrar broadcast yapmasın → sonsuz cascade önlenir (RemoveByTagAsync broadcast tetiklerdi). |
Konfigürasyon
| Ortam | BroadcastTagInvalidation |
|---|---|
| Single-node (dev, küçük prod) | false — broadcast gereksiz, RabbitMQ trafiği boşuna. |
| Multi-instance / multi-pod | true — yoksa diğer node’ların L1’i invalidate edilemez, bayat veri döner. |
BuildingBlocks.Caching.AddCache default olarak NoopRemoteTagBroadcaster’ı TryAdd ile koyar. Application katmanı EventBus aktifken bunu gerçek impl ile Replace eder (AddSingleton, TryAdd değil):
İlgili
Invalidation
RemoveByTagAsync vs RemoveByTagLocallyAsync.Integration Events
IntegrationEvent base ve consumer kaydı.RabbitMQ Topology
Exchange, routing key, queue.
Cache Mimarisi
Genel bakış.