Files
trade-doc/data-ingest-strategy.md

7.8 KiB

Data ingest strategy

Cel

Zachowac niski latency dla logiki tradingowej i read-side, ale rownoczesnie miec trwala persystencje danych do UI, replayu, debugowania i analityki.

Nie robimy modelu DB-first jako jedynej sciezki przetwarzania.

Rekomendowany model

Stosujemy dual-path:

  1. hot path Yellowstone gRPC / Agave RPC -> consumer -> Redis / internal state -> strategy / read-side

  2. durable path ten sam consumer -> Postgres -> UI / Hasura / replay / debug

Opcjonalnie pozniej:

  1. history / analytics path consumer lub batch exporter -> ClickHouse

Dlaczego nie DB-first

Model:

gRPC / RPC -> DB -> dopiero potem przetwarzanie

nie jest dobry jako glowna sciezka, bo:

  • dodaje latency do kazdego eventu
  • uzaleznia ingest od kondycji bazy
  • pogarsza odpornosc calego pipeline
  • zwieksza write amplification przy duzym wolumenie zdarzen
  • utrudnia low-latency strategie i read-side

DB ma byc miejscem persystencji i read modelu, nie bramka przed logika runtime.

Rola Yellowstone gRPC

Yellowstone ma byc glownym zrodlem live feed:

  • account updates
  • tx updates
  • slot updates
  • program updates

Sciezka:

Yellowstone -> consumer -> hot state

oraz rownolegle:

Yellowstone -> normalization / persistence -> Postgres

Yellowstone nie powinien byc uzalezniony od tego, czy Postgres aktualnie pisze szybko.

Rola Agave RPC

RPC nie jest naturalnym feedem eventowym.

RPC powinno sluzyc glownie do:

  • point reads
  • bootstrapu klienta
  • recovery
  • getAccountInfo / getMultipleAccounts
  • getHealth / getSlot
  • write path dla tx, jesli nie idziemy przez TPU/Jito

RPC nie powinno byc traktowane jak glowny event stream do zrzucania wszystkiego do DB.

Rola Redis

Redis powinien byc hot state layer:

  • szybki stan DLOB
  • cache
  • fanout wewnetrzny
  • ewentualnie stream / queue dla workerow

To jest warstwa pod:

  • strategy engine
  • orderbook read-side
  • szybkie projekcje

Nie trzymamy tam historii jako source of truth.

Rola Postgres

Postgres powinien byc durable read model:

  • dane dla UI
  • dane dla Hasury
  • znormalizowane ticki
  • pochodne tabele DLOB
  • stan aplikacji
  • konfiguracja
  • historia operacyjna

Postgres jest dobry do:

  • frontend
  • dashboard
  • replay lekkiego zakresu
  • audyt aplikacyjny

Kiedy ClickHouse

ClickHouse dodajemy dopiero, gdy:

  • wolumen raw feedu bedzie duzy
  • potrzebna bedzie ciezsza analityka historyczna
  • bedziemy chcieli trzymac dluzsza historie eventow i tx

Na obecnym etapie nie jest konieczny do uruchomienia runtime.

Docelowy przeplyw

mevnode_sol
  Agave RPC
  Yellowstone gRPC
        |
        v
mevnode_bot
  consumer
    |\
    | +--> Redis -> DLOB/read-side -> strategy/workers
    |
    +----> Postgres -> Hasura/API -> frontend

Zasada architektoniczna

  • gRPC / RPC dostarcza dane do runtime
  • Redis utrzymuje szybki stan operacyjny
  • Postgres utrzymuje trwaly stan i read model
  • frontend i Hasura czytaja z Postgresa
  • strategy i workers nie czekaja na zapis do DB, aby przetwarzac dane

Rekomendacja dla projektu

Na teraz:

  1. Yellowstone -> consumer -> Redis
  2. consumer -> Postgres
  3. Hasura / API / UI <- Postgres
  4. strategy / read-side <- Redis
  5. RPC zostawic do bootstrapu, point reads i write path

To daje najlepszy kompromis:

  • niski latency
  • odpornosc runtime
  • sensowna persystencja
  • dobra baza pod UI i replay

Ocena obecnego ukladu na k3s

Obecny runtime na mevnode_bot / trade-staging jest tylko czesciowo zgodny z docelowym modelem.

Co jest zgodne

  • postgres jest postawiony jako StatefulSet, wiec nadaje sie na trwały storage
  • dlob-redis jest osobna lekka usluga i pasuje do roli cache / hot state
  • hasura i trade-api siedza nad Postgresem, co jest poprawne dla durable read modelu
  • frontend korzysta z API/Hasury, a nie bezposrednio z Redis

