21 puan yazan GN⁺ 2025-10-27 | 3 yorum | WhatsApp'ta paylaş
  • Nanit, bebek uyku durumu analizi için kullanılan video işleme hattında AWS S3 kullanıyordu; ancak saniyede binlerce yükleme nedeniyle PutObject istek maliyeti toplam maliyetin büyük kısmını oluşturuyordu
  • Ayrıca S3 Lifecycle kurallarındaki en az 1 gün saklama sınırı yüzünden, gerçekte 2 saniye içinde işlenen videolar için bile 24 saatlik depolama ücreti ödemek zorunda kalıyordu
  • Bunu çözmek için Rust tabanlı bellek içi depolama sistemi N3 geliştirildi ve S3 yalnızca bir taşma tamponu olarak kullanıldı
  • N3, SQS FIFO üzerinden mevcut işleme hattıyla tamamen uyumlu çalışırken katı sıralama garantisini ve güvenilirliği korudu
  • Sonuç olarak yıllık yaklaşık 500 bin dolar maliyet tasarrufu sağlanırken, aynı zamanda basit ve kararlı bir yapı elde edildi

Arka plan

Video işleme hattına genel bakış

  • Nanit kameraları video parçalarını kaydediyor, Camera Service üzerinden S3 presigned URL istiyor ve ardından doğrudan S3’e yüklüyor
  • AWS Lambda, nesne anahtarını SQS FIFO kuyruğuna yayınlıyor (baby_uid ile shard edilerek); video işleme pod’ları ise SQS’den tüketip S3’ten indiriyor ve ardından uyku durumu çıkarımı yapıyor
  • Bu yapının avantajları
    • S3’e iniş + SQS kuyruklama, kamera yüklemeleri ile video işlemeyi birbirinden ayırarak bakım veya geçici kesintiler sırasında bile video kaybını önlüyor
    • S3 sayesinde erişilebilirlik ve dayanıklılığı doğrudan yönetmek gerekmiyor
    • SQS FIFO + grup ID ile bebek bazında sıra korunuyor, işleme düğümleri de büyük ölçüde stateless kalabiliyor
    • S3 Lifecycle kuralları çöp toplamayı üstleniyor, bu yüzden işlenmiş videoları takip etmeye gerek kalmıyor

Neden değişiklik gerekiyordu

  • PutObject maliyeti baskındı: videolar yalnızca birkaç saniye iniş alanında duran kısa ömürlü nesnelerdi; ancak saniyede binlerce yükleme ölçeğinde nesne başına istek maliyeti en büyük maliyet sürücüsü haline gelmişti
    • Chunk sıklığını artırıp (daha fazla küçük parça göndermek) gecikmeyi azaltmak istenirse, her ek parça yeni bir PutObject isteği anlamına geldiğinden maliyet doğrusal biçimde artıyordu
  • Depolama ikinci kez ücretlendiriliyordu: işleme yaklaşık 2 saniyede bitse bile Lifecycle silme kuralları yaklaşık 24 saatlik depolama maliyeti doğuruyordu
  • Bu nedenle güvenilirliği ve katı sıralama garantisini korurken, normal yolda nesne başına maliyetten kaçınan ve “bekleme için ödeme yapılan” depolamayı en aza indiren bir tasarım gerekiyordu

