Vela → inteligentna pamięć podręczna dla szeregów czasowych i nie tylko

W fintech często musimy przetwarzać dość ogromne ilości danych dotyczących kursów walut. Dane czerpiemy z różnych źródeł i każde z nich ma swój własny pomysł na to, jak ekstrapolować kursy walut na jutro, pojutrze, przyszły miesiąc, a nawet kolejne trzy lata. Gdyby tylko ktoś potrafił przewidzieć stawki prawidłowo, nadszedł czas, aby zamknąć firmę i po prostu głupio wymieniać pieniądze tam i z powrotem. Niektóre źródła są bardziej wiarygodne, inne podają kompletne bzdury, z rzadkimi wtrąceniami o prawie poprawnych wartościach, ale dla par egzotycznych. Naszym zadaniem jest przesiać te dziesiątki tysięcy wartości na sekundę i ustalić, co dokładnie pokazać klientom. Musimy odfiltrować jedyną prawidłową wartość z ton brudu i mułu, tak jak robią to flamingi podczas lunchu.

Vela → inteligentna pamięć podręczna dla szeregów czasowych i nie tylko

Szczególną cechą wyróżniającą flamingi jest ich masywny, zakrzywiony w dół dziób, za pomocą którego filtrują pokarm z wody lub błota.
 - Wiki

W ten sposób narodziła się biblioteka Vela, który przechowuje pamięć podręczną stanu dla wielu wartości w określonych odstępach czasu. Pod maską filtruje na bieżąco złe i nieaktualne dane, a także zapewnia dostęp do najnowszych N zweryfikowane wartości dla każdego klucza (w naszym przypadku pary walutowe).

Załóżmy, że zbieramy kursy dla trzech par walutowych. Najprostsza definicja Vela do przechowywania bieżącego stanu będzie wyglądać mniej więcej tak:

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

Aktualizowanie wartości

Vela.put/3 Funkcja wykona po kolei następujące czynności:

  • spowoduje validator na wartość, jeśli jest zdefiniowana (patrz rozdz Walidacja poniżej);
  • doda wartość albo do wiersza dobrych wartości, jeśli walidacja przebiegła pomyślnie, albo do wiersza usługi :__errors__ W przeciwnym razie;
  • spowoduje sortowanie if sorter zdefiniowany dla danego klucza lub po prostu umieści wartość na początku listy (LIFO, patrz rozdział Сортировка poniżej);
  • przytnie wiersz zgodnie z parametrem :limit przekazane podczas stworzenia;
  • zwróci zaktualizowaną strukturę 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]}

również Vela przybory Access, dzięki czemu możesz użyć dowolnej ze standardowych funkcji do głębokiego aktualizowania struktur z arsenału w celu aktualizacji wartości Kernel: Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2, Kernel.get_and_update_in/3.

Walidacja

Walidator można zdefiniować jako:

  • funkcja zewnętrzna z jednym argumentem (&MyMod.my_fun/1), otrzyma wartość jedynie do walidacji;
  • funkcja zewnętrzna z dwoma argumentami, &MyMod.my_fun/2, dostanie parę serie, value do walidacji;
  • wdrożenie modułu Vela.Validator;
  • parametr konfiguracyjny thresholdoraz – opcjonalnie – compare_by, patrz rozdział Porównanie poniżej.

Jeśli weryfikacja zakończy się pomyślnie, wartość zostanie dodana do listy pod odpowiednim kluczem; w przeciwnym razie krotka {serie, value} wysłane :__errors_.

Porównanie

Wartości przechowywane w tych wierszach mogą być dowolne. Nauczać Vela aby je porównać, konieczne jest przeniesienie compare_by parametr w definicji serii (chyba, że ​​wartości nie można porównać z normą Kernel.</2); ten parametr musi być typu (Vela.value() -> number()). Domyślnie jest to proste & &1.

Można także przekazać parametr do definicji wiersza comparator do obliczenia wartości delta (min/max); na przykład poprzez transmisję Date.diff/2 jako komparator możesz uzyskać prawidłowe delty dla dat.

Innym wygodnym sposobem pracy jest przekazanie parametru threshold, który określa maksymalny dopuszczalny stosunek nowej wartości do {min, max} interwał. Ponieważ jest on określony jako procent, sprawdzenie nie jest stosowane comparatorale nadal używa compare_by. Na przykład, aby określić wartość progową dla dat i godzin, należy określić compare_by: &DateTime.to_unix/1 (aby uzyskać wartość całkowitą) i threshold: 1, powodując, że nowe wartości będą dozwolone tylko wtedy, gdy są obecne ±band odstęp od bieżących wartości.

Wreszcie możesz użyć Vela.equal?/2 aby porównać dwie pamięci podręczne. Jeśli wartości definiują funkcję equal?/2 lub compare/2, wtedy te funkcje zostaną użyte do porównania, w przeciwnym razie głupio skorzystamy ==/2.

Uzyskiwanie wartości

Przetwarzanie bieżącego stanu zwykle rozpoczyna się od wywołania Vela.purge/1, który usuwa przestarzałe wartości (if validator przywiązany do timestamps). Możesz wtedy zadzwonić Vela.slice/1który powróci keyword z nazwami wierszy jako kluczami i pierwszymi, rzeczywistymi wartościami.

Możesz także użyć get_in/2/pop_in/2 dla niskiego poziomu dostępu do wartości w każdym wierszu.

Aplikacja

Vela może być niezwykle przydatny jako pamięć podręczna szeregów czasowych w stanie procesu, np GenServer/Agent. Nie chcemy nigdy używać nieaktualnych wartości kursu i w tym celu po prostu utrzymujemy proces w stanie przetworzonym Vela, z walidatorem pokazanym poniżej.

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

и Vela.purge/1 cicho usuwa wszystkie nieaktualne wartości za każdym razem, gdy potrzebujemy danych. Aby uzyskać dostęp do rzeczywistych wartości, po prostu wywołujemy Vela.slice/1, a gdy wymagana jest krótka historia kursu (cała seria), po prostu zwracamy ją - już posortowaną - ze zweryfikowanymi wartościami.

Szczęśliwego buforowania szeregów czasowych!

Źródło: www.habr.com

Dodaj komentarz