Реестр
Локальное, децентрализованное и масштабируемое хранилище пар «ключ-значение» для процессов.
Позволяет разработчикам искать один или несколько процессов по заданному ключу. Если реестр допускает :unique ключей, то ключ указывает на 0 или 1 процесс. Если реестр допускает :duplicate ключей, то один ключ может указывать на любое количество процессов. В обоих случаях разные ключи могут указывать на один и тот же процесс.
Каждый элемент в реестре связан с процессом, который зарегистрировал ключ. Если процесс завершается, ключи, связанные с этим процессом, автоматически удаляются. Все сравнения ключей в реестре выполняются с помощью операции совпадения (===/2).
Реестр может использоваться для различных целей, таких как поиск по имени (используя опцию :via), хранение свойств, настройка пользовательских правил маршрутизации или реализация системы pubsub. Мы рассмотрим некоторые из этих вариантов использования ниже.
Реестр также может быть прозрачно разделен на части, что обеспечивает более масштабируемое поведение для работы реестров в высококонкурентных средах с тысячами или миллионами записей.
Использование в :via
После запуска реестра с заданным именем с помощью Registry.start_link/1, он может использоваться для регистрации и доступа к именованным процессам с помощью кортежа {:via, Registry, {registry, key}}:
{:ok, _} = Registry.start_link(keys: :unique, name: Registry.ViaTest)
name = {:via, Registry, {Registry.ViaTest, "agent"}}
{:ok, _} = Agent.start_link(fn -> 0 end, name: name)
Agent.get(name, & &1)
#=> 0
Agent.update(name, &(&1 + 1))
Agent.get(name, & &1)
#=> 1 В предыдущем примере нас не интересовало назначение значения процессу:
Registry.lookup(Registry.ViaTest, "agent")
#=> [{self(), nil}] Однако в некоторых случаях может потребоваться назначение значения процессу с использованием альтернативного кортежа {:via, Registry, {registry, key, value}}:
{:ok, _} = Registry.start_link(keys: :unique, name: Registry.ViaTest)
name = {:via, Registry, {Registry.ViaTest, "agent", :hello}}
{:ok, _} = Agent.start_link(fn -> 0 end, name: name)
Registry.lookup(Registry.ViaTest, "agent")
#=> [{self(), :hello}] До этого момента мы запускали Registry с помощью start_link/1. Обычно реестр запускается как часть дерева управления:
{Registry, keys: :unique, name: Registry.ViaTest} Только реестры с уникальными ключами могут быть использованы в :via. Если имя уже занято, функция, специфичная для данного случая, start_link (Agent.start_link/2 в примере выше), вернёт {:error, {:already_started, current_pid}}.
Использование как диспетчера
Registry имеет механизм маршрутизации, позволяющий разработчикам реализовывать пользовательскую логику маршрутизации, вызываемую из вызывающей стороны. Например, предположим, что у нас есть дублирующий реестр, запущенный следующим образом:
{:ok, _} = Registry.start_link(keys: :duplicate, name: Registry.DispatcherTest) Вызывая register/3, разные процессы могут регистрироваться под заданным ключом и назначать любое значение под этим ключом. В этом случае давайте зарегистрируем текущий процесс под ключом "hello" и присоединим к нему кортеж {IO, :inspect}:
{:ok, _} = Registry.register(Registry.DispatcherTest, "hello", {IO, :inspect}) Теперь сущность, заинтересованная в маршрутизации событий для заданного ключа, может вызвать dispatch/3, передав ключ и обратный вызов. Этот обратный вызов будет вызван со списком всех значений, зарегистрированных под запрошенным ключом, наряду с pid процесса, который зарегистрировал каждое значение, в виде кортежей {pid, value}. В нашем примере value будет кортежем {module, function} в коде выше:
Registry.dispatch(Registry.DispatcherTest, "hello", fn entries ->
for {pid, {module, function}} <- entries, do: apply(module, function, [pid])
end)
# Prints #PID<...> where the pid is for the process that called register/3 above
#=> :ok Маршрутизация происходит в процессе, который вызывает dispatch/3, либо последовательно, либо параллельно в случае нескольких разделов (через запущенные задачи). Зарегистрированные процессы не участвуют в маршрутизации, если их участие не выполняется явно (например, путём отправки им сообщения в обратном вызове).
Кроме того, если при маршрутизации произошла ошибка из-за неправильной регистрации, маршрутизация всегда завершится ошибкой, и зарегистрированный процесс не будет уведомлен. Поэтому давайте убедимся, что мы по крайней мере обернём и сообщим об этих ошибках:
require Logger
Registry.dispatch(Registry.DispatcherTest, "hello", fn entries ->
for {pid, {module, function}} <- entries do
try do
apply(module, function, [pid])
catch
kind, reason ->
formatted = Exception.format(kind, reason, __STACKTRACE__)
Logger.error("Registry.dispatch/3 failed with #{formatted}")
end
end
end)
# Prints #PID<...>
#=> :ok Вы также можете заменить всю систему apply явным отправлением сообщений. Это пример, который мы рассмотрим дальше.
Использование как PubSub
Реестры также могут использоваться для реализации локальной, нераспределённой, масштабируемой системы PubSub, используя функцию dispatch/3, аналогично предыдущему разделу: в этом случае, однако, мы будем отправлять сообщения каждому связанному процессу, а не вызывать заданную функцию модуля.
В этом примере мы также установим количество разделов в соответствии с количеством активных планировщиков, что сделает реестр более эффективным в высококонкурентных средах:
{:ok, _} =
Registry.start_link(
keys: :duplicate,
name: Registry.PubSubTest,
partitions: System.schedulers_online()
)
{:ok, _} = Registry.register(Registry.PubSubTest, "hello", [])
Registry.dispatch(Registry.PubSubTest, "hello", fn entries ->
for {pid, _} <- entries, do: send(pid, {:broadcast, "world"})
end)
#=> :ok В примере выше сообщение {:broadcast, "world"} было разослано всем процессам, зарегистрированным под «темой» (или «ключом», как мы его называли до сих пор) "hello".
Третьим аргументом, передаваемым в register/3, является значение, связанное с текущим процессом. В то время как в предыдущем разделе мы использовали его при маршрутизации, в этом конкретном примере мы в нём не заинтересованы, поэтому мы установили его в пустой список. При необходимости вы можете сохранить более значимое значение.
Регистрации
Поиск, маршрутизация и регистрация являются эффективными и мгновенными за счёт отложенной отписки. Например, если процесс завершается, его ключи автоматически удаляются из реестра, но изменение может не распространиться немедленно. Это означает, что некоторые операции могут возвращать процессы, которые уже завершены. В таких случаях это будет явно указано в документации функции.
Однако помните, что такие случаи обычно не являются проблемой. В конце концов, процесс, на который ссылается pid, может завершиться в любое время, включая момент между получением значения из реестра и отправкой ему сообщения. Многие части стандартной библиотеки разработаны для работы с этим, например, Process.monitor/1, которая будет доставлять сообщение :DOWN немедленно, если отслеживаемый процесс уже завершился, и Kernel.send/2, которая действует как ничто для завершённых процессов.
ETS
Обратите внимание, что реестр использует одну таблицу ETS плюс две таблицы ETS на раздел.
Сводка
Типы
- guard()
Защита для оценки при сопоставлении объектов в реестре
- guards()
Список защит для оценки при сопоставлении объектов в реестре
- key()
Тип ключей, разрешённых при регистрации
- keys()
Тип реестра
- match_pattern()
Шаблон для сопоставления объектов в реестре
- meta_key()
Тип метаданных ключей реестра
- meta_value()
Тип метаданных значений реестра
- registry()
Идентификатор реестра
- value()
Тип значений, разрешённых при регистрации
Функции
- child_spec(opts)
Возвращает спецификацию для запуска реестра под надзором.
- count(registry)
Возвращает количество зарегистрированных ключей в реестре. Выполняется за постоянное время.
- count_match(registry, key, pattern, guards \\ [])
Возвращает количество пар «ключ-значение» под заданным ключом в реестре, которые соответствуют шаблону.
- dispatch(registry, key, mfa_or_fun, opts \\ [])
Вызывает обратный вызов со всеми записями под заданным ключом в каждом разделе для данного реестра.
- keys(registry, pid)
Возвращает известные ключи для данного процесса в реестре в произвольном порядке.
- lookup(registry, key)
Находит пару «ключ-значение» для заданного ключа в реестре в произвольном порядке.
- match(registry, key, pattern, guards \\ [])
Возвращает пары «ключ-значение» под заданным ключом в реестре, которые соответствуют шаблону.
- meta(registry, key)
Считывает метаданные реестра по заданному ключу.
- put_meta(registry, key, value)
Сохраняет метаданные реестра.
- register(registry, key, value)
Регистрирует текущий процесс под заданным ключом в реестре.
- start_link(options)
Запускает реестр как процесс с надзором.
- unregister(registry, key)
Удаляет все записи для заданного ключа, связанные с текущим процессом.
- unregister_match(registry, key, pattern, guards \\ [])
Удаляет записи для заданного ключа, соответствующие шаблону.
- update_value(registry, key, callback)
Обновляет значение для заданного ключа для текущего процесса в уникальном реестре.
Типы
guard()
guard() :: {atom() | term()} Защита для оценки при сопоставлении объектов в реестре
guards()
guards() :: [guard()] | []
Список защит для оценки при сопоставлении объектов в реестре
key()
key() :: term()
Тип ключей, разрешённых при регистрации
keys()
keys() :: :unique | :duplicate
Тип реестра
match_pattern()
match_pattern() :: atom() | term()
Шаблон для сопоставления с объектами в реестре
meta_key()
meta_key() :: atom() | tuple()
Тип ключей метаданных реестра
meta_value()
meta_value() :: term()
Тип значений метаданных реестра
registry()
registry() :: atom()
Идентификатор реестра
value()
value() :: term()
Тип значений, разрешенных для регистрации
Функции
child_spec(opts)
(since 1.5.0)Возвращает спецификацию для запуска реестра под надзором.
См. Supervisor.
count(registry)
(since 1.7.0)count(registry()) :: non_neg_integer()
Возвращает количество зарегистрированных ключей в реестре. Выполняется за постоянное время.
Примеры
В примере ниже мы регистрируем текущий процесс и запрашиваем количество ключей в реестре:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueCountTest)
iex> Registry.count(Registry.UniqueCountTest)
0
iex> {:ok, _} = Registry.register(Registry.UniqueCountTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.UniqueCountTest, "world", :world)
iex> Registry.count(Registry.UniqueCountTest)
2 То же самое относится к дублирующим реестрам:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateCountTest)
iex> Registry.count(Registry.DuplicateCountTest)
0
iex> {:ok, _} = Registry.register(Registry.DuplicateCountTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.DuplicateCountTest, "hello", :world)
iex> Registry.count(Registry.DuplicateCountTest)
2 count_match(registry, key, pattern, guards \\ [])
(since 1.7.0)count_match(registry(), key(), match_pattern(), guards()) :: non_neg_integer()
Возвращает количество пар {pid, value} по заданному key в registry которые соответствуют pattern.
Шаблон должен быть атомом или кортежем, который будет соответствовать структуре значения, хранящегося в реестре. Атом :_ может быть использован для игнорирования заданного значения или элемента кортежа, а атом :"$1" может быть использован для временного присвоения части шаблона переменной для последующего сравнения.
По желанию, можно передать список условий защиты для более точного соответствия. Каждая защита — это кортеж, который описывает проверки, которые должны быть пройдены присвоенной частью шаблона. Например, условие защиты $1 > 1 выражалось бы как кортеж {:>, :"$1", 1}. Обратите внимание, что условия защиты будут работать только для присвоенных переменных, таких как :"$1", :"$2" и т.д. Избегайте использования специальных переменных соответствия :"$_" и :"$$", так как это может работать непредсказуемо.
Если соответствий нет, возвращается ноль.
Для уникальных реестров требуется поиск в одном разделе. Для дублирующих реестров необходимо найти во всех разделах.
Примеры
В примере ниже мы регистрируем текущий процесс по одному ключу в дублирующем реестре, но с разными значениями:
iex> Registry.start_link(keys: :duplicate, name: Registry.CountMatchTest)
iex> {:ok, _} = Registry.register(Registry.CountMatchTest, "hello", {1, :atom, 1})
iex> {:ok, _} = Registry.register(Registry.CountMatchTest, "hello", {2, :atom, 2})
iex> Registry.count_match(Registry.CountMatchTest, "hello", {1, :_, :_})
1
iex> Registry.count_match(Registry.CountMatchTest, "hello", {2, :_, :_})
1
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:_, :atom, :_})
2
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:"$1", :_, :"$1"})
2
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:_, :_, :"$1"}, [{:>, :"$1", 1}])
1
iex> Registry.count_match(Registry.CountMatchTest, "hello", {:_, :"$1", :_}, [{:is_atom, :"$1"}])
2 dispatch(registry, key, mfa_or_fun, opts \\ [])
(since 1.4.0)dispatch(registry(), key(), dispatcher, keyword()) :: :ok
when dispatcher:
(entries :: [{pid(), value()}] -> term()) | {module(), atom(), [any()]} Вызывает обратный вызов со всеми записями под key в каждом разделе для данного registry.
Список entries — это непустой список пар из двух элементов, где первый элемент — pid, а второй — значение, связанное с pid. Если для данного ключа нет записей, обратный вызов никогда не вызывается.
Если реестр разбит на разделы, обратный вызов вызывается несколько раз в каждом разделе. Если реестр разбит на разделы и parallel: true задан в качестве опции, отправка происходит параллельно. В обоих случаях обратный вызов вызывается только если в этом разделе есть записи.
См. документацию модуля для примеров использования функции dispatch/3 для создания пользовательской отправки или системы публикаций-подписок.
keys(registry, pid)
(since 1.4.0)keys(registry(), pid()) :: [key()]
Возвращает известные ключи для данного pid в registry в произвольном порядке.
Если реестр уникальный, ключи уникальны. В противном случае они могут содержать дубликаты, если процесс был зарегистрирован по одному ключу несколько раз. Список будет пустым, если процесс мертв или у него нет ключей в этом реестре.
Примеры
Регистрация в уникальном реестре не допускает нескольких записей:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueKeysTest)
iex> Registry.keys(Registry.UniqueKeysTest, self())
[]
iex> {:ok, _} = Registry.register(Registry.UniqueKeysTest, "hello", :world)
iex> Registry.register(Registry.UniqueKeysTest, "hello", :later) # registry is :unique
{:error, {:already_registered, self()}}
iex> Registry.keys(Registry.UniqueKeysTest, self())
["hello"] Однако это возможно для дублирующих реестров:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateKeysTest)
iex> Registry.keys(Registry.DuplicateKeysTest, self())
[]
iex> {:ok, _} = Registry.register(Registry.DuplicateKeysTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.DuplicateKeysTest, "hello", :world)
iex> Registry.keys(Registry.DuplicateKeysTest, self())
["hello", "hello"] lookup(registry, key)
(since 1.4.0)lookup(registry(), key()) :: [{pid(), value()}] Находит пару {pid, value} для данного key в registry в произвольном порядке.
Пустой список, если нет соответствий.
Для уникальных реестров требуется поиск в одном разделе. Для дублирующих реестров необходимо искать во всех разделах.
Примеры
В примере ниже мы регистрируем текущий процесс и ищем его как из себя, так и из других процессов:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueLookupTest)
iex> Registry.lookup(Registry.UniqueLookupTest, "hello")
[]
iex> {:ok, _} = Registry.register(Registry.UniqueLookupTest, "hello", :world)
iex> Registry.lookup(Registry.UniqueLookupTest, "hello")
[{self(), :world}]
iex> Task.async(fn -> Registry.lookup(Registry.UniqueLookupTest, "hello") end) |> Task.await()
[{self(), :world}] То же самое относится к дублирующим реестрам:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateLookupTest)
iex> Registry.lookup(Registry.DuplicateLookupTest, "hello")
[]
iex> {:ok, _} = Registry.register(Registry.DuplicateLookupTest, "hello", :world)
iex> Registry.lookup(Registry.DuplicateLookupTest, "hello")
[{self(), :world}]
iex> {:ok, _} = Registry.register(Registry.DuplicateLookupTest, "hello", :another)
iex> Enum.sort(Registry.lookup(Registry.DuplicateLookupTest, "hello"))
[{self(), :another}, {self(), :world}] match(registry, key, pattern, guards \\ [])
(since 1.4.0)match(registry(), key(), match_pattern(), guards()) :: [{pid(), term()}] Возвращает пары {pid, value} по заданному key в registry которые соответствуют pattern.
Шаблон должен быть атомом или кортежем, который будет соответствовать структуре значения, хранящегося в реестре. Атом :_ может быть использован для игнорирования заданного значения или элемента кортежа, а атом :"$1" может быть использован для временного присвоения части шаблона переменной для последующего сравнения.
По желанию, можно передать список условий защиты для более точного соответствия. Каждая защита — это кортеж, который описывает проверки, которые должны быть пройдены присвоенной частью шаблона. Например, условие защиты $1 > 1 выражалось бы как кортеж {:>, :"$1", 1}. Обратите внимание, что условия защиты будут работать только для присвоенных переменных, таких как :"$1", :"$2" и т.д. Избегайте использования специальных переменных соответствия :"$_" и :"$$", так как это может работать непредсказуемо.
Если соответствий нет, возвращается пустой список.
Для уникальных реестров требуется поиск в одном разделе. Для дублирующих реестров необходимо искать во всех разделах.
Примеры
В примере ниже мы регистрируем текущий процесс по одному ключу в дублирующем реестре, но с разными значениями:
iex> Registry.start_link(keys: :duplicate, name: Registry.MatchTest)
iex> {:ok, _} = Registry.register(Registry.MatchTest, "hello", {1, :atom, 1})
iex> {:ok, _} = Registry.register(Registry.MatchTest, "hello", {2, :atom, 2})
iex> Registry.match(Registry.MatchTest, "hello", {1, :_, :_})
[{self(), {1, :atom, 1}}]
iex> Registry.match(Registry.MatchTest, "hello", {2, :_, :_})
[{self(), {2, :atom, 2}}]
iex> Registry.match(Registry.MatchTest, "hello", {:_, :atom, :_}) |> Enum.sort()
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}]
iex> Registry.match(Registry.MatchTest, "hello", {:"$1", :_, :"$1"}) |> Enum.sort()
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}]
iex> guards = [{:>, :"$1", 1}]
iex> Registry.match(Registry.MatchTest, "hello", {:_, :_, :"$1"}, guards)
[{self(), {2, :atom, 2}}]
iex> guards = [{:is_atom, :"$1"}]
iex> Registry.match(Registry.MatchTest, "hello", {:_, :"$1", :_}, guards) |> Enum.sort()
[{self(), {1, :atom, 1}}, {self(), {2, :atom, 2}}] meta(registry, key)
(since 1.4.0)meta(registry(), meta_key()) :: {:ok, meta_value()} | :error Считывает метаданные реестра, заданные по start_link/3.
В качестве ключей разрешены атомы и кортежи.
Примеры
iex> Registry.start_link(keys: :unique, name: Registry.MetaTest, meta: [custom_key: "custom_value"])
iex> Registry.meta(Registry.MetaTest, :custom_key)
{:ok, "custom_value"}
iex> Registry.meta(Registry.MetaTest, :unknown_key)
:error put_meta(registry, key, value)
(since 1.4.0)put_meta(registry(), meta_key(), meta_value()) :: :ok
Сохраняет метаданные реестра.
В качестве ключей разрешены атомы и кортежи.
Примеры
iex> Registry.start_link(keys: :unique, name: Registry.PutMetaTest)
iex> Registry.put_meta(Registry.PutMetaTest, :custom_key, "custom_value")
:ok
iex> Registry.meta(Registry.PutMetaTest, :custom_key)
{:ok, "custom_value"}
iex> Registry.put_meta(Registry.PutMetaTest, {:tuple, :key}, "tuple_value")
:ok
iex> Registry.meta(Registry.PutMetaTest, {:tuple, :key})
{:ok, "tuple_value"} register(registry, key, value)
(since 1.4.0)register(registry(), key(), value()) ::
{:ok, pid()} | {:error, {:already_registered, pid()}} Регистрирует текущий процесс по заданному key в registry.
Также необходимо указать значение, которое будет ассоциировано с этой регистрацией. Это значение будет получено при отправке или поиске ключа.
Функция возвращает {:ok, owner} или {:error, reason}. owner — это pid в разделе реестра, отвечающий за pid. Владелец автоматически связан с вызывающим процессом.
Если реестр имеет уникальные ключи, он возвращает {:ok, owner} за исключением случаев, когда ключ уже связан с pid, в этом случае возвращается {:error, {:already_registered, pid}}.
Если реестр имеет повторяющиеся ключи, несколько регистраций текущего процесса по одному ключу разрешены.
Примеры
Регистрация в уникальном реестре не допускает нескольких записей:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueRegisterTest)
iex> {:ok, _} = Registry.register(Registry.UniqueRegisterTest, "hello", :world)
iex> Registry.register(Registry.UniqueRegisterTest, "hello", :later)
{:error, {:already_registered, self()}}
iex> Registry.keys(Registry.UniqueRegisterTest, self())
["hello"] Однако это возможно для дублирующих реестров:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateRegisterTest)
iex> {:ok, _} = Registry.register(Registry.DuplicateRegisterTest, "hello", :world)
iex> {:ok, _} = Registry.register(Registry.DuplicateRegisterTest, "hello", :world)
iex> Registry.keys(Registry.DuplicateRegisterTest, self())
["hello", "hello"] start_link(options)
(since 1.5.0)start_link(
keys: keys(),
name: registry(),
partitions: pos_integer(),
listeners: [atom()],
meta: meta
) :: {:ok, pid()} | {:error, term()}
when meta: [{meta_key(), meta_value()}] Запускает реестр как процесс надзора.
Вручную его можно запустить следующим образом:
Registry.start_link(keys: :unique, name: MyApp.Registry)
В вашей дереве надзора вы напишите:
Supervisor.start_link([
{Registry, keys: :unique, name: MyApp.Registry}
], strategy: :one_for_one) Для интенсивных рабочих нагрузок реестр также может быть поделен на разделы (указанием опции :partitions). Если требуется разделение, хорошим значением по умолчанию является установка количества разделов на количество доступных планировщиков:
Registry.start_link( keys: :unique, name: MyApp.Registry, partitions: System.schedulers_online() )
или:
Supervisor.start_link([
{Registry, keys: :unique, name: MyApp.Registry, partitions: System.schedulers_online()}
], strategy: :one_for_one) Опции
Для реестра требуются следующие ключи:
-
:keys- выбрать, являются ли ключи:uniqueили:duplicate -
:name- имя реестра и его таблицы
Следующие ключи являются необязательными:
-
:partitions- количество разделов в реестре. По умолчанию1 -
:listeners- список именованных процессов, которые уведомляются о событиях:registerи:unregister. Регистрируемый процесс должен отслеживаться слушателем, если слушатель хочет быть уведомлён о сбоях зарегистрированного процесса. -
:meta- список ключевых метаданных, которые будут присоединены к реестру.
unregister(registry, key)
(since 1.4.0)unregister(registry(), key()) :: :ok
Удаляет все записи для заданного key связанного с текущим процессом в registry.
Всегда возвращает :ok и автоматически отсоединяет текущий процесс от владельца, если нет больше ключей, связанных с текущим процессом. См. также register/3 для получения дополнительной информации о "владельце".
Примеры
Для уникальных реестров:
iex> Registry.start_link(keys: :unique, name: Registry.UniqueUnregisterTest) iex> Registry.register(Registry.UniqueUnregisterTest, "hello", :world) iex> Registry.keys(Registry.UniqueUnregisterTest, self()) ["hello"] iex> Registry.unregister(Registry.UniqueUnregisterTest, "hello") :ok iex> Registry.keys(Registry.UniqueUnregisterTest, self()) []
Для реестров с дубликатами:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateUnregisterTest) iex> Registry.register(Registry.DuplicateUnregisterTest, "hello", :world) iex> Registry.register(Registry.DuplicateUnregisterTest, "hello", :world) iex> Registry.keys(Registry.DuplicateUnregisterTest, self()) ["hello", "hello"] iex> Registry.unregister(Registry.DuplicateUnregisterTest, "hello") :ok iex> Registry.keys(Registry.DuplicateUnregisterTest, self()) []
unregister_match(registry, key, pattern, guards \\ [])
(since 1.5.0)Отменить регистрацию записей для заданного ключа, соответствующего шаблону.
Примеры
Для уникальных реестров это можно использовать для условной отмены регистрации ключа на основе того, соответствует ли он определенному значению.
iex> Registry.start_link(keys: :unique, name: Registry.UniqueUnregisterMatchTest) iex> Registry.register(Registry.UniqueUnregisterMatchTest, "hello", :world) iex> Registry.keys(Registry.UniqueUnregisterMatchTest, self()) ["hello"] iex> Registry.unregister_match(Registry.UniqueUnregisterMatchTest, "hello", :foo) :ok iex> Registry.keys(Registry.UniqueUnregisterMatchTest, self()) ["hello"] iex> Registry.unregister_match(Registry.UniqueUnregisterMatchTest, "hello", :world) :ok iex> Registry.keys(Registry.UniqueUnregisterMatchTest, self()) []
Для реестров с дубликатами:
iex> Registry.start_link(keys: :duplicate, name: Registry.DuplicateUnregisterMatchTest)
iex> Registry.register(Registry.DuplicateUnregisterMatchTest, "hello", :world_a)
iex> Registry.register(Registry.DuplicateUnregisterMatchTest, "hello", :world_b)
iex> Registry.register(Registry.DuplicateUnregisterMatchTest, "hello", :world_c)
iex> Registry.keys(Registry.DuplicateUnregisterMatchTest, self())
["hello", "hello", "hello"]
iex> Registry.unregister_match(Registry.DuplicateUnregisterMatchTest, "hello", :world_a)
:ok
iex> Registry.keys(Registry.DuplicateUnregisterMatchTest, self())
["hello", "hello"]
iex> Registry.lookup(Registry.DuplicateUnregisterMatchTest, "hello")
[{self(), :world_b}, {self(), :world_c}] update_value(registry, key, callback)
(since 1.4.0)update_value(registry(), key(), (value() -> value())) ::
{new_value :: term(), old_value :: term()} | :error Обновляет значение для key для текущего процесса в уникальном registry.
Возвращает кортеж {new_value, old_value} или :error, если такой ключ не назначен текущему процессу.
Если предоставлен реестр с дубликатами, возникает ошибка.
Примеры
iex> Registry.start_link(keys: :unique, name: Registry.UpdateTest)
iex> {:ok, _} = Registry.register(Registry.UpdateTest, "hello", 1)
iex> Registry.lookup(Registry.UpdateTest, "hello")
[{self(), 1}]
iex> Registry.update_value(Registry.UpdateTest, "hello", &(&1 + 1))
{2, 1}
iex> Registry.lookup(Registry.UpdateTest, "hello")
[{self(), 2}]
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.8.2/Registry.html