Vela → cache inteligent pentru serii temporale și nu numai

În fintech, adesea trebuie să gestionăm volume destul de mari de date privind cursurile de schimb valutar. Obținem date din diverse surse, fiecare având propria reprezentare despre cum să extrapoleze valorile cursurilor pentru mâine, poimâine, luna următoare și chiar următorii trei ani. Dacă cineva ar putea prezice cursurile, săar fi momentul să închidem afacerea și pur și simplu să schimbăm bani dintr-o parte în alta. Unele surse sunt mai de încredere, altele oferă doar informații inutile, cu câteva valori aproape corecte, dar pentru perechi exotice. Munca noastră constă în a analiza zecile de mii de valori pe secundă și a determina ce să arătăm clienților. Trebuie să filtrăm valoarea corectă dintr-o tonă de mizerie și nămol, așa cum o fac flamingo la masă.

Vela → cache inteligent pentru serii temporale și nu numai

O caracteristică distinctivă a flamingo-ului este ciocul masiv, curbat în jos, cu ajutorul căruia își filtrează hrana din apă sau nămol.
 — Wiki

Așa a luat naștere biblioteca Vela, care stochează cache-ul stării pentru mai multe valori în intervale de timp date. Sub capotă, aceasta filtrează în timp real datele proaste și învechite, oferind acces la ultimele N valori validate pentru fiecare cheie (perechi valutare, în cazul nostru).

Să presupunem că strângem cursurile pentru trei perechi valutare. O definiție simplă Vela pentru stocarea stării actuale ar arăta cam așa:

defmodule Pairs do
  use Vela,
    eurusd: [sorter: &Kernel. 0
end

Actualizarea valorilor

Vela.put/3 funcția va face următoarele:

  • va apela validatorul pe valoare, dacă este definit (vezi capitolul Validare de mai jos);
  • va adăuga valoarea fie în rândul valorilor bune, dacă validarea a avut succes, fie într-un rând de erori :__errors__ în caz contrar;
  • va apela sortarea dacă sorter-ul este definit pentru această cheie, sau pur și simplu va adăuga valoarea la începutul listei (LIFO, vezi capitolul Sortare de mai jos);
  • îi va tăia rândul conform parametrului :limit transmis la crearea sa;
  • va returna structura actualizată Vela.

iex|1 > pairs = %Pairs{}
iex|2 > Vela.put(pairs, :eurcad, 1.0)
#⇒ %Pairs{..., eurcad: [1.0], ...}
iex|3 > Vela.put(pairs, :eurcad, -1.0)
#⇒ %Pairs{__errors__: [eurcad: -1.0], ...}
iex|4 > pairs |> Vela.put(:eurusd, 2.0) |> Vela.put(:eurusd, 1.0)
#⇒ %Pairs{... eurusd: [1.0, 2.0]}

De asemenea, Vela implementează Acces, astfel încât puteți utiliza oricare din funcțiile standard pentru actualizarea profundă a structurilor disponibile Kernel: Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2, și Kernel.get_and_update_in/3.

Validare

Validatorul poate fi definit ca:

  • o funcție externă cu un singur argument (&MyMod.my_fun/1), care va primi doar valoarea pentru validare;
  • o funcție externă cu două argumente, &MyMod.my_fun/2, care va primi o pereche serie, value pentru validare;
  • un modul care implementează Vela.Validator;
  • parametru de configurare threshold, și - opțional - compare_by, vezi capitolul Comparare mai jos.

Dacă validarea a fost efectuată cu succes, valoarea este adăugată în listă sub cheia corespunzătoare, iar în caz contrar tupla {serie, value} se trimite în :__errors_.

Comparare

Valorile stocate în aceste linii pot fi orice. Pentru a învăța Vela să le compare, este necesar să transmiteți compare_by parametrul în definiția liniei (cu excepția cazului în care valorile nu pot fi comparate standard Kernel.<\/2); acest parametru ar trebui să aibă tipul (Vela.value() -> number()). Implicit, acesta este pur și simplu & &1.

De asemenea, în definiția liniei se poate transmite parametrul comparator pentru calcularea valorilor de delta (min/max); de exemplu, prin transmiterea Date.diff/2 ca comparator, se pot obține delta corecte pentru date.

O altă metodă convenabilă de operare este transmiterea parametrului threshold, care definește raportul maxim permis al noii valori la {min, max} interval. Deoarece este specificat în procente, verificarea nu utilizează comparator, dar tot utilizează compare_by. De exemplu, pentru a specifica un prag pentru timestamp-uri, este necesar să specificați compare_by: &DateTime.to_unix/1 (pentru a obține o valoare întreagă) și threshold: 1, astfel încât noile valori să fie permise doar dacă se află în ±band intervalul valorilor curente.

În cele din urmă, se poate folosi Vela.equal?/2 compare/2 , dacă aceste funcții definesc sau equal?/2, atunci aceste funcții vor fi folosite pentru comparare, în caz contrar, utilizăm pur și simplu ==/2.

Obținerea valorilor

Procesarea stării curente începe de obicei cu apelul Vela.purge/1, care elimină valorile depășite (dacă validatorul este legat de timestamps). Apoi se poate apela Vela.slice/1, care va returna keyword cu numele rândurilor ca chei și prima, valorile actuale.

De asemenea, se poate utiliza get_in/2/pop_in/2 pentru acces de nivel inferior la valorile din fiecare rând.

Aplicație

Vela poate fi extrem de util ca un cache pentru serii temporale în starea unui proces de tip GenServer/Agent. Nu dorim niciodată să folosim valori învechite ale cursurilor, iar pentru asta pur și simplu menținem un proces cu starea gestionată Vela, cu un validator prezentat mai jos.

@impl Vela.Validator
def valid?(_cheie, %Rate{} = rate),
  do: Rate.age(rate) < @death_age

și Vela.purge/1 șterge în mod liniștit toate valorile învechite de fiecare dată când avem nevoie de date. Pentru a accesa valorile actuale, pur și simplu apelăm Vela.slice/1, iar când avem nevoie de o scurtă istorie a cursului (întreaga serie), pur și simplu o returnăm - deja sortată - cu valorile validate.

Succes la caching-ul seriilor temporale!

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster