Dans le secteur de la fintech, nous devons souvent traiter d'importants volumes de données concernant les taux de change. Nous recueillons des données provenant de diverses sources, et chacune d'elles a sa propre façon d'extrapoler les valeurs des taux pour demain, après-demain, le mois suivant, voire les trois prochaines années. Si quelqu'un savait prédire les taux, bien, il vaudrait mieux fermer le business et simplement échanger de l'argent sans réfléchir. Certaines sources sont plus fiables, d'autres fournissent uniquement des données erronées, avec quelques rares valeurs presque correctes, mais seulement pour des paires exotiques. Notre travail consiste à trier ces dizaines de milliers de valeurs par seconde et à déterminer ce qu'il convient de montrer aux clients. Nous devons filtrer la seule valeur correcte dans une tonne de saleté et de boues, comme le font les flamants durant leur repas.

Une caractéristique distinctive des flamants est leur long bec massif orienté vers le bas, qui leur permet de filtrer leur nourriture dans l'eau ou dans la boue.
—
C'est ainsi qu'est née la bibliothèque , qui conserve un cache d'état pour plusieurs valeurs sur des intervalles de temps définis. En arrière-plan, elle filtre en temps réel les données obsolètes et de mauvaise qualité, et fournit également l'accès aux dernières N valeurs validées pour chaque clé (paires de devises, dans notre cas).
Supposons que nous recueillons les taux pour trois paires de devises. La définition la plus simple Vela pour stocker l'état actuel pourrait ressembler à ceci :
defmodule Pairs do
use Vela,
eurusd: [sorter: &Kernel. 0
endMise à jour des valeurs
la fonction effectuera successivement les opérations suivantes :
- appelera
validatorsur la valeur, si elle est définie (voir le chapitre Validation ci-dessous); - ajoutera la valeur soit dans la liste des bonnes valeurs, si la validation est réussie, soit dans la liste des erreurs
:__errors__dans le cas contraire; - appellera le tri si
sorterest défini pour cette clé, ou simplement ajoutera la valeur au début de la liste (LIFO, voir le chapitre Tri ci-dessous); - tronquera la liste selon le paramètre
:limitpassé lors de la création; - retournera la structure mise à jour
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]}Aussi Vela implémente , donc on peut utiliser n'importe quelle des fonctions standard pour mettre à jour en profondeur les structures provenant de l'arsenal : , , , , et .
Validation
Le validateur peut être défini comme :
- une fonction externe avec un argument (
&MyMod.my_fun/1), elle recevra uniquement la valeur à valider ; - une fonction externe avec deux arguments,
&MyMod.my_fun/2, elle recevra la paireserie, valueà valider ; - un module implémentant ;
- paramètre de configuration
seuil, et — optionnellement —compare_by, voir le chapitre Comparaison ci-dessous.
Si la validation réussit, la valeur est ajoutée à la liste sous la clé correspondante, sinon le tuple {serie, value} est envoyé dans :__errors_.
Comparaison
Les valeurs stockées dans ces lignes peuvent être n'importe quoi. Pour apprendre à Vela les comparer, il est nécessaire de passer compare_by un paramètre dans la définition de la série (à moins que les valeurs ne puissent pas être comparées par le standard Kernel.</2); ce paramètre doit être de type (Vela.value() -> number()). Par défaut, c'est simplement & &1.
De plus, on peut passer un paramètre comparator pour le calcul des valeurs de décalage (min/max); par exemple, en passant Date.diff/2 comme comparateur, on peut obtenir des décalages corrects pour les dates.
Une autre méthode simple consiste à passer un paramètre seuil, qui définit le rapport maximal autorisé de la nouvelle valeur par rapport à {min, max} intervalle. Étant donné qu'il est spécifié en pourcentage, la vérification n'utilise pas comparator, mais utilise toujours compare_by. Par exemple, pour spécifier une valeur seuil pour le temps des dates, il est nécessaire de spécifier compare_by: &DateTime.to_unix/1 (pour obtenir une valeur entière) et threshold: 1, ce qui fera en sorte que les nouvelles valeurs ne seront autorisées que si elles se situent dans ±band l'intervalle des valeurs actuelles.
Enfin, on peut utiliser Vela.equal?/2 compare/2 , si les valeurs définissent une fonction ou equal?/2compare/2 ==/2.
, alors ces fonctions seront utilisées pour comparer, sinon nous utiliserons simplement
Obtenir des valeurs Le traitement de l'état actuel commence généralement par un appel àVela.purge/1 validator , qui retire les valeurs obsolètes (si lié àtimestamps ). Ensuite, on peut appeler les, qui renverra keyword avec les noms des lignes comme clés et la première, des valeurs actuelles.
Vous pouvez également utiliser get_in/2/pop_in/2 pour un accès de bas niveau aux valeurs de chaque ligne.
L'application
Vela peut s'avérer extrêmement utile comme cache pour les séries temporelles dans l'état du processus de type GenServer/Agent. Nous voulons jamais utiliser des valeurs de taux obsolètes, et pour cela, nous maintenons simplement un processus avec un état traité Vela, avec le validateurs indiqué ci-dessous.
@impl Vela.Validator
def valid?(_key, %Rate{} = rate),
do: Rate.age(rate) < @death_ageet Le traitement de l'état actuel commence généralement par un appel à qui supprime tranquillement toutes les valeurs obsolètes chaque fois que nous avons besoin de données. Pour accéder aux valeurs actuelles, nous appelons simplement ). Ensuite, on peut appeler les, et lorsque nous avons besoin d'un petit historique du taux (l'intégralité de la série), nous le renvoyons simplement — déjà trié — avec des valeurs validées.
Bonne mise en cache des séries temporelles !
Source : habr.com