Plan

  • Tasarım ilkeleri

    • Mimari üzerinden sadelik: akıllı implementasyonlar yerine tasarım seviyesinde karmaşıklığı ortadan kaldırmak
    • Doğruluk: hattın geri kalanı için şeffaf ve tam bir ikame olmak
    • Normal yola göre optimizasyon: yaygın duruma göre tasarlamak ve uç durumlarda S3’ü güvenlik ağı olarak kullanmak; işleme algoritması ara sıra oluşan boşluklara dayanıklı olduğundan, karmaşık garantiler kurmaktan ziyade sadelik tercih edildi
  • Tasarımı yönlendiren etkenler

    • Kısa ömürlü nesneler: segmentler iniş alanında yalnızca birkaç saniye kalıyor
    • Sıralama: bebek bazında katı sıralama (yeniler eskilerden önce işlenmiyor)
    • Verim: saniyede binlerce yükleme, segment başına 2-6 MB
    • İstemci kısıtları: kameraların sınırlı yeniden deneme sayısı var, yeniden gönderime güvenilemiyor
    • Operasyon: bakım veya ölçek büyütme sırasında milyonlarca öğelik backlog tolere edilmeli
    • Firmware değişikliği yok: mevcut kameralarla çalışmalı
    • Kayıp toleransı: çok küçük boşluklar kabul edilebilir, algoritma bunları maskeleyebiliyor
    • Maliyet: normal yolda nesne başına S3 maliyetinden kaçınmak, “bekleme için ödeme yapılan” depolamayı en aza indirmek

Tasarıma genel bakış (N3 normal yol + S3 taşması)

  • Mimari

    • N3, işleme tarafının boşaltması için gereken süre boyunca (yaklaşık 2 saniye) videoyu bellekte tutan özel bir iniş alanı; yalnızca N3 yükü kaldıramadığında S3 kullanılıyor
    • İki bileşen
      • N3-Proxy (stateless, çift arayüzlü)
        • Dışarıya açık (internete bağlı): presigned URL üzerinden kamera yüklemelerini kabul ediyor
        • İç tarafta (özel): Camera Service’e presigned URL veriyor
      • N3-Storage (stateful, yalnızca iç erişim): yüklenen segmentleri RAM’de saklıyor ve pod adreslenebilir indirme URL’siyle SQS’ye kuyruğa alıyor
    • Video işleme pod’ları SQS FIFO’dan tüketiyor ve URL’nin işaret ettiği depodan (N3 veya S3) indiriyor
  • Normal akış (Happy Path)

    • Kamera, Camera Service’ten yükleme URL’si istiyor
    • Camera Service, N3-Proxy’nin iç API’sinden presigned URL istiyor
    • Kamera, videoyu N3-Proxy’nin dış endpoint’ine yüklüyor
    • N3-Proxy bunu N3-Storage’a iletiyor
    • N3-Storage videoyu bellekte tutuyor ve kendisini işaret eden indirme URL’siyle SQS’ye kuyruğa alıyor
    • İşleme pod’u N3-Storage’dan indirip işliyor
  • İki katmanlı fallback

    • Katman 1: proxy düzeyinde fallback (istek bazında)
      • Bellek baskısı, işleme backlog’u, pod arızası gibi nedenlerle N3-Storage yükleme kabul edemezse, N3-Proxy kamera adına S3’e yükleme yapıyor
      • Kamera ise arıza tespit edilmeden önce zaten N3 URL’sini almış oluyor
    • Katman 2: küme düzeyinde yeniden yönlendirme (tüm trafik)
      • N3-Proxy veya N3-Storage sağlıksızsa, Camera Service N3 URL vermeyi durduruyor ve doğrudan S3 presigned URL döndürüyor
      • N3 toparlanana kadar tüm trafik S3’e akıyor
  • Neden iki bileşene ayrıldı

    • Arıza yarıçapı: depolama çökse bile proxy trafiği S3’e yönlendirebiliyor; proxy çökerse yalnızca o düğümün trafiği etkileniyor, tüm depolama kümesi değil
    • Kaynak profili: proxy CPU/ağ yoğun (TLS sonlandırma), depolama bellek yoğun (videoyu tutma); dolayısıyla farklı instance tipleri ve ölçekleme ihtiyaçları var
    • Güvenlik: depolama katmanı asla internete doğrudan temas etmiyor
    • Dağıtım güvenliği: proxy (stateless) güncellenirken depolama tarafındaki (aktif veri tutan) yapı etkilenmiyor

