Vela → cache intelligente per serie temporali e altro ancora

Nel fintech, spesso dobbiamo elaborare volumi piuttosto massicci di dati sui tassi di cambio. Otteniamo dati da diverse fonti e ognuna di esse ha la propria idea su come estrapolare i tassi di cambio per domani, dopodomani, il mese prossimo e anche i prossimi tre anni. Se solo qualcuno potesse prevedere i tassi correttamente, sarebbe ora di chiudere l'attività e cambiare stupidamente i soldi avanti e indietro. Alcune fonti sono più attendibili, altre forniscono completa spazzatura, con rare inclusioni di valori quasi corretti, ma per coppie esotiche. Il nostro compito è vagliare queste decine di migliaia di valori al secondo e determinare cosa mostrare esattamente ai clienti. Dobbiamo filtrare l'unico valore corretto da tonnellate di sporco e limo, proprio come fanno i fenicotteri a pranzo.

Vela → cache intelligente per serie temporali e altro ancora

Una caratteristica distintiva dei fenicotteri è il loro massiccio becco ricurvo verso il basso, con il quale filtrano il cibo dall'acqua o dal fango.
 - Wiki

Così è nata la biblioteca Vela, che memorizza una cache di stato per più valori a intervalli di tempo specificati. Sotto il cofano, filtra al volo i dati non validi e obsoleti e fornisce anche l'accesso a quelli più recenti N valori convalidati per ciascuna chiave (coppie di valute, nel nostro caso).

Diciamo che raccogliamo tassi per tre coppie di valute. Definizione più semplice Vela per memorizzare lo stato corrente sarà simile a questo:

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

Aggiornamento dei valori

Vela.put/3 La funzione eseguirà le seguenti operazioni in sequenza:

  • causerà validator sul valore, se definito (vedi capitolo Validazione sotto);
  • aggiungerà il valore alla riga dei valori validi se la convalida ha avuto successo, oppure alla riga del servizio :__errors__ Altrimenti;
  • causerà l'ordinamento se sorter definito per una determinata chiave o metterà semplicemente il valore in testa all'elenco (LIFO, vedere il capitolo Ordinamento sotto);
  • taglierà la riga in base al parametro :limit trasmesso alla creazione;
  • restituirà la struttura aggiornata 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]}

anche Vela implementa Access, quindi puoi utilizzare una qualsiasi delle funzioni standard per l'aggiornamento approfondito delle strutture dall'arsenale per aggiornare i valori Kernel: Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2e Kernel.get_and_update_in/3.

Validazione

Un validatore può essere definito come:

  • funzione esterna con un argomento (&MyMod.my_fun/1), riceverà solo il valore per la validazione;
  • funzione esterna con due argomenti, &MyMod.my_fun/2, ne prenderà un paio serie, value per la convalida;
  • implementazione del modulo Vela.Validator;
  • parametro di configurazione threshold, e - facoltativamente - compare_by, vedere il capitolo Confronto qui di seguito.

Se la convalida ha esito positivo, il valore viene aggiunto all'elenco sotto la chiave corrispondente; altrimenti, la tupla {serie, value} va a :__errors_.

Confronto

I valori memorizzati in queste righe possono essere qualsiasi cosa. Insegnare Vela per confrontarli è necessario trasferire compare_by parametro nella definizione della serie (a meno che i valori non possano essere confrontati con lo standard Kernel.</2); questo parametro deve essere di tipo (Vela.value() -> number()). Per impostazione predefinita è semplice & &1.

Inoltre, puoi passare un parametro alla definizione di riga comparator per calcolare i valori delta (min/max); ad esempio, trasmettendo Date.diff/2 come comparatore, puoi ottenere i delta corretti per le date.

Un altro modo conveniente di lavorare è passare un parametro threshold, che definisce il rapporto massimo consentito del nuovo valore a {min, max} intervallo. Poiché è specificato in percentuale, il controllo non viene utilizzato comparatorma usa ancora compare_by. Ad esempio, per specificare un valore di soglia per la data e l'ora, è necessario specificare compare_by: &DateTime.to_unix/1 (per ottenere un valore intero) e threshold: 1, facendo sì che i nuovi valori siano consentiti solo se sono presenti ±band intervallo dai valori correnti.

Infine, puoi usare Vela.equal?/2 per confrontare due cache. Se i valori definiscono una funzione equal?/2 o compare/2, quindi queste funzioni verranno utilizzate per il confronto, altrimenti usiamo stupidamente ==/2.

Ottenere valori

L'elaborazione dello stato corrente di solito inizia con la chiamata Vela.purge/1, che rimuove i valori obsoleti (if validator legato a timestamps). Successivamente potrai chiamare Vela.slice/1che ritornerà keyword con i nomi delle righe come chiavi e i primi valori effettivi.

Puoi anche usare get_in/2/pop_in/2 per l'accesso di basso livello ai valori in ogni riga.

applicazione

Vela può essere estremamente utile come cache di serie temporali in uno stato di processo come GenServer/Agent. Vogliamo non utilizzare mai valori di corso obsoleti e per fare ciò manteniamo semplicemente il processo con lo stato elaborato Vela, con il validatore mostrato di seguito.

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

и Vela.purge/1 rimuove silenziosamente tutti i valori obsoleti ogni volta che abbiamo bisogno dei dati. Per accedere ai valori effettivi chiamiamo semplicemente Vela.slice/1, e quando è richiesta una piccola storia del corso (l'intera serie), la restituiamo semplicemente - già ordinata - con i valori validati.

Buona memorizzazione nella cache delle serie temporali!

Fonte: habr.com

Aggiungi un commento