Удалите все записи из таблицы ets, имеющие метку даты старше 10 секунд

У меня есть ets накрывать на стол в приложении эликсир. Мне нужно очистить записи, у которых их поле updated_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 ответ

Решение

Если вы храните "обновлено в" время как целое число, а не как NaiveDateTime struct, вы можете использовать соответствие спецификации.

Например, чтобы получить текущее время в виде количества секунд с начала эпохи 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 Я передаю соответствие спецификации. Он состоит из трех частей:

  • С {:_, :"$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]}])
Другие вопросы по тегам