7.0 KiB
7.0 KiB
decisions.md — Port Sırasında Alınan Kararlar
Geri-dönülebilir kararlar burada kayıt altına alındı; CLAUDE.md gereği seçildi, gerekçe yazıldı, devam edildi.
Mimari
- Klasör yapısı: Go repo kökünde (
go.mod+main.go+internal/). Kullanıcı onayıyla.loadtest/ayrı modül.frontend/yerinde. EskiSource/referans olarak bırakıldı. - WebSocket kütüphanesi:
gorilla/websocketv1.5.3. Kullanıcı onayıyla. En yaygın, en okunaklı; tek-yazıcı hub deseni doğrudan uyuyor. Modül önbelleğinde mevcut olduğundan ağ gerekmedi. - Concurrency: RWMutex + bağlantı-başına-tek-yazıcı (actor yerine). İzin verilen iki seçenekten (#22) bu seçildi çünkü gerçek race "tek yazıcı +
doneseçimliSend" ile zaten çözülüyor; RWMutex versiyonu yeni Go geliştiricisi için belirgin biçimde daha okunaklı. Actor (reply-channel) modeli senkron request/response handler'larına ceremoni ekler. →REVIEW.md'de onaya açık. - Modül yolu:
git.saqut.com/saqut/mwse(engine),git.saqut.com/saqut/mwse-loadtest(yük testi). Mantıksal isim; repo dizininin Türkçe karakterli olması etkilemez. - Dolu outbound buffer politikası: mesajı düşür, bağlantıyı KAPATMA. İlk versiyon (gorilla hub örneği gibi) yavaş peer'i komple düşürüyordu; yük testinde bu, ani trafik altında zincirleme kopmalara yol açtı (relay'de %92 kayıp). Best-effort relay için doğru politika: tek frame'i düşür, bağlantıyı koru. Gerçekten ölü peer zaten write-deadline ile temizlenir. Düzeltme sonrası teslim %98.5'e çıktı.
Client.Dropped()ile gözlemlenebilir.
Node'daki hataların düzeltilmesi (tel sözleşmesi korunarak)
Kullanıcı talimatı: "tüm fonksiyonların aynı olması önemli değil; amaç/iş aynı kalsın." Node kaynağındaki bariz bug'lar doğru çalışacak şekilde yeniden yazıldı; mesaj isimleri/şekilleri (SDK'nın gördüğü tel) korundu:
- Pairing akışı (Auth): Node'da
accept/pairyanlış tarafınpairssetini kontrol ediyordu (akış asla tamamlanmıyordu). Doğru el sıkışma uygulandı:request/pairisteyen tarafı kaydeder + hedeferequest/pairsinyali;accept/pairisteyenin gerçekten istek attığını doğrular +accepted/pairsinyali. Frontend'in beklediği{from, info}yükü gönderiliyor. is/reachable: NodeotherPeer.pairs.has(to)(kendi id'sini) kontrol ediyordu — her zaman false. Doğrusu: hedef pairing istemiyorsa VEYA gönderenle eşleşmişse erişilebilir.- Davet sistemi (Room
accept/invite-room/reject/invite-room): NodeSetüzerinde.includes/.filterçağırıyordu (çalışmaz, HANDLER_ERROR) vejoinType=='invite'kontrolü ters çevrilmişti. Doğru akış: sadece davet odaları, sadece bekleme listesindeki id'ler. closeroom: Noderoom.owner === client.idile Client nesnesini string'e kıyaslıyordu (her zaman false). Go'daOwnerID stringtutuluyor; doğru kıyas.create-roomdoğrulaması: Node tanımsızCreateRoomValidatedeğişkenine referans veriyordu (throw). joi şemasının niyetini taşıyan hafif bir doğrulama yazıldı. (Not: Node'un doğrulama hatasında döndürdüğümessagesanahtarı —tekil değil— korundu.)joinroominvite dalı: Node davet gönderdikten sonraNOT-FOUND-ROOMdöndürüyordu (yanıltıcı).{status:"success", message:"INVITE-REQUESTED"}ile değiştirildi.pack/topairing kontrolü: NodeotherPeer.pairs.has(to)(kendi id'si) kullanıyordu. Doğrusu hedefin gönterene pairing'i:other.HasPair(c.ID).
Bilinçli sadakat (Node davranışı korundu)
request/to→ 'E' yanıtı: SDK request'i action 'R' ile gönderdiğinden generic dispatcher hemen[null, id, 'E']yanıtlar; eş cevabıresponse/toile sonra gelir. Bu Node ile bire bir aynı (ve aynı uyumsuzluğu taşır). Dispatcher'ı özel-durumlamak tel sözleşmesini değiştirebileceğinden DOKUNULMADI. →REVIEW.md.auth/infoçift gönderim: Hem pair hem roompair olan bir peer ikipair/infoalır (Node ile aynı).- Heartbeat: 10sn
saQutping; pong "saQut" değilse bağlantı kapanır (Node ile aynı).
Ölçek ayarı & leak sağlamlaştırma (yüksek bağlantı + bitmek bilmeyen trafik)
Hedef: çok yüksek bağlantı sayısı + sürekli mesaj trafiği; limitleri/poolları yüksek tut, belleği bol kullan ama leak yok.
- Yapılandırılabilir, yüksek limitler (
internal/configConnConfig, env ile):MWSE_OUTBOUND_BUFFER(varsayılan 1024) — bağlantı başına gönderim kuyruğu. Bellek notu: kanal ~24 B/slot önceden ayrılır → ~24 KiB/bağlantı (100k bağlantıda ~2.4 GiB boştayken). Düşük bellekli host'ta düşür, burst'lü üreticide yükselt.MWSE_MAX_MESSAGE_SIZE(varsayılan 16 MiB) — büyük tünel payload'ları (#30) için yüksek.MWSE_READ_BUFFER/MWSE_WRITE_BUFFER,MWSE_PING_INTERVAL,MWSE_PONG_WAIT,MWSE_WRITE_WAIT.
- gorilla
WriteBufferPool(paylaşımlısync.Pool) — yazma scratch buffer'ları bağlantılar arasında yeniden kullanılır; yüksek bağlantı sayısında büyük bellek tasarrufu, leak yok (GC yönetir). 150 bağlantı + ağır trafikte RSS ~43 MB ölçüldü. - Dolu buffer'da düşür-ama-kapatma (bkz. #5): yüksek buffer + bu politika → relay teslimat %98.5'ten %99.98'e çıktı (150 bağlantıda 630974 gönderim, 99 düşüş).
Leak düzeltmeleri (sınırsız büyümeyi önle)
- Pairing ters-indeksi (
pairedBy). Önceki halde tek-yönlü bekleyen istekler (hedef yanıt vermeden koparsa) uzun ömürlü istemcininpairssetinde kalıcı çöp bırakıyordu → churn altında sınırsız büyüme. Çözüm: her istemci hem giden (pairs) hem gelen (pairedBy) kenarları tutar;AddPair/RemovePairiki tarafı da günceller (kilitler iç içe değil → deadlock yok); disconnect'teForgetPeerile X'e değen TÜM kenarlar O(derece) temizlenir (tüm istemcileri taramadan). - Davet bekleme listesi temizliği. İstemci
waiting(beklediği oda id'leri) tutar; disconnect'te ilgili odalarınwaitingInvitedsetinden düşülür → ölü id birikmez. reallocartık farklı adres verir. Node "önce release sonra lock" yapıyordu → tek istemcide aynı adresi döndürüyordu (işlevsiz). Düzeltme: önce yeni adresi al, sonra eskiyi bırak (eski rezerve kaldığından yeni mutlaka farklı); adres alanı tükenmişse eskiyi koru, fail dön.- Goroutine sızıntısı yok: writePump/pingLoop/readLoop hepsi
done/conn-close ile çıkar; gerçekten ölü peer write-deadline ile temizlenir.
#27 paritesi — eklenen
ifexistsJoin: create-room'da isim çakışırsa hata yerine mevcut odaya katıl (tek tur "create or join").
Session varsayılanları
packrecaive/packsending/notifyPairInfo/notifyRoomInfo varsayılanları NewClient constructor'ında set ediliyor (listener sırasından bağımsız her zaman mevcut). Session servisinin connect hook'u parite için yine de bunları yeniden uyguluyor.