Vela → memòria cau intel·ligent per a sèries temporals i molt més

A fintech, sovint hem de processar volums força massius de dades de tipus de canvi de divises. Obtenim dades de diferents fonts, i cadascun d'ells té la seva pròpia idea de com extrapolar els tipus de canvi per a demà, passat demà, el mes que ve i fins i tot els propers tres anys. Si només algú pogués predir les taxes correctament, seria hora de tancar el negoci i canviar estúpidament diners d'anada i tornada. Algunes fonts són més fiables, algunes proporcionen escombraries completes, amb rares inclusions de valors gairebé correctes, però per a parelles exòtiques. La nostra feina és examinar aquestes desenes de milers de valors per segon i determinar què mostrar exactament als clients. Hem de filtrar el valor correcte de tones de brutícia i llim, tal com fan els flamencs al dinar.

Vela → memòria cau intel·ligent per a sèries temporals i molt més

Una característica distintiva especial dels flamencs és el seu bec massiu corbat cap avall, amb el qual filtren els aliments de l'aigua o el fang.
 - Wiki

Així va néixer la biblioteca Vela, que emmagatzema una memòria cau d'estat per a diversos valors en intervals de temps especificats. Sota el capó, filtra dades dolentes i obsoletes sobre la marxa i també proporciona accés a les últimes N valors validats per a cada clau (parells de divises, en el nostre cas).

Suposem que recollim tarifes per a tres parells de divises. La definició més senzilla Vela per emmagatzemar l'estat actual es veurà com això:

defmodule Pairs do
  use Vela,
    eurusd: [sorter: &Kernel.<=/2],
    eurgbp: [limit: 3, errors: 1],
    eurcad: [validator: Pairs]

  @behaviour Vela.Validator

  @impl Vela.Validator
  def valid?(:eurcad, rate), do: rate > 0
end

Actualització de valors

Vela.put/3 La funció farà el següent en seqüència:

  • provocarà validator sobre el valor, si se'n defineix un (vegeu el capítol Validació baix);
  • afegirà el valor a la fila de bons valors si la validació ha tingut èxit, o bé a la fila de servei :__errors__ d'una altra manera;
  • provocarà la classificació si sorter definit per a una clau determinada, o simplement posarà el valor al capdavant de la llista (VIDA, vegeu el capítol Сортировка baix);
  • retallarà la fila segons el paràmetre :limit transmès a la creació;
  • retornarà l'estructura actualitzada 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]}

També Vela implements Access, de manera que podeu utilitzar qualsevol de les funcions estàndard per actualitzar en profunditat les estructures de l'arsenal per actualitzar els valors Kernel: Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2i Kernel.get_and_update_in/3.

Validació

Un validador es pot definir com:

  • funció externa amb un argument (&MyMod.my_fun/1), només rebrà el valor per a la validació;
  • funció externa amb dos arguments, &MyMod.my_fun/2, tindrà un parell serie, value per a la validació;
  • implementació del mòdul Vela.Validator;
  • paràmetre de configuració thresholdi, opcionalment, compare_by, vegeu el capítol Comparació a continuació

Si la validació té èxit, el valor s'afegeix a la llista sota la clau corresponent; en cas contrari, la tupla {serie, value} va a :__errors_.

Comparació

Els valors emmagatzemats en aquestes files poden ser qualsevol cosa. Ensenyar Vela per comparar-los, cal transferir-los compare_by paràmetre a la definició de la sèrie (tret que els valors no es puguin comparar amb l'estàndard Kernel.</2); aquest paràmetre ha de ser del tipus (Vela.value() -> number()). Per defecte és senzill & &1.

A més, podeu passar un paràmetre a la definició de fila comparator per calcular valors delta (min/max); per exemple, transmetent Date.diff/2 com a comparador, podeu obtenir els deltas correctes per a les dates.

Una altra manera convenient de treballar és passar un paràmetre threshold, que defineix la relació màxima permesa del nou valor a {min, max} interval. Com que s'especifica com a percentatge, el xec no s'utilitza comparatorperò encara utilitza compare_by. Per exemple, per especificar un valor de llindar per a la data i l'hora, heu d'especificar-lo compare_by: &DateTime.to_unix/1 (per obtenir un valor enter) i threshold: 1, fent que només es permetin nous valors si estan a ±band interval dels valors actuals.

Finalment, podeu utilitzar Vela.equal?/2 per comparar dos cachés. Si els valors defineixen una funció equal?/2 o compare/2, llavors aquestes funcions s'utilitzaran per a la comparació, en cas contrari, les fem servir estúpidament ==/2.

Aconseguint valors

El processament de l'estat actual normalment comença amb la trucada Vela.purge/1, que elimina els valors obsolets (si validator lligat a timestamps). Aleshores pots trucar Vela.slice/1que tornarà keyword amb noms de files com a claus i els primers valors reals.

També podeu utilitzar get_in/2/pop_in/2 per accedir de baix nivell als valors de cada fila.

Aplicació

Vela pot ser extremadament útil com a memòria cau de sèries temporals en un estat de procés com GenServer/Agent. Volem no utilitzar mai valors de curs obsolets i, per fer-ho, simplement mantenim el procés amb l'estat processat Vela, amb el validador que es mostra a continuació.

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

и Vela.purge/1 elimina silenciosament tots els valors obsolets cada vegada que necessitem les dades. Per accedir als valors reals simplement trucem Vela.slice/1, i quan es requereix un petit historial del curs (la sèrie sencera), simplement el retornem -ja ordenat- amb valors validats.

Feliç memòria cau de sèries temporals!

Font: www.habr.com

Afegeix comentari