Tasarımın doğrulanması

  • Doğrulanması gerekenler

    • Kapasite ve boyutlandırma: istemci ağları genelindeki gerçek yükleme süreleri, gereken hesaplama gücü ve yükleme tampon boyutu
    • Depolama modeli: her şeyin RAM’de tutulup tutulamayacağı ya da disk gerekip gerekmediği
    • Dayanıklılık: yük dengelemenin ve arıza yapan düğümlerin düşük maliyetle nasıl ele alınacağı
    • Operasyon politikası: GC ihtiyacı, retry beklentileri, GET sırasında silmenin yeterli olup olmadığı
    • Bilinmeyen bilinmezler: fikir gerçek dünyayla buluştuğunda hangi uç durumların ortaya çıkacağı
  • Yaklaşım 1: sentetik stres testi

    • Farklı eşzamanlılık seviyeleri, yavaş istemciler, sürekli yük ve işleme kesintileriyle sistemi sınırına kadar zorlayan bir yük üretici kuruldu
    • Amaç: limit noktalarını bulmak, beklenmeyen darboğazları tespit etmek ve kapasite planlaması için deterministik bir başlangıç çizgisi elde etmek
  • Yaklaşım 2: prodüksiyon PoC (mirror mode)

    • Sentetik testler gerçek kamera davranışını kopyalayamıyordu: kararsız Wi-Fi, farklı firmware sürümleri, öngörülemeyen ağ koşulları
    • Mirror mode: n3-proxy önce S3’e yazıyor (prodüksiyon korunuyor), ardından PoC N3-Storage’a da yazıyor (canary SQS + video processor’a bağlı)
    • Hedef kohortlar: firmware sürümüne veya Baby-UID listelerine göre
    • Veri eşliği: PoC ile prodüksiyonun uyku durumu sonuçları karşılaştırılıyor, farklar inceleniyor
    • Gözlemlenebilirlik: yol bazlı panolar (N3 vs S3), kuyruk derinliği, gecikme/RPS, hata bütçesi, egress analizi
    • Feature flag’lerin (Unleash kullanılarak) önemi büyüktü: dağıtım yapmadan kohortların gerçek zamanlı değiştirilebilmesini sağladı; dar dilimler (eski firmware, zayıf Wi-Fi kullanan kameralar) test edilip sorun olduğunda anında geri alınabildi
  • Neler bulundu

    • Darboğazlar: CPU’nun büyük kısmını TLS sonlandırma tüketiyordu; AWS burstable networking ise kredi tükendiğinde throttling’e yol açıyordu
    • Yalnızca bellek kullanan depolama uygulanabilirdi: gerçek yükleme süresi dağılımı ve eşzamanlılık sayesinde çalışma setinin RAM’de güvenli payla tutulabildiği doğrulandı, diske gerek kalmadı
    • TCP timestamp overhead’i: aktarılan toplam baytın yaklaşık %85’i ACK frame’lerinden oluşuyordu; TCP timestamp’leri devre dışı bırakmak (sysctl -w net.ipv4.tcp_timestamps=0) ile ACK başına 12 bayt tasarruf edildi
      • Risk: aynı sokette çok yüksek miktarda veri gönderilirse sıra numaralarının dolanması ve gecikmiş paketlerin yanlış birleştirilmesiyle bozulma oluşabilir
      • Azaltım: (1) her yükleme için yeni soket, (2) n3-proxyn3-storage soketlerini yaklaşık 1 GB veri aktarımından sonra yenilemek
    • Bellek sızıntısı: ilk yayından sonra n3-proxy belleği istikrarlı biçimde artıyordu
      • jemalloc profillemesi, artışın bağlantı başına hyper BytesMut tamponlarında olduğunu gösterdi
      • Bazı istemci bağlantıları aktarım sırasında takılı kalıyor ve temizlenmediği için tamponlar elde tutuluyor, bu da belleğin sürekli artmasına yol açıyordu
      • Düzeltme: soketleri kısa ömürlü yapmak ve süre sınırları uygulamak
        • Keep-alive kapatıldı: her yükleme tamamlandıktan sonra bağlantı hemen kapatıldı
        • Timeout’lar sıkılaştırıldı: header/soket timeout’larıyla takılı kalan yüklemeler sonlandırıldı ve tamponlar serbest bırakıldı

