Skip to content

Instantly share code, notes, and snippets.

@leikind
Created January 7, 2021 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leikind/5574e1f2d40aeee239959b337c416df9 to your computer and use it in GitHub Desktop.
Save leikind/5574e1f2d40aeee239959b337c416df9 to your computer and use it in GitHub Desktop.
some ETS features
test "ets match, select, and select_delete" do
ets_table_name = :bar
:ets.new(ets_table_name, [:named_table, :set, :private])
expires_at1 = ~U[2021-01-07 15:00:00.000000Z]
# ~U is just a syntax sugar for struct DateTime
IO.inspect(~U[2021-01-07 15:00:00.000000Z], structs: false)
# I would convert DateTime to unix seconds and put it as a separate field to the ets record
# just for selecting expired records
expires_at1_unix = DateTime.to_unix(expires_at1)
token_login1 = %RemoteLogin.Models.TokenLogin{
encrypted_keystore_token: "encrypted_keystore_token1",
encrypted_pdk: "encrypted_pdk1",
encrypted_vault_token: "encrypted_vault_token1",
expires_at: expires_at1,
public_key: "PK1",
token: "token1"
}
expires_at2 = ~U[2021-01-07 15:33:33.000000Z]
expires_at2_unix = DateTime.to_unix(expires_at2)
token_login2 = %RemoteLogin.Models.TokenLogin{
encrypted_keystore_token: "encrypted_keystore_token2",
encrypted_pdk: "encrypted_pdk2",
encrypted_vault_token: "encrypted_vault_token2",
expires_at: expires_at2,
public_key: "PK2",
token: "token2"
}
# store unix seconds as the third item in the tuple
:ets.insert(ets_table_name, {"token1", token_login1, expires_at1_unix})
:ets.insert(ets_table_name, {"token2", token_login2, expires_at2_unix})
# return all records, that's what you do
ets_table_name |> :ets.tab2list() |> IO.inspect()
# match and match_object can only do `==`, not `>` or `<`. I think :-)
# so this is not what you are looking for
ets_table_name |> :ets.match_object({:"$1", :"$2", 1_610_031_600}) |> IO.inspect()
# we need to use :ets.select
select_specification = [
{
# initial match pattern the same as in `match_object` and `match`
{:"$1", :"$2", :"$3"},
# this is your WHERE clause :)
[{:<, :"$3", 1_610_031_601}],
# this is your SELECT clause, $_ will return the whole tuple
[:"$_"]
}
]
ets_table_name |> :ets.select(select_specification) |> IO.inspect()
# let's only return the key so that you can then run :ets.delete inside Enum.each
select_specification = [
{
{:"$1", :"$2", :"$3"},
[{:<, :"$3", 1_610_031_601}],
# only the key, the first item in the tuple
[:"$1"]
}
]
ets_table_name |> :ets.select(select_specification) |> IO.inspect()
# but we can do better, you don't need to run Enum.each with :ets.delete, you can delete all expired records in 1 ets call: select_delete
select_specification = [
{
{:"$1", :"$2", :"$3"},
[{:<, :"$3", 1_610_031_601}],
# it must be true for select_delete
# the docs:
# The match specification has to return the atom true if the object is to
# be deleted. No other return value gets the object deleted. So one cannot use
# the same match specification for looking up elements as for deleting them.
[true]
}
]
# will delete token1
:ets.select_delete(ets_table_name, select_specification)
# token2 is left
ets_table_name |> :ets.tab2list() |> IO.inspect()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment