Vela → 用于时间序列等的智能缓存

在金融科技领域,我们经常需要处理大量的货币汇率数据。 我们从不同的来源获得数据,每个人对于如何推断明天、后天、下个月甚至未来三年的汇率都有自己的想法。 如果有人能预测利率就好了 正确地,是时候关闭业务并愚蠢地来回换钱了。 有些来源更可靠,有些提供完全垃圾,很少包含几乎正确的值,但对于异国情调的夫妇来说。 我们的工作是每秒筛选这数以万计的值,并确定到底要向客户展示什么。 我们需要从大量的污垢和淤泥中过滤出一个正确的值,就像火烈鸟在午餐时所做的那样。

Vela → 用于时间序列等的智能缓存

火烈鸟的一个显着特征是它们巨大的向下弯曲的喙,它们用它从水或泥土中过滤食物。
  -  维基

图书馆就这样诞生了 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/2Kernel.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,这会删除过时的值(如果 validatortimestamps)。 然后你可以打电话 Vela.slice/1这将返回 keyword 以行名称作为键和第一个实际值。

你也可以使用 get_in/2/pop_in/2 用于对每行中的值进行低级访问。

应用

Vela 作为进程状态下的时间序列缓存非常有用,例如 GenServer/Agent。 我们希望永远不要使用过时的课程值,为此,我们只需保持流程的状态已处理 Vela,验证器如下所示。

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

и Vela.purge/1 每次我们需要数据时,都会悄悄地删除所有陈旧值。 要访问实际值,我们只需调用 Vela.slice/1,当需要课程的一小部分历史记录(整个系列)时,我们只需返回它(已经排序)和经过验证的值。

快乐的时间序列缓存!

来源: habr.com

添加评论