Depolama

  • Bellek içi depolama

    • En basit yoldan başlandı: bellek içi depolama ile I/O ayarlarından kaçınıldı ve sezgisel veri yapıları kullanıldı
    • Videolar Arc<DashMap<Ulid, Bytes>> içinde saklandı; her video yüklemesi bytes_used değerini artırıyor, her indirme ise videoyu silip bu değeri azaltıyordu
    • Kapasitenin yaklaşık %80’inin üzerine çıkıldığında OOM’dan kaçınmak için yüklemeler reddedilmeye başlanıyor ve n3-proxy’ye yükleme URL’si imzalamayı durdurma sinyali veriliyordu
    • control handle ile yüklemeleri ve garbage collection’ı elle duraklatmak mümkün hale getirildi
  • Zarif yeniden başlatma

    • Yalnızca bellek kullanan depolama nedeniyle yeniden başlatma sırasında ilerlemekte olan verilerin düşmemesi gerekiyordu
    • Zarif yeniden başlatma süreci
      • Pod’a SIGTERM gönderiliyor (StatefulSet, rolling update’i aynı anda bir pod olacak şekilde yapıyor)
      • Pod Not Ready durumuna geçiyor ve Service’ten çıkarılıyor (yeni yükleme gelmiyor)
      • Daha önce yüklenmiş videoların indirilmesine hizmet etmeye devam ediyor
      • İndirmeler durduğunda (yakın zamanda okuma yok → işleme boşaltıldı)
      • Açık isteklerin tamamlanması bekleniyor
      • Ardından yeniden başlatılıp sonraki pod’a geçiliyor
    • Sağlıklı durumda pod’lar birkaç saniye içinde boşalıyor
  • GC

    • İki temizleme mekanizması kullanıldı
      • İndirme sırasında silme: video indirildikten hemen sonra siliniyor; PoC’de yeniden indirme sayısının sıfır olduğu görüldü, video processor zaten içeride retry yaptığı için veri tutmaya veya “işlendi” durumunu izlemeye gerek kalmadı
      • Geride kalanlar için TTL GC: indirme sırasında silme, processor’ın atladığı segmentleri kapsayamıyor (indirilmezse silinmiyor)
        • Bu yüzden hafif bir TTL GC eklendi: bellek içi DashMap periyodik olarak taranıyor ve yapılandırılabilir eşiğin (ör. birkaç saat) üzerindeki kayıtlar kaldırılıyor
    • Bakım modu: planlı işleme kesintileri sırasında dahili kontrol yoluyla GC geçici olarak durdurulabiliyor, böylece tüketim durmuşken videolar silinmiyor

Sonuç

  • Temel kazanımlar

    • S3’ü fallback tamponu, N3’ü ise ana iniş alanı olarak kullanarak sistemi basit ve güvenilir tutarken yıllık yaklaşık 500 bin dolar tasarruf elde edildi
    • Ana içgörü: çoğu “build vs buy” kararı özelliklere odaklanır, ancak ölçek büyüdüğünde ekonomi hesabı değiştirir
      • Kısa ömürlü nesneler için (normal çalışmada yaklaşık 2 saniye) replikasyon veya sofistike dayanıklılık gerekmiyor; basit bir bellek içi depo yeterli olabiliyor
      • İşleme geciktiğinde veya bakım nesne ömrünü uzattığında ise S3’ün güvenilirlik garantilerine ihtiyaç duyuluyor
      • İki dünyanın en iyi yanı: N3 normal yolu verimli şekilde yönetiyor, S3 ise nesnelerin daha uzun yaşaması gerektiğinde dayanıklılık sağlıyor
      • N3’te sorun olursa (bellek baskısı, pod çökmesi, küme sorunu) yüklemeler sorunsuz biçimde S3’e failover ediyor
  • Başarı faktörleri

    • Problemin önceden net tanımlanması: kısıtlar, varsayımlar ve sınırlar kapsamın gereksiz genişlemesini engelledi
    • Mirror mode PoC ile erken doğrulama: darboğazlar (TLS, ağ throttling’i) bulundu ve varsayımlar büyük taahhütlerden önce test edildi
      • aşırı mühendisliğin ve geri dönüp düzeltmenin önüne geçildi
  • Böyle bir şey ne zaman inşa edilmeli

    • Ancak yeterli ölçekte anlamlı maliyet tasarrufu mümkünse ve basit çözümü mümkün kılan özel kısıtlar mevcutsa özel altyapı düşünülmeli
    • Sistemi kurup sürdürmenin mühendislik maliyeti, ortadan kaldırılan altyapı maliyetinden düşük olmalı
    • Nanit örneğinde, özel gereksinimler (geçici depolama, kayıp toleransı, S3 fallback’i) bakım maliyetini düşük tutacak kadar basit bir sistem kurmayı mümkün kıldı
    • Bu iki koşul yoksa yönetilen servislerde kalmak daha doğru
    • Tekrar yaparlar mıydı? Evet; sistem prodüksiyonda kararlı şekilde çalışıyor ve fallback tasarımı sayesinde güvenilirlikten ödün vermeden karmaşıklıktan kaçınılabiliyor

