Keď OS Layer prerástol svoj dashboard

Keď OS Layer prerástol svoj dashboard

Board 1.33: Keď OS Layer prerástol svoj dashboard

Board nebol od začiatku problém.

Práve naopak.

OS Layer testujem od začiatku na rôznych strojoch a platformách — desktop, VPS, Termux, rôzne linuxové prostredia. Board na tom všetkom fungoval normálne.

Runtime, AI, Agent, approvals, execution flows — pribúdali ďalšie časti systému a Board ich vedel sledovať bez toho, aby sám predstavoval výraznú záťaž.

Potom však prišlo Crypto.

Za ním CryptoBot.

A postupne Bank.

A presne tam sa situácia začala meniť.

Nie preto, že by niektorá z tých capabilities bola sama o sebe pokazená.

Problém bol jednoduchší:

OS Layer začal produkovať podstatne viac reálnych dát, než na aké bol pôvodný Board navrhnutý.

A Board sa z toho jednoducho posral.

Keď už monitoring nie je malý

Pri Runtime alebo Agentovi sa pracuje najmä s execution eventami, stavmi behov, approvals a relatívne malými dátovými štruktúrami.

Crypto je iná liga.

Zrazu tu boli:

  • viaceré exchange účty,

  • pravidelný reconciliation,

  • balance snapshoty,

  • trades,

  • orders,

  • market candles,

  • quotes,

  • historické eventy,

  • CryptoBot,

  • market watcher,

  • agregované portfolio dáta.

A toto všetko bolo persistentné.

Databáza rástla.

Počet eventov rástol.

História rástla.

Board však stále fungoval približne podľa pôvodnej filozofie:

Načítaj veľa dát, vytvor snapshot a zobraz ho.

Pri menšom systéme je to úplne v pohode.

Pri stovkách tisíc eventov už nie.

Na VPS zrazu Crypto nefungovalo

Pri testovaní na 4 GB Debian VPS sa začala objavovať zvláštna situácia.

Crypto reconcileri bežali.

Watcher bežal.

Runtime bežal.

assets.json bol aktuálny.

Dáta v SQLite databáze boli aktuálne.

Crypto pipeline teda fungoval.

Ale Board mal problém.

Grafy nenabehli tak, ako mali, a proces po chvíli zmizol.

Kernel nakoniec ukázal veľmi jasnú odpoveď:

Out of memory: Killed process (...) (board-native)
anon-rss: ~3.8 GB

Board zožral prakticky celý 4 GB VPS.

To už nebola drobná odchýlka.

Monitoring nástroj mal skoro štyri gigabajty heapu.

Pozreli sme sa, ako rýchlo to rastie

Board sme spustili znova a sledovali RSS každé dve sekundy.

Vyzeralo to približne takto:

00:00      10 MB
00:12      13 MB
00:14      77 MB
00:16     423 MB
00:18     755 MB
00:20    1056 MB
00:24    1629 MB
00:28    2156 MB
00:32    2694 MB
00:34    2959 MB
...

A potom OOM.

To už vyzeralo ako veľmi konkrétny problém.

Board niečo masívne načítaval do heapu.

Nebola to page cache.

Kernel ukazoval takmer všetko ako anonymous RSS.

Takže sme išli po dátach.

50 000 eventov a 623 MB JSONu

Crypto databáza mala v tom čase približne 2.1 GB.

Tabuľka events mala viac než 700 000 riadkov.

Board vedel pre timeline načítať až 50 000 eventov.

Každý z nich obsahoval aj payload_json.

Tak sme zmerali, koľko dát vlastne predstavuje posledných 50 000 eventov.

Výsledok:

50 000 payloadov
623 094 900 bajtov

Približne 600 MiB raw JSON dát.

Ale ešte zaujímavejšie bolo rozdelenie podľa typu eventu:

balances.snapshot
12 500 eventov
617 182 400 bajtov
priemer: ~49.4 KB

Takmer celý objem tvorili balance snapshoty.

A to už vysvetľovalo skoro všetko.

Binance snapshot obsahuje veľa assetov, vrátane množstva nulových balances.

Na disku je to jeden pomerne kompaktný JSON string.

Board ho však načítal a potom z neho vytvoril:

SQLite TEXT
    ↓
Rust String
    ↓
serde_json::Value
    ↓
arrays
objects
strings
    ↓
timeline structures
    ↓
UI state

600 MB raw JSONu tak pokojne znamená niekoľko gigabajtov heap alokácií.

A presne to sme videli.

Prvý fix: história môže byť veľká, UI nie

Najjednoduchší hack by bol znížiť počet eventov napríklad z 50 000 na 2 000.

Board by prestal padať.

