Feat/m7 flush addons - #4
Merged
Merged
Conversation
Eşik aşıldığında (cpu>90 / ram>90 / disk>95 / error|critical log) 30 saniyelik
gönderim turu beklenmez; spool anında boşaltılır. Projenin ana içgörüsünün
("çöküş anında göndermek çok geç") kod karşılığı budur.
flush.py yalnızca KARAR üretir, yan etki üretmez: evaluate() eşiği,
cooldown_active() sel korumasını, build_crash_snapshot() wire satırını verir.
Spool'a yazmak, shipper'ı çağırmak ve last_flush_at'i damgalamak döngünün işi.
Kararlar:
- ram eşiği yüzdedir ama şemada yalnızca ram_used_mb var. collect() artık
MetricReading döndürüyor: sample (spool'a gider) + ram_percent (yalnızca
eşik karşılaştırması, hiçbir yere yazılmaz). Yüzde MetricSample'a
konamazdı — o nesne asdict() ile doğrudan wire gövdesi oluyor ve collector
sözleşme dışı alanı extra="forbid" ile 422'liyor.
- crash_processes eklentisi kapalıyken de (varsayılan) her flush bir
crash_snapshots satırı yazar, processes boş kalır. Metrikler "CPU %95'ti"
der; bu satır "flush gerçekten attı" der. CLAUDE.md §7'nin lafzından sapma,
bilinçli.
- Aynı anda birden çok eşik tutabilir ama sütun tek değer alır:
log > ram > cpu > disk. log en üstte çünkü diğerleri "yük yüksek" derken
log "bir şey bozuldu" der.
- cooldown duvar saatiyle ölçülür, monotonic ile değil: monotonic her yeniden
başlatmada sıfırlanır, cooldown da onunla birlikte sıfırlanırdı.
- last_flush_at gönderimden ÖNCE damgalanır. Collector erişilemezken eşik her
turda yeniden tutar; damga sonra yazılsaydı her tur yeni bir snapshot
üretilir, spool kesinti boyunca boş yere şişerdi.
- Pause'da flush YOK. Acil gönderim de "buluta yükleme"dir; toplama ve spool
sürer, gönderim durur. Komut poll'ünün aksine istisnası yok — flush
telemetridir, kontrol mesajı değil.
seconds_since_iso() bilerek negatif de dönebiliyor: sistem saati geri
alındığında cooldown'ı süresiz kilitlememek için kararı çağıran veriyor.
agent 0.2.0 -> 0.3.0. Testler M7 commit 2'de.
Buradaki bozuklukların hepsi SESSİZ: eşik yanlış alana bağlanırsa, cooldown ters tarafa kayarsa ya da pause'da flush atarsa hata mesajı çıkmaz. Agent çalışmaya devam eder; yalnızca çöküş anındaki veri ya hiç gelmez ya da gereksiz sel olur. Kapsam: - evaluate — eşik `>` ile karşılaştırılır (`>=` değil: disk %95'te sabit duran makine her turda flush ederdi), None eşiği aşmış sayılmaz, ram eşiği YÜZDEye bağlı (ram_used_mb'ye değil), sebep önceliği log > ram > cpu > disk, eşikler config'ten okunur. - cooldown — damga yoksa/çözülemiyorsa flush serbest, gelecekteki damga (saat geri alınmış) cooldown'ı kilitlemez. - build_crash_snapshot — eklenti kapalıyken satır yine yazılır (processes boş), açıkken ilk 5 süreç; RAM tetiklemesinde belleğe göre sıralanır, ölen süreç atlanır, psutil hatası snapshot'ı düşürmez. - loop._maybe_flush — pause'da hiç flush yok, cooldown hem snapshot'ı hem gönderimi bastırır, backoff'ta snapshot alınır ama gönderilmez, snapshot gönderimden ÖNCE spool'a girer, başarısız gönderim de cooldown'ı başlatır. - _collect_logs yalnızca error|critical sayar; okuma hatasında 0 döner. İki sözleşme testi, birbirini import etmeyen iki katmanı bağlar: - flush.REASON_ORDER == collector'ın CrashSnapshotIn.trigger_reason Literal'ı, - asdict(MetricSample) ⊆ collector'ın MetricIn alanları (extra="forbid"). İkisi de bozulursa hata yalnızca gerçek bir çöküş anında, 422 olarak görünürdü. 35 sabotaj denendi, 35'i yakalandı. Biri ilk turda kaçtı: "damga diske yazılmaz" sabotajı başarılı gönderim yolunda görünmüyordu, çünkü _send_spool last_send için state'i zaten kaydediyor — yani o testteki "diske yazıldı" iddiası yanlışlıkla tatmin oluyordu. İddia, gönderimin yapılmadığı yollara (backoff / 500) taşındı; kalıcılığın tek gerçek kanıtı orası. 345 test.
…d_addons filtresi Çekirdek metrikler her zaman toplanır; eklentiler yalnızca config'in enabled_addons listesinde adı geçiyorsa okunur, kapalıyken sütun null kalır. Varsayılan kapalı olmasının sebebi ölçüm maliyeti değil ANLAM maliyeti: her makinede olmayan bir sütun (sensör, NVIDIA kartı, Linux'a özgü load average) açıkça istenmeden doldurulmaz. Kararlar: - Sıcaklık için "bulduğun ilk sensörü al" YOK. Bilinen CPU sensörleri sırayla denenir: coretemp (Intel) -> k10temp (AMD) -> cpu_thermal (ARM) -> acpitz (anakart). Sıra daralan güvenilirliktedir. Sebep şemada: sensör adını tutan sütun yok, yani temperature_c'nin neyi ölçtüğü satırdan okunamaz — aynı grafikteki iki nokta iki farklı şeyi anlatamaz. - GPU nvidia-smi ile okunur, kütüphaneyle değil. journald için verilen kararın aynısı: sürücüyle birlikte zaten gelen programı çağırmak, kurulum maliyeti olan bir bağımlılık eklemekten iyidir. - Yeni modül core/gpu.py (CLAUDE.md §3'teki dosya listesinde yok — bilinçli ekleme): GPU iki yere veri veriyor (model -> envanter, kullanım -> metrik) ve ikisi de aynı programa dayanıyor. Ayrı modül olmasaydı nvidia-smi'nin komut satırı ve hata halleri iki dosyada tekrarlanırdı. - GpuReader "program hiç yok" halini HATIRLAR ve bir daha denemez; zaman aşımı gibi geçici hatalar hatırlanmaz. Olmayan bir programı 5 saniyede bir başlatmaya çalışmak sürekli ve boş bir maliyettir. - Çoklu GPU'da yalnızca ilk kart okunur — şemada tek sütun var; kart başına satır tutmak zaman serisinin şeklini değiştirirdi. - swap yoksa 0 döner, None değil: "swap'ı olmayan makine" ölçülemeyen değil, sıfır kullanan makinedir. - external_ip'i agent GÖNDERMEZ (envanterde alan bile yok). Cihazın kendi dış IP'sini bildirmesi, doğruluğu cihazın insafına bırakırdı; değeri isteği gerçekten alan taraf yazacak (collector, sıradaki commit). Kullanıcının açık/kapalı tercihi yine payload'daki enabled_addons ile gidiyor. - Eklenti adları artık tek yerde (config.KNOWN_ADDONS); tanınmayan ad HATA değil UYARI üretir. "temprature" yazan kullanıcı yüzünden agent başlamamalı, ama sütunun neden hep null olduğunu da günlerce aramamalı. collect() artık config alıyor: kullanıcı bir eklentiyi açtığında değişiklik servis yeniden başlatılmadan geçerli oluyor (döngü config'i her tick okuyor). Dev makinesinde canlı doğrulandı: temperature_c=45.0, swap_used_mb=0, load_avg 1.09/1.00/1.14, gpu_usage_percent=15.0, gpu_vram_used_mb=12, gpu_model="NVIDIA GeForce RTX 4050 Laptop GPU". Testler sıradaki commit'te.
… IP sözleşmesi Eklentilerin ortak riski hata üretmemesidir: kapalıyken sessizce açık olmak ya da açıkken sessizce yanlış şeyi ölçmek. İkisi de log'a düşmez — biri kullanıcının istemediği veriyi toplar, diğeri sütuna makineden makineye farklı anlam taşıyan bir sayı yazar. Testler donanımı değil KARARI doğruluyor: hangi sensör seçiliyor, hangi alan ne zaman doldu, hata nasıl yutuldu. Kapsam: - Filtre: kapalıyken yedi eklenti sütununun hepsi null; bir eklentiyi açmak diğerlerini açmıyor; liste her collect'te config'den yeniden okunuyor (eklenti açmak servisi yeniden başlatmayı gerektirmiyor). - Sıcaklık: coretemp, acpitz'in önüne geçiyor; tanınmayan sensör HİÇ kullanılmıyor (şemada sensör adı sütunu olmadığı için "ne bulursan al" aynı sütuna iki farklı şeyi yazardı); okumasız sensör atlanıyor; sensors_temperatures'ın hiç tanımlı olmadığı platformda ve okuma hatasında ölçüm turu düşmüyor. - swap: sıfır bir ÖLÇÜMDÜR, null değil. Grafikte null "ölçemedim", 0 "kullanılmıyor" der. - load_avg: üç alan tek çağrıdan geliyor, birlikte doluyor ya da birlikte null oluyor. - GPU: CSV ayrıştırma, çoklu kartta ilk satır, [N/A] -> null, sıfırdan farklı çıkış kodu -> null, zaman aşımı -> null. Program yoksa BİR kez deneniyor ve bir daha denenmiyor; zaman aşımı ise kalıcı sayılmıyor. Eklenti kapalıyken nvidia-smi hiç çalıştırılmıyor — kapalı bir eklentinin bedeli sıfır olmalı, süreç başlatıp sonucu atmak "kapalı" demek değildir. - Envanter: gpu_model yalnızca eklenti açıkken doluyor. external_ip envanterde ALAN OLARAK YOK — sözleşme testi, agent'ın kendi dış IP'sini bildirmesinin bir daha mümkün olmamasını sağlıyor; kullanıcının tercihi yine enabled_addons ile gidiyor. - Config: tanınmayan ad uyarı üretiyor ama agent'ı durdurmuyor; tanınan adlar sessiz geçiyor; KNOWN_ADDONS'taki her ad config.example.toml'da anılıyor (koda eklenip örnekte anılmayan eklenti, kullanıcının varlığını hiç öğrenemeyeceği eklentidir). Testte bilinçli bir ayrıntı: çıkış kodu testinde taklit çıktı ayrıştırılabilir bırakıldı. Boş stdout ile, returncode denetimi tamamen kaldırılsa bile test yeşil kalırdı — yani testin koruduğu şeyi korumazdı. 26/26 sabotaj yakalandı. Sabotaj koşucusuna PYTHONDONTWRITEBYTECODE eklendi: aynı dosya saniyeler içinde defalarca yazıldığı için .pyc geçersizlemesi (mtime+boyut) bir sabotajı görünmez kılıp sahte "kaçtı" üretiyordu. Mevcut flush koşusu da bu düzeltmeyle yeniden çalıştırıldı: 35/35 duruyor. Toplam 373 test.
external_ip artık InventoryIn'de bir alan DEĞİL. Cihaz kendi dış adresini bildirseydi, doğruluğu cihazın insafına kalırdı: bir agent istediği adresi yazabilirdi ve dashboard'daki "bu makine nereden bağlanıyor" sorusunun cevabı cihazın beyanı olurdu. Bu, device_id'de verilen kararın (Boşluk A) aynısıdır — kimlik anahtardan türetiliyor, adres de bağlantının kendisinden türetilir. `extra="forbid"` sayesinde alanın yokluğu pasif değil aktif bir REDDİR: göndermeye çalışan agent 422 alır. Kararlar: - Kaynak Fly-Client-IP. Fly'ın proxy'si bu başlığı KENDİSİ yazar ve istemcinin gönderdiğini ezer. X-Forwarded-For kullanılmadı: o bir listedir ve istemci başına istediğini ekleyebilir — reddedilen "gövdede gönder" yolunu başka bir adla geri açmak olurdu. - Başlık yoksa sütun null kalır. Soketin karşı ucuna düşmek bir seçenek değil: proxy arkasında o adres proxy'nin kendisidir ve cihazın adresi diye kaydedilmesi boş bırakmaktan daha kötüdür. - Değer ip_address ile ÇÖZÜLÜR ve normalleştirilmiş hali yazılır. Doğrulama, proxy zinciri varsayıldığı gibi değilse metin sütununa güvenilmeyen girdi yazılmasını engeller; normalleştirme, aynı IPv6 adresinin farklı yazımlarının dashboard'da "adres değişti" gibi görünmesini engeller. - Rıza her gönderimde yeniden okunur ve eklenti KAPALIYKEN sütuna açıkça null yazılır. "Yazmamak" yetmezdi: eski adres satırda kalırdı. Kullanıcı eklentiyi kapattığında enabled_addons değiştiği için envanter zaten yeniden gönderilir, o gönderim eski değeri siler. - Yalnızca /inventory yazar, /ingest yazmaz. /ingest gövdesinde enabled_addons yok; oradan yazılan bir adres rızayı hiç okumadan yazılmış olurdu. Adres "statik eklenti" olarak tanımlı, tazeliği envanterin tazeliği kadar (açılışta + değiştiğinde) — daha sık istenirse önce rızanın cihaz satırından okunması gerekir. - Eklenti adı collector'da TEKRAR tanımlandı, agent'tan import edilmedi: ikisi ayrı deploy edilen ayrı programlar (collector imajında agent kodu yok), paylaştıkları şey Python nesnesi değil wire sözleşmesidir. Ayrışmayı test yakalıyor — log seviyeleri ve flush sebepleri için kullanılan yöntemin aynısı. - DEVICE_WRITABLE_COLUMNS'ta external_ip artık "agent'ın bildirdikleri" grubunda değil, "sunucunun kendi bildiklerinden yazdıkları" grubunda. 12 test, 12/12 sabotaj yakalandı. Aralarında iki sözleşme testi var: eklenti adının iki tarafta aynı kalması ve agent'ın Inventory'si ile InventoryIn'in alan alan eşleşmesi (fazla alan 422 üretir, eksik alan sessizce null kalır — ikisi de tek listenin bir tarafta unutulmasıyla oluşur). Collector 0.4.0 -> 0.5.0. Toplam 385 test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Eşik aşıldığında (cpu>90 / ram>90 / disk>95 / error|critical log) spool 30 sn'lik
turu beklemeden gönderilir; cooldown seli önler. crash_processes açıksa flush anında
ilk 5 kaynak-yiyen süreç crash_snapshots'a yazılır.
Seçilebilir eklentiler: temperature, swap, load_avg, gpu, external_ip, crash_processes.
enabled_addons kapalıyken sütun null kalır ve ölçüm hiç yapılmaz (nvidia-smi çağrılmaz).
external_ip'i cihaz GÖNDERMEZ: InventoryIn'de alan yok, extra="forbid" sayesinde
göndermeye çalışan agent 422 alır. Değeri collector, Fly'ın proxy başlığından yazar
ve rızayı her istekte yeniden okur — eklenti kapatıldığında sütuna açıkça null yazılır.
56 yeni test (385 toplam). 73 sabotajın tamamı yakalandı.
Canlı doğrulandı (Multipass VM): flush 0.28 sn'de gitti (30 sn değil), cooldown iki
denemeyi eledi, swap=0 null'a düşmedi, sensörsüz makinede temperature null kaldı.
Not: external_ip yalnızca Fly proxy'sinin arkasında dolar — collector 0.5.0 deploy'undan
sonra doğrulanacak.