Skip to content

Commit

Permalink
Merge pull request #622 from Th3-M4jor/optimize-basic-get-by-id-all-c…
Browse files Browse the repository at this point in the history
…aches

Optimize fetching individual records on each cache type
  • Loading branch information
jchristgit authored Aug 16, 2024
2 parents e6e0b12 + af23bc5 commit 034f68c
Show file tree
Hide file tree
Showing 23 changed files with 168 additions and 128 deletions.
20 changes: 8 additions & 12 deletions lib/nostrum/cache/guild_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,14 @@ defmodule Nostrum.Cache.GuildCache do
Returns `{:error, :not_found}` if no result was found.
"""
@spec get(Guild.id()) :: {:ok, Guild.t()} | {:error, :not_found}
@spec get(Guild.id(), module()) :: {:ok, Guild.t()} | {:error, :not_found}
def get(guild_id, cache \\ @configured_cache) do
handle = :nostrum_guild_cache_qlc.get(guild_id, cache)

wrap_qlc(cache, fn ->
case :qlc.eval(handle) do
[guild] -> {:ok, guild}
[] -> {:error, :not_found}
end
end)
end
@callback get(Guild.id()) :: {:ok, Guild.t()} | {:error, :not_found}

@doc """
Retrieves a single `Nostrum.Struct.Guild` from the cache via its `id`.
Returns `{:error, :not_found}` if no result was found.
"""
defdelegate get(guild_id), to: @configured_cache

# Functions called from nostrum.

Expand Down
11 changes: 11 additions & 0 deletions lib/nostrum/cache/guild_cache/ets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ defmodule Nostrum.Cache.GuildCache.ETS do
def tabname, do: @table_name

# IMPLEMENTATION

@doc "Get a guild from the cache."
@impl GuildCache
@spec get(Guild.id()) :: {:ok, Guild.t()} | {:error, :not_found}
def get(guild_id) do
case :ets.lookup(@table_name, guild_id) do
[{_id, guild}] -> {:ok, guild}
[] -> {:error, :not_found}
end
end

@doc "Create the given guild in the cache."
@impl GuildCache
@spec create(map()) :: Guild.t()
Expand Down
12 changes: 12 additions & 0 deletions lib/nostrum/cache/guild_cache/mnesia.ex
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ if Code.ensure_loaded?(:mnesia) do
:ok
end

@impl GuildCache
@doc "Get a guild from the cache."
@spec get(Guild.id()) :: {:ok, Guild.t()} | {:error, :not_found}
def get(guild_id) do
:mnesia.activity(:sync_transaction, fn ->
case :mnesia.read(@table_name, guild_id) do
[{_tag, _id, guild}] -> {:ok, guild}
[] -> {:error, :not_found}
end
end)
end

# Used by dispatch

@impl GuildCache
Expand Down
3 changes: 3 additions & 0 deletions lib/nostrum/cache/guild_cache/noop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ defmodule Nostrum.Cache.GuildCache.NoOp do
Supervisor.init([], strategy: :one_for_one)
end

@impl GuildCache
def get(_guild_id), do: {:error, :not_found}

@impl GuildCache
def create(payload), do: Guild.to_struct(payload)

Expand Down
22 changes: 7 additions & 15 deletions lib/nostrum/cache/member_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ defmodule Nostrum.Cache.MemberCache do

@configured_cache Nostrum.Cache.Base.get_cache_module(:members, @default_cache_implementation)

@doc """
Retrieve a member from the cache by guild and user ID.
"""
@callback get(Guild.id(), Member.user_id()) :: {:ok, Member.t()} | {:error, atom()}

@doc """
Add the member for the given guild from upstream data.
Expand Down Expand Up @@ -116,7 +121,7 @@ defmodule Nostrum.Cache.MemberCache do
@spec get_with_user(Guild.id(), Member.user_id(), module()) ::
{Member.t(), User.t() | nil} | nil
def get_with_user(guild_id, member_id, cache \\ @configured_cache) do
case get(guild_id, member_id, cache) do
case cache.get(guild_id, member_id) do
{:ok, member} ->
case UserCache.get(member_id) do
{:ok, user} ->
Expand Down Expand Up @@ -152,20 +157,7 @@ defmodule Nostrum.Cache.MemberCache do
Get a single member on the given guild ID.
"""
@spec get(Guild.id(), Member.user_id()) :: {:ok, Member.t()} | {:error, atom()}
@spec get(Guild.id(), Member.user_id(), module()) :: {:ok, Member.t()} | {:error, atom()}
def get(guild_id, user_id, cache \\ @configured_cache) do
handle = :nostrum_member_cache_qlc.lookup(guild_id, user_id, cache)

wrap_qlc(cache, fn ->
case :qlc.e(handle) do
[result] ->
{:ok, result}

[] ->
{:error, :not_found}
end
end)
end
defdelegate get(guild_id, member_id), to: @configured_cache

@doc """
Fold (reduce) over members for the given guild ID.
Expand Down
13 changes: 13 additions & 0 deletions lib/nostrum/cache/member_cache/ets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ defmodule Nostrum.Cache.MemberCache.ETS do