3 yorum

 
click 2025-10-28

Sadece ec2 ya da eks pod’larının videoyu doğrudan yükleyip işlemesi olmaz mıydı diye merak ediyorum.
Proxy’ye kadar geliştirdilerse, pod yüküne göre eks otomatik ölçeklendirmesi de fazlasıyla mümkün görünüyor.
Video işleme tarafında genelde dosyanın tamamını belleğe almak gerekmez; her instance’ın yerel SSD’sinde geçici dosya oluşturup işleselerdi, s3 fallback’e de gerek kalmazdı gibi geliyor.

 
t7vonn 2025-10-28

Sunucusuz mimari ile S3'ün kötü kullanıldığı bir örnek gibi görünüyor.
Ama çözüm de daha da tuhaf görünüyor.

 
GN⁺ 2025-10-27
Hacker News görüşleri
  • Gerçekten çok faydalı bir yazıydı. Böyle teknik yaklaşım sürecinin paylaşılmasını çok seviyorum
    Aynı problemi bizzat yaşamıyor olsam bile, nasıl bir düşünce tarzıyla yaklaşıldığını görmek bile çok şey öğretiyor

  • Dürüst olmak gerekirse, buna en baştan serverless kullanılmasaydı çok daha temiz olurdu gibi geliyor
    Saniyelik veriyi zorla AWS serverless paradigmasına sıkıştırmaya çalışırken gereksiz maliyet ve karmaşıklık ortaya çıkmış gibi
    Yine de bellek tabanlı çözüme geçmeleri iyi bir tercih olmuş

    • Sonuçta ağ işleme kapasitesi ve RAM sağlamak için ağır instance'lar çalıştırmak zorunda kalmışlar, ama CPU neredeyse hiç kullanılmıyor
      TLS handshake'in CPU'yu çok kullandığını söylemişler ama asıl darboğazın bu olduğunu sanmıyorum
      Yine de böyle bir iş akışına özel sistem tasarımı denemeleri ilginçti
  • Aslında başlığın dediği gibi “S3'ü kendileri implemente etmişler” değil, S3'ün önüne bellek önbelleği koymuşlar
    Havalı ama tam anlamıyla şirket içi bir S3 alternatifi değil

    • Evet, ama mevcut istemcileri değiştirmek zor olduğu için sanırım S3 API'sini taklit etmek zorunda kalmışlar
      Başlık her ne olursa olsun ilginç bir projeydi
    • Neden ille de bellek önbelleği olması gerektiğini anlamadım. Local storage da yeterli olurdu gibi
  • HN tarzında söylemek gerekirse, Nanit şirketinin kendisi hakkında konuşmak istiyorum
    Nanit, bulut tabanlı bebek monitörü kamerası işletiyor. Tüm video ve ses E2EE olmadan yükleniyor
    Donanım pahalı ve abonelik olmadan neredeyse kullanılamıyor. Üstelik uyku takibi özelliğini açmak için 200 dolarlık bir stand da satın almanız gerekiyor
    Böyle bir yapının sonuçta buluta bağımlı modeli güçlendirmesi üzücü
    Ama yine de bu yazıdaki gibi S3 bağımlılığını azaltıp kendi depolamalarına geçmeleri iyi bir iş olmuş

    • Ben memnun bir müşteriyim. Nanit'i seçme nedenim basitçe “iyi çalışmasıydı”
      Diğer ürünlerde uygulama kararsızdı. Local-first + E2EE bir çözüm güzel olurdu ama pratikte kullanılabilirlik daha önemliydi
    • E2EE konusunda, bu durumda mesele sadece depolama değil, bulutta video analizi yapılması, dolayısıyla E2EE zaten mümkün değil
      Gerçek E2EE isteniyorsa analiz local'de yapılmalı ve yalnızca sonuçlar yüklenmeli
    • Ben doğrudan ahşap bir aparat yapıp kullanmıştım, o zamanlar yazılımsal kısıtlama yoktu. Şimdi değişti mi merak ediyorum
    • “Kendi barındırdığınız video zor değildir” sözüne katılmıyorum. Ortalama kullanıcı açısından bu hiç düşünülmeyecek bir seçenek
    • Aslında bir bebek monitörü için bulutun gerçekten gerekli olup olmadığından da emin değilim. Sonuçta yakında güvenilir bir yetişkin oluyor
  • Bu yazı, sanki kendi sorununu kendi yaratıp sonra çözdüğünü kutlama havası veriyordu
    En baştan local storage donanımı satsalardı daha basit ve daha ucuz olurdu
    Bulut merkezli tasarım artık 2015 tarzı bir yaklaşım gibi geliyor

  • Yazı harikaydı ama S3 üzerinde 'delete on read' uygulandığında maliyet azaltma etkisinin ne olduğunu da merak ettim
    Eğer S3 saniye bazlı faturalandırma yapıyorsa tasarruf oldukça büyük olmuş olabilir
    Ayrıca bu çözüm aslında S3'ün 'reduced redundancy' seçeneğine de benziyor

  • 500 bin dolar tasarruf ettiklerini söylüyorlar ama toplam maliyetin ne olduğunu bilmiyoruz
    Bunun 500 bin doların içinden 500 bin 1 dolar mı olduğu, yoksa 55 milyon doların içinden 500 bin dolar mı olduğu anlamı değiştirir

    • Evet, aslında böyle bir sorun teknolojiyle değil, AWS ile fiyat pazarlığı (PPA) ile de çözülebilir
    • Yine de S3 hâlâ fiyat/performans açısından iyi bir hizmet, bu yüzden biz de AWS'den ayrılsak bile S3 ve SQS'yi kullanmayı sürdürmeyi planlıyoruz
  • En başta yanlış mimari seçilmiş, sonra da üstü önbellekle kapatılmış gibi hissettiriyor
    Ortalama 2 saniyelik videoları S3'e yüklemek için yedekli depolama dışında bir sebep yok
    Sunucuda doğrudan işleselerdi S3, SQS ve Lambda'nın hepsini ortadan kaldırabilirlerdi

    • Evet, S3 depolama içindir, işleme için değil
      Böyle basit bir problemi neden bu kadar karmaşık hale getirdiklerini anlamıyorum
  • Bu bana klasik “uygulama geliştirmeye odaklan, altyapıyı sadeleştir” dersini hatırlattı
    Hatta önbelleği doğrudan video işleme sunucusunun içine koymak daha iyi olabilirdi

  • Başlık aslında “S3'ü yanlış kullandık” olsaydı daha doğru olurdu

    • Gerçekten de neden milyonlarca kısa ömürlü geçici dosyayı S3'te tutmaya çalıştıklarını anlamıyorum
      Sonunda kendi bellek deposunu yapmışlar ama bunun yerine Redis gibi bir şey kullansalar olurmuş
      Kendi yaptıkları sistem çökerse videolar kayboluyor mu?
      En baştan Kinesis veya SQS'ye gönderseler çok daha iyi olurdu