Vela → cache intelligent pour les séries chronologiques et plus

Dans le domaine de la fintech, nous devons souvent traiter des volumes assez massifs de données sur les taux de change. Nous obtenons des données de différentes sources, et chacune d’elles a sa propre idée sur la manière d’extrapoler les taux de change pour demain, après-demain, le mois prochain et même les trois prochaines années. Si seulement quelqu'un pouvait prédire les taux correctement, il serait temps de fermer l'entreprise et de changer bêtement de l'argent. Certaines sources sont plus fiables, d'autres fournissent des conneries complètes, avec de rares inclusions de valeurs presque correctes, mais pour des couples exotiques. Notre travail consiste à passer au crible ces dizaines de milliers de valeurs par seconde et à déterminer exactement ce qu'il faut montrer aux clients. Nous devons filtrer la seule valeur correcte parmi des tonnes de saleté et de limon, tout comme le font les flamants roses au déjeuner.

Vela → cache intelligent pour les séries chronologiques et plus

Une caractéristique particulière des flamants roses est leur bec massif courbé vers le bas, avec lequel ils filtrent la nourriture de l'eau ou de la boue.
 - Wiki

Ainsi est née la bibliothèque Vela, qui stocke un cache d'état pour plusieurs valeurs à des intervalles de temps spécifiés. Sous le capot, il filtre à la volée les données incorrectes et obsolètes et donne également accès aux dernières N valeurs validées pour chaque clé (paires de devises, dans notre cas).

Disons que nous collectons les taux de trois paires de devises. Définition la plus simple Vela pour stocker l'état actuel, cela ressemblera à ceci :

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

Mise à jour des valeurs

Vela.put/3 La fonction effectuera les opérations suivantes dans l'ordre :

  • provoquera validator sur la valeur, si celle-ci est définie (voir chapitre Validation ci-dessous);
  • ajoutera la valeur soit à la ligne des bonnes valeurs si la validation a réussi, soit à la ligne du service :__errors__ sinon;
  • provoquera un tri si sorter défini pour une clé donnée, ou mettra simplement la valeur en tête de liste (LIFO, voir chapitre Сортировка ci-dessous);
  • va couper la ligne en fonction du paramètre :limit transmis à la création;
  • renverra 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 met en oeuvre Access, vous pouvez donc utiliser n'importe laquelle des fonctions standard de mise à jour approfondie des structures de l'arsenal pour mettre à jour les valeurs Kernel: Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2et Kernel.get_and_update_in/3.

Validation

Un validateur peut être défini comme :

  • fonction externe avec un argument (&MyMod.my_fun/1), il ne recevra la valeur que pour validation ;
  • fonction externe à deux arguments, &MyMod.my_fun/2, elle en aura une paire serie, value pour validation;
  • module implémentant Vela.Validator;
  • paramètre de configuration threshold, et - éventuellement - compare_by, voir 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} envoyés :__errors_.

Comparaison

Les valeurs stockées dans ces lignes peuvent être n'importe quoi. Enseigner Vela pour les comparer, il faut transférer compare_by paramètre dans la définition de la série (sauf si les valeurs ne peuvent pas être comparées à la norme Kernel.</2); ce paramètre doit être de type (Vela.value() -> number()). Par défaut c'est simple & &1.

Vous pouvez également transmettre un paramètre à la définition de ligne comparator pour calculer les valeurs delta (min/max); par exemple, en transmettant Date.diff/2 en tant que comparateur, vous pouvez obtenir les deltas corrects pour les dates.

Une autre façon pratique de travailler consiste à transmettre un paramètre threshold, qui définit le rapport maximum autorisé entre la nouvelle valeur et {min, max} intervalle. Puisqu'il est spécifié en pourcentage, le contrôle n'utilise pas comparatormais utilise toujours compare_by. Par exemple, pour spécifier une valeur seuil pour les dates et heures, vous devez spécifier compare_by: &DateTime.to_unix/1 (pour obtenir une valeur entière) et threshold: 1, ce qui fait que les nouvelles valeurs ne sont autorisées que si elles sont dans ±band intervalle à partir des valeurs actuelles.

Enfin, vous pouvez utiliser Vela.equal?/2 pour comparer deux caches. Si les valeurs définissent une fonction equal?/2 ou compare/2, alors ces fonctions seront utilisées à des fins de comparaison, sinon on utilise bêtement ==/2.

Obtenir des valeurs

Le traitement de l'état actuel commence généralement par l'appel Vela.purge/1, qui supprime les valeurs obsolètes (si validator lié à timestamps). Vous pourrez alors appeler Vela.slice/1qui reviendra keyword avec les noms de lignes comme clés et les premières valeurs réelles.

Vous pouvez aussi utiliser get_in/2/pop_in/2 pour un accès de bas niveau aux valeurs de chaque ligne.

Application

Vela peut être extrêmement utile comme cache de séries chronologiques dans un état de processus comme GenServer/Agent. Nous voulons ne jamais utiliser de valeurs de cours obsolètes, et pour ce faire, nous gardons simplement le processus avec l'état traité. Vela, avec le validateur illustré ci-dessous.

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

и Vela.purge/1 supprime discrètement toutes les valeurs obsolètes à chaque fois que nous avons besoin des données. Pour accéder aux valeurs réelles, nous appelons simplement Vela.slice/1, et lorsqu'un petit historique du cours est demandé (la série entière), on le renvoie simplement - déjà trié - avec les valeurs validées.

Bonne mise en cache des séries chronologiques !

Source: habr.com

Ajouter un commentaire