从ets表中删除所有日期戳早于10秒的记录

时间:2018-12-19 10:14:13

标签: erlang elixir data-cleaning ets cache-expiration

我在灵药应用程序中有一个ets设置表。我需要清理其update_at字段早于10秒的记录。有没有一种方法可以设置到期时间或手动进行设置而无需遍历所有记录?我根据大于给定时间的时间戳来匹配记录。

示例记录:

key: key_1
record: %{id: key_1, updated_at: ~N[2018-12-19 10:08:47.803075]}

到目前为止,我已经有了这段代码

def clean_stale(previous_key) do
  if previous_key == :"$end_of_table" do
    :ok
  else
    device = get(previous_key)
    next_key = :ets.next(__MODULE__, previous_key)
    if NaiveDateTime.diff(NaiveDateTime.utc_now, device.last_recorded_at) > 10 do
      remove(device.id)
    end
    clean_stale(next_key)
  end
end

1 个答案:

答案 0 :(得分:4)

如果将“更新时间”存储为整数而不是NaiveDateTime结构,则可以使用匹配规范。

例如,获取当前时间作为自Unix时代以来的秒数:

> DateTime.to_unix(DateTime.utc_now())
1545215338

您可以执行以下操作:

iex(3)> :ets.new(:foo, [:public, :named_table])
:foo
iex(4)> :ets.insert(:foo, {:key1, DateTime.to_unix(DateTime.utc_now())})
true
iex(5)> :ets.insert(:foo, {:key2, DateTime.to_unix(DateTime.utc_now())})
true
iex(6)> :ets.tab2list(:foo)
[key2: 1545215144, key1: 1545215140]
iex(7)> :ets.select_delete(:foo, [{{:_, :"$1"}, [{:<, :"$1", 1545215144}], [true]}])
1
iex(8)> :ets.tab2list(:foo)
[key2: 1545215144]

在对ets:select_delete/2的呼叫中,我传递了一个match specification。它由三部分组成:

  • 使用{:_, :"$1"},我对表中的记录进行匹配。在此示例中,我有一个包含两个元素的元组。我用:_忽略了键,并用:"$1"将时间戳分配给匹配变量。
  • 使用[{:<, :"$1", 1545215144}],我指定只希望在此时间之前将记录与时间戳进行匹配。对于您的情况,您将计算过去十秒的时间,并将该值放在此处。
  • 对于[true],我指定要返回true来匹配记录,对于select_delete来说,这意味着“删除此记录”。

因此,在调用select_delete之后,只有第二条记录保留在表中。


如果时间戳记在地图内,则可以使用map_get进行访问并进行比较:

:ets.select_delete(:foo, [{{:_, :"$1"},
                           [{:<, {:map_get, :updated_at, :"$1"}, 1545215339}],
                           [true]}])

或者(在Erlang / OTP 18.0及更高版本中)匹配地图值:

:ets.select_delete(:foo, [{{:_, #{updated_at: :"$1"}},
                           [{:<, :"$1", 1545215339}],
                           [true]}])