Co nie jest zgodne

  • dlob-publisher i dlob-server nie sa zdrowe, wiec hot path nie jest operacyjnie domkniety
  • dlob-publisher jest podpiety do gRPC, ale nie jest glownym i pewnym writerem do Postgresa
  • trade-ingestor nie ingestuje danych bezposrednio z chain source, tylko polluje dane pochodne z Hasury
  • persistent path jest wiec dzisiaj czesciowo odwrocony:
    • derived view -> ingest -> Postgres
    • zamiast:
    • chain source -> normalize -> Postgres

Plan zmian

Cel koncowy

Chcemy dojsc do ukladu:

Yellowstone gRPC / Agave RPC
        |
        v
consumer
   |\
   | +--> Redis      -> hot state / workers / strategy
   |
   +----> Postgres   -> Hasura / API / frontend / replay

Etap 1. Naprawa hot path

Najpierw trzeba przywrocic dzialanie:

  • dlob-publisher
  • dlob-server

Bez zdrowego publishera nie ma sensu budowac dalszej warstwy Redis = hot state.

Zakres:

  • ustabilizowac Agave RPC i Yellowstone
  • doprowadzic dlob-publisher do Ready
  • potwierdzic, ze Redis dostaje aktualny stan
  • doprowadzic dlob-server do Ready

Etap 2. Uczynic publisher glownym consumerem danych chain

Obecnie trade-ingestor korzysta z danych pochodnych z Hasury.

To trzeba zmienic tak, aby glownym miejscem wejscia danych byl komponent konsumujacy:

  • Yellowstone gRPC
  • Agave RPC

Rekomendacja:

  • rozszerzyc dlob-publisher
  • albo wydzielic nowy market-consumer

ale w obu przypadkach komponent ma robic:

  • odczyt live z Yellowstone
  • wymagane point reads z RPC
  • normalizacje danych
  • jednoczesny zapis do Redis i Postgresa

Etap 3. Ustalic twardy podzial rol Redis / Postgres

Redis

Do Redis trafia tylko to, co potrzebne jako szybki stan runtime:

  • latest DLOB state
  • latest L2
  • quick cache dla workerow
  • fanout do read-side i strategii
  • dane z TTL lub dane odtwarzalne

Redis nie jest source of truth.

Postgres

Do Postgresa trafia trwaly model danych:

  • znormalizowane ticki
  • znormalizowane snapshoty rynku
  • pochodne tabele pod Hasure
  • historia operacyjna
  • stan aplikacji

Postgres jest source of truth dla aplikacji i UI.

Etap 4. Przebudowac worker path

Docelowy model dla workerow:

  • Redis in
  • Postgres out

To znaczy:

  • workery czytaja szybki stan z Redis
  • licza projekcje
  • zapisują wynik do Postgresa

Dotyczy to szczegolnie:

  • dlob-worker
  • dlob-depth-worker
  • dlob-slippage-worker

Etap 5. Wygasic obecny odwrocony ingest

Obecny trade-ingestor zapisuje dane do Postgresa na podstawie danych juz pochodnych z Hasury.

To nie powinno byc glowna sciezka ingestu.

Mozliwe role po zmianie:

  • backfill
  • sanity check
  • fallback
  • testowy importer

Ale nie:

  • glowny chain ingest

Etap 6. Utrwalic read model pod UI

Po zmianach:

  • Hasura czyta tylko z Postgresa
  • trade-api czyta tylko z Postgresa
  • frontend czyta przez API/Hasure

Frontend nie powinien zalezec od Redis.

Etap 7. Zostawic RPC w waskiej roli

Agave RPC zostawiamy do:

  • bootstrapu
  • point reads
  • recovery
  • write path dla tx

Nie robimy z RPC glownej magistrali trwałego ingestu.

Plan wdrozenia krok po kroku

  1. Naprawic dlob-publisher i dlob-server.
  2. Potwierdzic, ze Redis jest zasilany poprawnym hot state.
  3. Rozszerzyc publisher albo dodac nowy consumer tak, aby pisal rownolegle do:
    • Redis
    • Postgresa
  4. Zdefiniowac schemat tabel normalized data w Postgresie.
  5. Przepiac workery na model:
    • read z Redis
    • write do Postgresa
  6. Zostawic Hasure i trade-api jako warstwe odczytowa nad Postgresem.
  7. Zdegradowac trade-ingestor z roli glownego ingestu do roli pomocniczej albo go usunac z glownej sciezki.

Rekomendacja praktyczna

Najmniejsza sensowna zmiana to nie budowac od razu nowego systemu od zera, tylko:

  1. naprawic obecny dlob-publisher
  2. dodac mu zapis do Postgresa
  3. stopniowo wyprowadzac trade-ingestor z glownego path

To jest najmniej ryzykowna droga do modelu:

  • Redis = cache / hot state
  • Postgres = persistent normalized store