Geohash hücreleri ve Neo4j ile milisaniyeler içinde gemi buluşması tespiti
Her gemi çiftini karşılaştırmanın sorunu
Gemi buluşması (rendezvous) tespiti, ölçeğe bakana kadar basit bir geometri sorunu gibi görünür. Kendimiz izlemek için kurduğumuz bir denizcilik anomali tespit motoru, Kafka üzerinden sürekli akan, binlerce gemiden gelen AIS konum raporlarını alıyor. Her gemi çiftini diğer her çiftle karşılaştırmak ikinci dereceden bir işlem ve bu hacimde milisaniyeye yakın hiçbir şekilde çalışmıyor. Ne saf sorgu ölçekleniyor, ne saf veritabanı şeması.
Ham koordinat yerine geohash hücresi
Ham enlem boylam çiftlerini karşılaştırmak yerine, her konum raporunu 6 hassasiyetinde bir geohash hücresine yerleştiriyoruz. Bu da yaklaşık 1,2 kilometre genişliğinde hücreler veriyor. İki gemi ancak aynı hücreyi paylaşıyorsa ya da komşu hücrelerdeyse yakınlaşma tespiti için önemli sayılacak kadar yakın olabilir. Arama alanı böylece her gemiyle her geminin karşılaştırılmasından, her geminin yakındaki hücrelerdeki bir avuç gemiyle karşılaştırılmasına küçülüyor. Bu GPS doğruluğu hakkında bir iddia değil, bu tespit görevi için gereken ayrıntı düzeyi hakkında bir tasarım kararı. Yutma hacmimizde sorunu çözülebilir hale getiren tek değişiklik buydu.
Neden sadece uzamsal SQL değil, graf
Gemileri ve yakınlık olaylarını ilişkisel bir tabloda satır olarak değil, Neo4j'de bir graf olarak saklıyoruz. Bir buluşma iki geminin bir kez yakın olması değil. Zaman içinde süren, tekrarlayan ya da yoğunlaşan bir ilişki, ve bu tür bir ilişki doğal olarak bir grafta yaşar. İki gemi bir geohash hücresini yakınlaşma sayılacak kadar uzun paylaştığında, :Vessel düğümleri arasına önceden hesaplanmış bir ENCOUNTERED kenarı yazıyoruz, hem gemi kimliğinden hem zaman damgasından indekslenmiş. Sonraki sorular, bu çift daha önce karşılaştı mı, ne sıklıkla, hangi limana yakın, büyüyen bir geçmiş tablosuyla birleştirme yapmak yerine indekslenmiş düğümler üzerinde bir Cypher dolaşımı olarak çalışıyor.
Akışı dürüst tutmak
- Backpressure (geri basınç): Kafka tüketicisi AIS mesajlarını geohash ve graf yazıcılarının gerçekten yetişebildiği hızda işliyor, alt akıştaki bir yazma yavaşladığında bellekte sınırsız mesaj biriktirmek yerine.
- Idempotent projeksiyon: AIS akışları mesajı yeniden gönderir ve yineler, bu yüzden her konum güncellemesi gemi kimliği ve mesaj zaman damgasına göre anahtarlanarak yazılıyor. Yinelenen mesaj yeni satır açmak yerine aynı satırı güncelliyor.
- Outbox: bir buluşma tespit edildiğinde, alt akıştaki tüketicilerin abone olduğu olay graf kenarıyla aynı işlemde bir outbox tablosuna yazılıyor, sonra asenkron yayınlanıyor. Böylece bir tüketici, sonradan geri alınan bir kenar için hiç yakınlaşma olayı görmüyor.
Spring WebFlux burada ne kazandırıyor
Alım hattı Java 21 üzerinde Spring WebFlux ile çalışıyor, çünkü iş yükü CPU hesaplamasından değil G/Ç (I/O) beklemesinden baskın: Kafka tüketicisi, Neo4j yazmaları. Reaktif bir hat, az sayıda iş parçacığının, bir yavaş yazmanın kuyruktaki sonraki mesajı bloke etmesine izin vermeden, çok sayıda eşzamanlı konum akışını yönetmesini sağlıyor. Bu çerçeve seçiminden tek başına daha önemli, çünkü backpressure yönetimini bir diyagramda değil yük altında gerçekten çalışır kılan bu.
Benzerini kuracak bir ekibe söyleyeceğimiz
Veritabanı şemasına dokunmadan önce uzamsal hassasiyeti bilinçli bir tasarım kararı olarak seçin, daha kesin hissettiriyor diye ham koordinata varsayılan olarak yönelmeyin. Süren ya da tekrarlayan ilişkiyi, sürekli kendisiyle birleştirmeniz gereken tablo satırı yerine graf olarak modelleyin. Mesaj yinelenmesini ve backpressure'ı bir kenar durum değil kesinlik olarak ele alın, çünkü gerçek alım hacminde bunlar ilk yılda değil ilk haftada ortaya çıkıyor.