Vela → интелигентен кеш за времеви серии и др

Във финтех често трябва да обработваме доста огромни обеми от данни за валутните курсове. Получаваме данни от различни източници и всеки от тях има собствена идея как да екстраполира обменните курсове за утре, вдругиден, следващия месец и дори следващите три години. Само ако някой можеше да предвиди курсовете правилно, ще е време да затворите бизнеса и просто глупаво да обменяте пари напред-назад. Някои източници са по-надеждни, някои предоставят пълен боклук, с редки включвания на почти правилни стойности, но за екзотични двойки. Нашата работа е да пресеем тези десетки хиляди стойности в секунда и да определим какво точно да покажем на клиентите. Трябва да филтрираме единствената правилна стойност от тонове мръсотия и тиня, точно както фламингото правят на обяд.

Vela → интелигентен кеш за времеви серии и др

Особена отличителна черта на фламингото е масивният им извит надолу клюн, с който филтрират храната от вода или кал.
 - Wiki

Така се ражда библиотеката Vela, който съхранява кеш на състоянието за множество стойности на определени интервали от време. Под капака той филтрира лоши и остарели данни в движение и също така осигурява достъп до най-новите N валидирани стойности за всеки ключ (валутни двойки, в нашия случай).

Да кажем, че събираме курсове за три валутни двойки. Най-простото определение Vela за съхраняване на текущото състояние ще изглежда нещо подобно:

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

Актуализиране на стойностите

Vela.put/3 Функцията ще направи следното последователно:

  • ще причини validator върху стойността, ако такава е дефинирана (вижте глава Валидиране По-долу);
  • ще добави стойността или към реда с добри стойности, ако валидирането е било успешно, или към реда на услугата :__errors__ в противен случай;
  • ще доведе до сортиране, ако sorter дефинирана за даден ключ, или просто ще постави стойността в началото на списъка (LIFO, Вижте Глава сортиране По-долу);
  • ще отреже реда според параметъра :limit предадено при създаване;
  • ще върне актуализираната структура 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]}

Също Vela инструменти Access, така че можете да използвате всяка от стандартните функции за дълбоко актуализиране на структури от арсенала, за да актуализирате стойности Kernel: Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2, и Kernel.get_and_update_in/3.

Валидиране

Валидаторът може да се дефинира като:

  • външна функция с един аргумент (&MyMod.my_fun/1), той ще получи само стойността за валидиране;
  • външна функция с два аргумента, &MyMod.my_fun/2, тя ще получи чифт serie, value за валидиране;
  • внедряване на модул Vela.Validator;
  • конфигурационен параметър thresholdи - по избор - compare_by, Вижте Глава сравнение по-долу.

Ако проверката е успешна, стойността се добавя към списъка под съответния ключ; в противен случай кортежът {serie, value} отива :__errors_.

Сравнение

Стойностите, съхранявани в тези редове, могат да бъдат всякакви. Да преподавам Vela за да ги сравните, е необходимо да прехвърлите compare_by параметър в дефиницията на серията (освен ако стойностите не могат да бъдат сравнени със стандарта Kernel.</2); този параметър трябва да е от тип (Vela.value() -> number()). По подразбиране е просто & &1.

Освен това можете да подадете параметър към дефиницията на реда comparator за изчисляване на делта стойности (min/max); например чрез предаване Date.diff/2 като компаратор можете да получите правилните делти за дати.

Друг удобен начин за работа е предаването на параметър threshold, което определя максимално допустимото съотношение на новата стойност към {min, max} интервал. Тъй като е посочено като процент, проверката не се използва comparatorно все още използва compare_by. Например, за да посочите прагова стойност за дати и часове, трябва да посочите compare_by: &DateTime.to_unix/1 (за да получите цяло число) и threshold: 1, което кара нови стойности да бъдат разрешени само ако са в ±band интервал от текущите стойности.

Накрая можете да използвате Vela.equal?/2 за сравняване на два кеша. Ако стойностите дефинират функция equal?/2 или compare/2, тогава тези функции ще се използват за сравнение, в противен случай ние глупаво използваме ==/2.

Получаване на стойности

Обработката на текущото състояние обикновено започва с повикване Vela.purge/1, който премахва остарелите стойности (ако validator завързан за timestamps). След това можете да се обадите Vela.slice/1което ще се върне keyword с имена на редове като ключове и първите действителни стойности.

Можете също да използвате get_in/2/pop_in/2 за достъп на ниско ниво до стойностите във всеки ред.

App

Vela може да бъде изключително полезно като кеш за времеви серии в състояние на процес като GenServer/Agent. Искаме никога да не използваме остарели стойности на курса и за да направим това, просто поддържаме процеса с обработено състояние Vela, с показания по-долу валидатор.

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

и Vela.purge/1 тихо премахва всички остарели стойности всеки път, когато имаме нужда от данните. За достъп до действителните стойности просто се обаждаме Vela.slice/1, а когато се изисква малка история на курса (цялата серия), ние просто я връщаме - вече сортирана - с валидирани стойности.

Приятно кеширане на времеви серии!

Източник: www.habr.com

Добавяне на нов коментар