# Used by dispatch

@impl MemberCache
@doc "Retrieve the member for the given guild and user in the cache."
@spec get(Guild.id(), Member.user_id()) :: {:ok, Member.t()} | {:error, any()}
def get(guild_id, user_id) do
case :ets.lookup(@table_name, {guild_id, user_id}) do
[{_, member}] ->
{:ok, member}

[] ->
{:error, :member_not_found}
end
end

@doc "Add the given member to the given guild in the cache."
@impl MemberCache
@spec create(Guild.id(), map()) :: Member.t()
Expand Down
14 changes: 14 additions & 0 deletions lib/nostrum/cache/member_cache/mnesia.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ if Code.ensure_loaded?(:mnesia) do

# Used by dispatch

@impl MemberCache
@doc "Retrieve the member for the given guild and user in the cache."
@spec get(Guild.id(), Member.user_id()) :: {:ok, Member.t()} | {:error, any()}
def get(guild_id, user_id) do
key = {guild_id, user_id}

:mnesia.activity(:sync_transaction, fn ->
case :mnesia.read(@table_name, key) do
[{_tag, _key, _guild_id, _user_id, member}] -> {:ok, member}
[] -> {:error, :member_not_found}
end
end)
end

@impl MemberCache
@doc "Add the given member to the given guild in the cache."
@spec create(Guild.id(), map()) :: Member.t()
Expand Down
3 changes: 3 additions & 0 deletions lib/nostrum/cache/member_cache/noop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ defmodule Nostrum.Cache.MemberCache.NoOp do
Supervisor.init([], strategy: :one_for_one)
end

@impl MemberCache
def get(_guild_id, _user_id), do: {:error, :member_not_found}

@impl MemberCache
def create(_guild_id, payload), do: Util.cast(payload, {:struct, Member})