Lenže to nie je oprava.

To je poistka.

Ak systém rastie, riešenie nemôže byť:

Zobrazuj menej dát, aby sme prežili.

Správny model je:

Nenačítavaj dáta, ktoré práve nepotrebuješ.

Crypto timeline preto dostal lazy loading.

Bulk query už nenačítava payload_json.

Načíta iba malé metadata:

SELECT
    seq,
    event_id,
    timestamp_ms,
    kind,
    exchange,
    subject_id,
    previous_hash,
    hash
FROM events
WHERE profile = ?
ORDER BY seq DESC
LIMIT ?

Plný payload sa načíta až pri otvorení konkrétneho eventu:

SELECT payload_json
FROM events
WHERE profile = ?
  AND seq = ?

Takže namiesto:

50 000 eventov
+ 50 000 JSON payloadov

máme:

50 000 malých metadata záznamov
+ payload jedného vybraného eventu

História zostala.

Pamäť zmizla.

Po tejto úprave Board na tom istom VPS namiesto ~3.8 GB používal približne:

170–250 MB RAM

To bol prvý veľký úspech.

Ale zároveň sa ukázal ďalší problém.

RAM bola vyriešená. CPU išlo na bomby.

Board už nezomieral.

Lenže CPU bolo stále okolo:

96 %

Takže observability panel síce už nezožral celú RAM, ale zato prakticky permanentne držal jedno CPU jadro.

Prvá optimalizácia to dostala približne na 50 %.

Stále priveľa.

Tentoraz sme namiesto ďalšieho hádania pustili perf.

A ten ukázal veľmi jasný obraz:

sqlite3VdbeExec
_copy_to_iter
filemap_read
sqlite3BtreeInsert
sqlite3BtreeDelete

Board väčšinu času netrávil renderovaním UI.

Board brutálne pracoval so SQLite.

Board refreshoval veci, ktoré nikto nepozeral

Tu sa ukázal ďalší pozostatok pôvodnej architektúry.

Board už medzitým obsahoval samostatné views:

Overview
Runtime
AI
Agent
Crypto
CryptoBot
Bank

Lenže datasource model tomu úplne nezodpovedal.

Jedno okno mohlo refreshovať aj dáta ďalších subsystémov.

Agent mohol ťahať Runtime a AI.

Crypto mohlo mať zbytočné cross-domain závislosti.

Live subscribery mohli zostať aktívne aj pre veci, ktoré práve neboli zobrazené.

Pri malom datasete to nevadilo.

Keď systém narástol, začalo to byť drahé.

Board 1.33 preto dostal jednoduché pravidlo:

Runtime   → Runtime
AI        → AI
Agent     → Agent
Crypto    → Crypto
CryptoBot → CryptoBot
Bank      → Bank
Overview  → všetko potrebné

Každý view si platí iba svoje dáta.

A toto platí pre:

  • snapshot loading,

  • live subscriptions,

  • live-event merge.

Keď používateľ prejde z Agent view do Crypto, Agent sa prestane aktívne refreshovať.

Jeho posledný snapshot môže zostať v cache.

Ale CPU už naň nemíňame.

Overview je jediný agregátor.

Presne tak, ako to má byť.

Graf za 24 hodín nepotrebuje celú históriu

Ďalšia vec bola market história.

Board vedel načítať veľké množstvo candles a až následne z nich vyberať body potrebné pre aktuálny graf.

To je opačne.

Ak sa pozerám na 24 hodín, databáza má dostať query na 24 hodín.

Ak sa pozerám na 7 dní, query má byť na 7 dní.

Nie:

Daj mi kopu dát a ja si potom vyberiem.

Query sme teda zmenili na bounded model:

WHERE profile = ?
  AND pair = ?
  AND interval_seconds = ?
  AND open_time_ms >= ?
ORDER BY open_time_ms

To navyše presne sedí na existujúci databázový key:

(profile, pair, interval_seconds, open_time_ms)

Board teda prestal databázu používať ako obrovský dump, ktorý si potom filtruje v RAM.

Databáza dostane presne otázku, na ktorú má odpovedať.

A CPU stále 40 %

Po všetkých týchto zmenách CPU výrazne kleslo.

Ale stále zostávalo približne:

40 %

Na idle dashboard stále príliš veľa.

Tak sme pustili perf znova.

A tentoraz sa veľmi jasne ukázalo:

sqlite3VdbeExec
sqlite3BtreeInsert
sqlite3BtreeDelete

SQLite si pri query vytváral temporary B-tree.

Prečo?

Crypto už malo viac než:

133 000 reconciliation záznamov

A Board sa pri refreshoch stále pýtal historickej tabuľky napríklad:

SELECT DISTINCT profile
FROM reconciliations;

a potom:

SELECT ...
FROM reconciliations
WHERE profile = ?
ORDER BY created_at_ms DESC
LIMIT 1;

EXPLAIN QUERY PLAN ukázal:

SCAN reconciliations
USE TEMP B-TREE FOR DISTINCT

a:

SCAN reconciliations
USE TEMP B-TREE FOR ORDER BY

Čiže každú sekundu sme si znovu prechádzali obrovskú historickú tabuľku len preto, aby sme zistili aktuálny stav.

To je ďalšia architektonická chyba.

Historická databáza nemá odpovedať na otázku:

Aký je stav práve teraz?

Na to už Crypto reconciler mal:

crypto/state/reconcile/<profile>.json

Malú current-state projekciu.

Board 1.33 teda začal reconciliation status čítať práve odtiaľ.

História zostala v SQLite.

Current state zostal v current-state projection.

Po tejto zmene CPU padlo približne na:

1–2 %

A bolo vybavené.

Board predtým a teraz

Výsledok celej operácie:

PRED

RAM: ~3.8 GB
CPU: ~96 %
výsledok: OOM killer


BOARD 1.33

RAM: ~170–250 MB
CPU: ~1–2 %
výsledok: stabilný monitoring

To už nie je kozmetická optimalizácia.

Je to rozdiel medzi:

Na tomto stroji Board nemôžem používať.

a:

Board môže byť otvorený stále.

A teraz tá najlepšia časť

Po oprave som Board 1.33 pustil znova tam, kde OS Layer normálne testujem.

Arch.

4 GB Debian VPS.

Termux na OnePlus 11.

A funguje.

Nie „nejako prežije“.

Normálne funguje.

Grafy nabehnú.

Crypto ide.

Board drží rozumnú RAM.

CPU v idle padne na približne jedno až dve percentá.

Presne takto má monitoring vyzerať.

Nebol problém slabý VPS

Toto je možno najdôležitejšia pointa celej epizódy.

Problém nevznikol preto, že sme Board prvýkrát pustili na malom VPS.

OS Layer beží a testuje sa na rôznych platformách od začiatku.

Board tiež.

Zmenil sa samotný OS Layer.

Prišlo Crypto.

Prišlo CryptoBot.

Prišla Bank.

Pribudli veľké persistentné datasety.

Státisíce eventov.

Market história.

Reconciliation história.

Raw exchange snapshoty.

A tým sa ukázalo, že niektoré pôvodné rozhodnutia v Boarde už jednoducho neškálujú.

To je normálna fáza vývoja systému.

Najprv architektúra funguje.

Potom systém narastie.

A až reálne dáta ukážu, ktoré abstrakcie boli správne a ktoré boli správne iba dovtedy, kým bolo všetkého málo.

Čo z toho zostáva v Board 1.33

Board 1.33 nie je iba „Board s menšou spotrebou RAM“.

Zmenili sa základné pravidlá práce s dátami.

Historické dáta môžu byť obrovské

Ale nemusia byť celé v pamäti.

Raw payload je detail

Takže sa načíta až vtedy, keď sa používateľ na detail pozrie.

Graf má svoj rozsah

Takže databáza vráti iba tento rozsah.

Current state nie je história

Current-state projekcia má odpovedať na current-state otázky.

Každý view vlastní svoj datasource

Agent nepotrebuje AI.

AI nepotrebuje Runtime.

Crypto nepotrebuje Agent.

Ak to nepotrebuje UI, nemá dôvod to refreshovať.

Overview je agregátor

A preto je jediným miestom, kde je cross-domain pohľad prirodzený.

Tieto pravidlá nie sú triky na znižovanie CPU.

Sú to správne hranice systému.

A keď sa nastavili správne, výkon prišiel spolu s nimi.

Konečne môžeme testovať to, čo pribudlo

A toto je pre mňa na dnešnej epizóde najlepšie.

Posledné obdobie pribudlo v OS Layeri kopu nových vecí.

Codex.

Claude Code.

Crypto.

CryptoBot.

Bank sa začína rysovať.

Ďalšie execution flows.

Ďalšie integrations.

A konečne mám Board, ktorý môžem nechať otvorený a cez ktorý to celé vidím.

Nie dashboard, ktorý sám potrebuje monitoring.

Ale normálnu observability vrstvu.

Takže po dnešku už môžem prestať riešiť Board a konečne začať robiť to, na čo mal byť určený od začiatku:

pozerať sa cez neho na OS Layer a testovať všetky tie nové veci.

A presne preto vznikol.

Board 1.33.

Konečne.

Marek Mihók