Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name = "reki"
version = "1.1.0"
version = "1.2.0"

description = "A Gleam actor registry that manages actors by key, with fast ETS lookups and automatic cleanup"
licences = ["MIT"]
Expand Down
18 changes: 18 additions & 0 deletions src/reki.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,21 @@ pub fn do_start(
start_child(registry.server_name, key, start_fn, timeout)
}

/// List every `(key, subject)` pair currently in the registry, in no
/// particular order. A direct ETS read (`ets:tab2list`), so it's cheap and
/// doesn't serialise through the gen_server — and, like `lookup`, it may
/// briefly include an entry for a process that just died but whose EXIT
/// reki hasn't processed yet.
///
/// Mirrors gen_registry's `to_list/1` (its `reduce/3` is `to_list |> fold`
/// here): use it to sweep the registry (rebalancing, metrics, draining)
/// without maintaining a parallel index of keys.
pub fn to_list(
registry: Registry(key, msg),
) -> List(#(key, process.Subject(msg))) {
ets_tab2list(registry.table_name)
}

@internal
pub fn get_pid(registry: Registry(a, b)) -> Result(process.Pid, Nil) {
whereis_server(registry.server_name)
Expand Down Expand Up @@ -171,3 +186,6 @@ fn new_table_name() -> TableName

@external(erlang, "reki_server", "lookup")
fn ets_lookup(table_name: TableName, key: key) -> Option(process.Subject(msg))

@external(erlang, "ets", "tab2list")
fn ets_tab2list(table_name: TableName) -> List(#(key, process.Subject(msg)))
27 changes: 27 additions & 0 deletions test/reki_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -1091,3 +1091,30 @@ pub fn start_child_timeout_returns_init_timeout_test() -> Result(
let assert Error(actor.InitTimeout) =
reki.lookup_or_start_with_timeout(registry, "slow_key", slow_start_fn, 1)
}

pub fn to_list_test() -> Nil {
let registry = create_registry()

assert reki.to_list(registry) == []

let assert Ok(actor1) = reki.lookup_or_start(registry, "key1", test_start_fn)
let assert Ok(actor2) = reki.lookup_or_start(registry, "key2", test_start_fn)

let entries = reki.to_list(registry)
assert list.length(entries) == 2
assert list.contains(entries, #("key1", actor1))
assert list.contains(entries, #("key2", actor2))
}

pub fn to_list_drops_dead_entries_test() -> Nil {
let registry = create_registry()

let assert Ok(actor1) = reki.lookup_or_start(registry, "key1", test_start_fn)
let assert Ok(_) = reki.lookup_or_start(registry, "key2", test_start_fn)

process.send(actor1, Crash)
// give the registry a moment to process the EXIT and prune the entry
process.sleep(timeout)

assert list.map(reki.to_list(registry), fn(entry) { entry.0 }) == ["key2"]
}
Loading