Expand Down
18 changes: 5 additions & 13 deletions lib/nostrum/cache/presence_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,10 @@ defmodule Nostrum.Cache.PresenceCache do
end
```
"""
@spec get(Guild.id(), User.id()) :: {:ok, presence()} | {:error, :presence_not_found}
@spec get(Guild.id(), User.id(), module()) :: {:ok, presence()} | {:error, :presence_not_found}
def get(guild_id, user_id, cache \\ @configured_cache) do
handle = :nostrum_presence_cache_qlc.get(guild_id, user_id, cache)

wrap_qlc(cache, fn ->
case :qlc.eval(handle) do
[presence] -> {:ok, presence}
[] -> {:error, :not_found}
end
end)
end
@spec get(Guild.id(), User.id()) :: {:ok, presence()} | {:error, any()}
defdelegate get(guild_id, user_id), to: @configured_cache

@callback get(Guild.id(), User.id()) :: {:ok, presence()} | {:error, any()}

@doc """
Create a presence in the cache.
Expand Down Expand Up @@ -154,7 +146,7 @@ defmodule Nostrum.Cache.PresenceCache do
def get!(guild_id, user_id, cache \\ @configured_cache)
when is_snowflake(user_id) and is_snowflake(guild_id) do
guild_id
|> get(user_id, cache)
|> get(user_id)
|> Util.bangify_find({guild_id, user_id}, cache)
end

Expand Down
12 changes: 12 additions & 0 deletions lib/nostrum/cache/presence_cache/ets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ defmodule Nostrum.Cache.PresenceCache.ETS do
@table_name :nostrum_presences

alias Nostrum.Struct.Guild
alias Nostrum.Struct.User

use Supervisor

@doc "Start the supervisor."
Expand All @@ -34,6 +36,16 @@ defmodule Nostrum.Cache.PresenceCache.ETS do
Supervisor.init([], strategy: :one_for_one)
end

@impl PresenceCache
@doc "Retrieve a presence from the cache."
@spec get(Guild.id(), User.id()) :: {:ok, PresenceCache.presence()} | {:error, any}
def get(guild_id, user_id) do
case :ets.lookup(@table_name, {guild_id, user_id}) do
[{_, presence}] -> {:ok, presence}
[] -> {:error, :presence_not_found}
end
end

@impl PresenceCache
@doc "Add the given presence data to the cache."
@spec create(map) :: :ok
Expand Down
15 changes: 15 additions & 0 deletions lib/nostrum/cache/presence_cache/mnesia.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ if Code.ensure_loaded?(:mnesia) do
@behaviour Nostrum.Cache.PresenceCache

alias Nostrum.Cache.PresenceCache

alias Nostrum.Struct.Guild
alias Nostrum.Struct.User

use Supervisor

@doc "Retrieve the table name used by the cache."
Expand Down Expand Up @@ -46,6 +49,18 @@ if Code.ensure_loaded?(:mnesia) do
Supervisor.init([], strategy: :one_for_one)
end

@impl PresenceCache
@doc "Retrieve a presence from the cache."
@spec get(Guild.id(), User.id()) :: {:ok, PresenceCache.presence()} | {:error, any}
def get(guild_id, user_id) do
:mnesia.activity(:sync_transaction, fn ->
case :mnesia.read(@table_name, {guild_id, user_id}) do
[{_tag, _key, presence}] -> {:ok, presence}
[] -> {:error, :presence_not_found}
end
end)
end

@impl PresenceCache
@doc "Add the given presence to the cache."
@spec create(map()) :: :ok
Expand Down
7 changes: 7 additions & 0 deletions lib/nostrum/cache/presence_cache/noop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ defmodule Nostrum.Cache.PresenceCache.NoOp do
@behaviour Nostrum.Cache.PresenceCache

alias Nostrum.Cache.PresenceCache

alias Nostrum.Struct.Guild
alias Nostrum.Struct.User

use Supervisor

@doc "Start the supervisor."
Expand All @@ -22,6 +25,10 @@ defmodule Nostrum.Cache.PresenceCache.NoOp do
Supervisor.init([], strategy: :one_for_one)
end

@impl PresenceCache
@spec get(Guild.id(), User.id()) :: {:error, :presence_not_found}
def get(_guild_id, _user_id), do: {:error, :presence_not_found}

@impl PresenceCache
@spec create(map) :: :ok
def create(_presence), do: :ok
Expand Down
51 changes: 20 additions & 31 deletions lib/nostrum/cache/user_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,9 @@ defmodule Nostrum.Cache.UserCache do
## Behaviour specification

@doc ~s"""
Retrieves a user from the cache by id.
This function can be called with the cache to use as an optional argument. By
default, the cache configured at compile time is used.
## Example
```elixir
case Nostrum.Cache.UserCache.get(1111222233334444) do
{:ok, user} ->
"We found " <> user.username
{:error, _reason} ->
"No es bueno"
end
```
Retrieve a user from the cache by id.
"""
@spec get(User.id()) :: {:ok, User.t()} | {:error, atom}
@spec get(User.id(), module()) :: {:ok, User.t()} | {:error, atom}
def get(user_id, cache \\ @configured_cache) do
handle = :nostrum_user_cache_qlc.get(user_id, cache)

wrap_qlc(cache, fn ->
case :qlc.eval(handle) do
[{_user_id, user}] ->
{:ok, user}

[] ->
{:error, :not_found}
end
end)
end
@callback get(User.id()) :: {:ok, User.t()} | {:error, atom()}

@doc ~S"""
Add a new user to the cache based on the Discord Gateway payload.
Expand Down Expand Up @@ -121,11 +93,28 @@ defmodule Nostrum.Cache.UserCache do
"""
@callback child_spec(term()) :: Supervisor.child_spec()

@doc """
Retrieve a user from the cache by ID.
## Example
```elixir
case Nostrum.Cache.UserCache.get(1111222233334444) do
{:ok, user} ->
"We found " <> user.username
{:error, _reason} ->
"No es bueno"
end
```
"""
@spec get(User.id()) :: {:ok, User.t()} | {:error, atom()}
defdelegate get(id), to: @configured_cache

@doc """
Same as `get/1`, but raises `Nostrum.Error.CacheError` in case of a failure.
"""
@spec get!(User.id()) :: no_return | User.t()
def get!(id) when is_snowflake(id), do: id |> get |> Util.bangify_find(id, __MODULE__)
def get!(id) when is_snowflake(id), do: id |> get() |> Util.bangify_find(id, __MODULE__)

@doc "Call `c:query_handle/0` on the configured cache."
@doc since: "0.8.0"
Expand Down
5 changes: 5 additions & 0 deletions lib/nostrum/cache/user_cache/ets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ defmodule Nostrum.Cache.UserCache.ETS do
@spec table :: :ets.table()
def table, do: @table_name

@doc "Retrieve a user from the cache."
@impl Nostrum.Cache.UserCache
@spec get(User.id()) :: {:ok, User.t()} | {:error, :user_not_found}
def get(id), do: lookup(id)

@doc "Bulk create a list of users from upstream data."
@impl Nostrum.Cache.UserCache
@spec bulk_create(Enum.t()) :: :ok
Expand Down
15 changes: 15 additions & 0 deletions lib/nostrum/cache/user_cache/mnesia.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ if Code.ensure_loaded?(:mnesia) do
:ok
end

@doc "Retrieve a user from the cache."
@impl UserCache
@spec get(User.id()) :: {:ok, User.t()} | {:error, :user_not_found}
def get(user_id) do
:mnesia.activity(:sync_transaction, fn ->
case :mnesia.read(@table_name, user_id) do
[{_tag, _id, user}] ->
{:ok, user}

_ ->
{:error, :user_not_found}
end
end)
end

# Used by dispatch

@impl UserCache
Expand Down
3 changes: 3 additions & 0 deletions lib/nostrum/cache/user_cache/noop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ defmodule Nostrum.Cache.UserCache.NoOp do
@impl Nostrum.Cache.UserCache
def bulk_create(_users), do: :ok

@impl Nostrum.Cache.UserCache
def get(_id), do: {:error, :user_not_found}

@impl Nostrum.Cache.UserCache
def create(payload), do: User.to_struct(payload)

Expand Down
Loading

0 comments on commit 034f68c

Please sign in to comment.