I finanssektoren må vi ofte håndtere ret store mængder data vedrørende valutakurser. Vi henter data fra forskellige kilder, og hver af dem har sin egen måde at ekstrapolere værdierne for kurserne til i morgen, overmorgen, næste måned og endda de næste tre år. Hvis nogen kunne forudsige kurserne, er det vigtigt, ville det være på tide at lukke forretningen og bare skifte penge frem og tilbage. Nogle kilder nyder større tillid, mens andre leverer rent skrammel med sjældne indslag af næsten korrekte værdier, men mest for eksotiske par. Vores opgave er at silke disse titusindvis af værdier per sekund og bestemme, hvad vi egentlig skal vise vores kunder. Vi skal filtrere det ene korrekte værd fra en bunke skidt og mudder, ligesom flamingoer gør ved frokost.

Et særligt kendetegn ved flamingoen er dens massive, nedadvendte næb, som de bruger til at filtrere føde fra vand eller mudder.
—
Sådan blev biblioteket , som opbevarer cache-tilstanden for flere værdier inden for angivne tidsintervaller, født. Under motorhjelmen sorterer det automatisk dårlige og forældede data og giver også adgang til de seneste N validerede værdier for hver nøgle (valutapar i vores tilfælde).
Lad os antage, at vi indsamler kurser for tre valutapar. Den enkleste definition Vela for at opbevare den aktuelle tilstand vil se nogenlunde sådan ud:
defmodule Pairs do
use Vela,
eurusd: [sorter: &Kernel. 0
endOpdatering af værdierne
funktionen vil sekventielt gøre følgende:
- kalde
validatorpå værdien, hvis en sådan er defineret (se kapitel Validering nedenfor); - tilføje værdien enten til rækken af gode værdier, hvis valideringen er blevet bestået, eller til den interne række
:__errors__i modsætning til; - kalde sortering, hvis
sorterer defineret for denne nøgle, eller bare placere værdien i toppen af listen (LIFO, se kapitel Sortering nedenfor); - skære rækken i henhold til parameteren
:limitgivet ved oprettelsen; - returnere den opdaterede struktur
Vela.
iex|1 > par = %Pairs{}
iex|2 > Vela.put(par, :eurcad, 1.0)
#⇒ %Pairs{..., eurcad: [1.0], ...}
iex|3 > Vela.put(par, :eurcad, -1.0)
#⇒ %Pairs{__errors__: [eurcad: -1.0], ...}
iex|4 > par |> Vela.put(:eurusd, 2.0) |> Vela.put(:eurusd, 1.0)
#⇒ %Pairs{... eurusd: [1.0, 2.0]}Derudover Vela implementerer , så man kan opdatere værdier ved at bruge en hvilken som helst af de standardfunktioner til dyb opdatering af strukturer : , , , , og .
Validering
Valideren kan defineres som:
- en ekstern funktion med et argument (
&MyMod.my_fun/1), den vil kun modtage værdien til validering; - en ekstern funktion med to argumenter,
&MyMod.my_fun/2, den vil modtage parretserie, værditil validering; - modul, der implementerer ;
- konfigurationsparameter
grænseværdi, og — valgfrit —compare_by, se kapitel Sammenligning nedenfor.
Hvis valideringen var vellykket, vil værdien blive tilføjet til listen under den relevante nøgle, ellers returneres tuple {serie, værdi} til :__errors_.
Sammenligning
Værdierne, der gemmes i disse rækker, kan være vilkårlige. For at lære Vela dem at sammenligne, skal man give compare_by parameteren i rædefsiniton (medmindre værdierne kan sammenlignes med standard Kernel.<\/2); denne parameter skal have typen (Vela.value() -> number()). Som standard er det simpelthen & &1.
Derudover kan man i rædefiniton overføre parameteren comparator til beregning af delta-værdier (min/max); for eksempel, ved at overføre Date.diff/2 som en comparator, kan man få korrekte deltaløsninger for datoer.
En anden praktisk metode er at give parameteren grænseværdi, der definerer det maksimalt tilladte forhold mellem den nye værdi og {min, max} intervallet. Da den er angivet i procent, bruger tjekket ikke comparator, men bruger stadig compare_by. For eksempel, for at angive en grænseværdi for tidsdatoer, skal man angive compare_by: &DateTime.to_unix/1 (for at få et heltalsværdi) og grænseværdi: 1, hvilket gør, at nye værdier kun er tilladt, hvis de ligger inden for ±bånd intervallet fra de nuværende værdier.
Endelig kan man bruge Vela.equal?/2 compare/2 , hvis værdierne definerer funktionen eller equal?/2compare/2 ==/2.
, så vil disse funktioner blive brugt til sammenligning, ellers bruger vi bare
Hentning af værdier Behandling af den nuværende tilstand begynder normalt med at kaldeVela.purge/1 validator , som fjerner forældede værdier (hvis bundet tiltimestamps. ) Derefter kan man kalde Vela.slice/1, der returnerer nøgleord med rækkerne som nøgler og de første, aktuelle værdier.
Man kan også benytte get_in/2/pop_in/2 til lavniveauadgang til værdierne i hver række.
Applikation
Vela kan vise sig at være ekstremt nyttig som cache til tidsserier i tilstanden af en type GenServer/Agent. Vi ønsker aldrig at bruge forældede valutakurser, og for dette holder vi simpelthen en proces med en tilstand, der behandles Vela, med en validator, der vises nedenfor.
@impl Vela.Validator
def valid?(_key, %Rate{} = rate),
do: Rate.age(rate) < @death_ageog Behandling af den nuværende tilstand begynder normalt med at kalde fint fjerner alle forældede værdier hver gang vi har brug for data. For at tilgå de aktuelle værdier kalder vi simpelthen ) Derefter kan man kalde Vela.slice/1, og når vi har brug for en lille historik over kursen (hele rækken), returnerer vi den bare – allerede sorteret – med validerede værdier.
God caching af tidsserier!
Kilde: habr.com
