+2
CHANGELOG.md
+2
CHANGELOG.md
···
38
39
- `mix atex.lexicons` now adds `@moduledoc false` to generated modules to stop
40
them from automatically cluttering documentation.
41
+
- `Atex.IdentityResolver.Cache.ETS` now uses ConCache instead of ETS directly,
42
+
with a 1-hour TTL for cached identity information.
43
44
## [0.6.0] - 2025-11-25
45
+35
-22
lib/atex/identity_resolver/cache/ets.ex
+35
-22
lib/atex/identity_resolver/cache/ets.ex
···
1
defmodule Atex.IdentityResolver.Cache.ETS do
2
alias Atex.IdentityResolver.Identity
3
@behaviour Atex.IdentityResolver.Cache
4
use Supervisor
5
6
-
@table :atex_identities
7
8
def start_link(opts) do
9
-
Supervisor.start_link(__MODULE__, opts)
10
end
11
12
@impl Supervisor
13
def init(_opts) do
14
-
:ets.new(@table, [:set, :public, :named_table])
15
-
Supervisor.init([], strategy: :one_for_one)
16
end
17
18
@impl Atex.IdentityResolver.Cache
19
@spec insert(Identity.t()) :: Identity.t()
20
def insert(identity) do
21
-
# TODO: benchmark lookups vs match performance, is it better to use a "composite" key or two inserts?
22
-
:ets.insert(@table, {{identity.did, identity.handle}, identity})
23
identity
24
end
25
26
@impl Atex.IdentityResolver.Cache
27
@spec get(String.t()) :: {:ok, Identity.t()} | {:error, atom()}
28
def get(identifier) do
29
-
lookup(identifier)
30
end
31
32
@impl Atex.IdentityResolver.Cache
33
@spec delete(String.t()) :: :noop | Identity.t()
34
def delete(identifier) do
35
-
case lookup(identifier) do
36
{:ok, identity} ->
37
-
:ets.delete(@table, {identity.did, identity.handle})
38
identity
39
40
_ ->
41
:noop
42
-
end
43
-
end
44
-
45
-
defp lookup(identifier) do
46
-
case :ets.match(@table, {{identifier, :_}, :"$1"}) do
47
-
[] ->
48
-
case :ets.match(@table, {{:_, identifier}, :"$1"}) do
49
-
[] -> {:error, :not_found}
50
-
[[identity]] -> {:ok, identity}
51
-
end
52
-
53
-
[[identity]] ->
54
-
{:ok, identity}
55
end
56
end
57
end
···
1
defmodule Atex.IdentityResolver.Cache.ETS do
2
+
@moduledoc """
3
+
ConCache-based implementation for Identity Resolver caching.
4
+
5
+
Stores identity information (DID and handle mappings) with a 1-hour TTL.
6
+
Uses two separate cache entries per identity to allow lookups by either DID or handle.
7
+
"""
8
+
9
alias Atex.IdentityResolver.Identity
10
@behaviour Atex.IdentityResolver.Cache
11
use Supervisor
12
13
+
@cache :atex_identities_cache
14
+
@ttl_ms :timer.hours(1)
15
16
def start_link(opts) do
17
+
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
18
end
19
20
@impl Supervisor
21
def init(_opts) do
22
+
children = [
23
+
{ConCache,
24
+
[
25
+
name: @cache,
26
+
ttl_check_interval: :timer.minutes(5),
27
+
global_ttl: @ttl_ms
28
+
]}
29
+
]
30
+
31
+
Supervisor.init(children, strategy: :one_for_one)
32
end
33
34
@impl Atex.IdentityResolver.Cache
35
@spec insert(Identity.t()) :: Identity.t()
36
def insert(identity) do
37
+
ConCache.put(@cache, {:did, identity.did}, identity)
38
+
ConCache.put(@cache, {:handle, identity.handle}, identity)
39
identity
40
end
41
42
@impl Atex.IdentityResolver.Cache
43
@spec get(String.t()) :: {:ok, Identity.t()} | {:error, atom()}
44
def get(identifier) do
45
+
case ConCache.get(@cache, {:did, identifier}) do
46
+
nil ->
47
+
case ConCache.get(@cache, {:handle, identifier}) do
48
+
nil -> {:error, :not_found}
49
+
identity -> {:ok, identity}
50
+
end
51
+
52
+
identity ->
53
+
{:ok, identity}
54
+
end
55
end
56
57
@impl Atex.IdentityResolver.Cache
58
@spec delete(String.t()) :: :noop | Identity.t()
59
def delete(identifier) do
60
+
case get(identifier) do
61
{:ok, identity} ->
62
+
ConCache.delete(@cache, {:did, identity.did})
63
+
ConCache.delete(@cache, {:handle, identity.handle})
64
identity
65
66
_ ->
67
:noop
68
end
69
end
70
end