This repository was archived by the owner on Jan 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
This repository was archived by the owner on Jan 19, 2022. It is now read-only.
Single process bottleneck #17
Copy link
Copy link
Open
Description
Motivation
- The implementation of the token cache is a potential bottleneck https://github.com/brainnco/pluggy_elixir/blob/main/lib/pluggy_elixir/auth/guard.ex#L1 as it is a single process that holds the state, and is been called by all other processes.
- Also related to that as the cache is global, it doesn't support multiple keys/clients, with different credentials.
cc: @adrianolisboa @dmarasquin
Proposed solution
Principles behind the proposal:
- Avoid application configuration (https://hexdocs.pm/elixir/library-guidelines.html#avoid-application-configuration)
- Expose a interface for the user of the library add the client to its own supervisor tree, instead of starting our own (https://cevado.me/posts/otp-applications/#outline-container-headline-3)
- Allow multiples clients with different credentials
- Remove the single process bottleneck by leveraging a protected ets table. (https://dashbit.co/blog/goth-redesign)
Steps:
- Provide a way for the user start its own instance of pluggy in its own supervisor tree
{Pluggy, name: MyPluggy, client_id: "your-app-client-id", client_secret: "your-app-client-secret"}This "Pluggy" process could be a named supervisor that starts the token cache and store the client_id and client_secret.
- All operations receive the name of the instance of pluggy.
Pluggy.Webhooks.create(MyPluggy, params)- Start a token cache that uses an ets table an make all reads go directly to the ets table.
# example
defmodule Pluggy.CoolNameForTokenCache do
use GenServer
@token_key :token
def start_link(opts) do
name = Keyword.fetch!(opts, :name)
GenServer.start_link(__MODULE__, opts, name: name)
end
def init(opts) do
name = Keyword.fetch!(opts, :name)
:ets.new(table_name(name), [:set, :named_table, :protected, read_concurrency: true])
{:ok, %{name: name}}
end
def set(name, value) do
GenServer.call(name, {:set, value})
end
# note that the lookup goes directly to the table without calling the genserver
# removing the single process bottleneck that currently exists with the agent
def get(name) do
case :ets.lookup(table_name(name), @token_key) do
[{_key, value}] -> {:ok, value}
_ -> {:error, :not_found}
end
end
def handle_call({:set, value}, _from, state) do
:ets.insert(table_name(state.name), {@token_key, value})
{:reply, :ok, state}
end
defp table_name(name), do: Module.concat(name, "TokeCache.Table")
endMetadata
Metadata
Assignees
Labels
No labels