Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize fetching individual records on each cache type #622

Merged
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
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