GameServer.Cache (game_server_sdk v1.0.951)

Copy Markdown View Source

In-memory cache backed by Nebulex (2-level near-cache topology).

Plugins can use this to cache expensive computations (e.g. API calls, PDF generation, data transformations) so that repeated hook calls return instantly from cache.

Quick start — cached/3

The simplest way to cache a hook function. Checks the cache first; on miss, runs the function, stores the result, and returns it:

defmodule GameServer.Modules.MyPlugin do
  use GameServer.Hooks

  def generate_cheatsheet(lang) do
    GameServer.Cache.cached({:my_plugin, :cheatsheet, lang}, ttl: :timer.minutes(30), fn ->
      do_expensive_pdf_generation(lang)
    end)
  end
end

Equivalent to:

case GameServer.Cache.get(key) do
  nil ->
    result = fun.()
    GameServer.Cache.put(key, result, opts)
    result

  cached ->
    cached
end

Manual get/put

For more control (conditional caching, partial updates, etc.):

def my_hook(lang) do
  cache_key = {:my_plugin, :data, lang}

  case GameServer.Cache.get(cache_key) do
    nil ->
      result = expensive_work(lang)
      GameServer.Cache.put(cache_key, result, ttl: :timer.minutes(10))
      {:ok, result}

    cached ->
      {:ok, cached}
  end
end

Cache keys

Use namespaced tuples to avoid collisions with other plugins and the server:

{:my_plugin, :resource_type, resource_id}

Options

  • :ttl — time-to-live in milliseconds. After this time the entry is automatically evicted. If omitted, the entry lives until explicitly deleted or the cache is full (LRU eviction).

Version-based invalidation

For data that changes (e.g. leaderboard entries, user profiles), embed a version counter in the cache key. When data changes, increment the version so the next read produces a cache miss:

defp my_version(entity_id) do
  GameServer.Cache.get({:my_plugin, :version, entity_id}) || 1
end

defp invalidate(entity_id) do
  GameServer.Cache.incr({:my_plugin, :version, entity_id}, 1, default: 1)
end

def get_data(entity_id) do
  vsn = my_version(entity_id)
  cache_key = {:my_plugin, :data, vsn, entity_id}

  case GameServer.Cache.get(cache_key) do
    nil ->
      data = fetch_from_db(entity_id)
      GameServer.Cache.put(cache_key, data, ttl: :timer.minutes(5))
      {:ok, data}

    cached ->
      {:ok, cached}
  end
end

Note: This is an SDK stub. The actual implementation uses Nebulex on the GameServer.

Summary

Functions

Cache-through helper: returns the cached value for key, or computes and caches the result of fun.

Delete a cache entry by key.

Get a cached value by key. Returns nil on cache miss.

Check whether a key exists in the cache.

Atomically increment a numeric counter.

Store a value in the cache.

Functions

cached(key, opts \\ [], fun)

@spec cached(term(), keyword(), (-> term())) :: term()

Cache-through helper: returns the cached value for key, or computes and caches the result of fun.

This is the recommended way to add caching to hook functions. It combines get + put in a single call:

GameServer.Cache.cached({:my_plugin, :data, id}, ttl: :timer.minutes(10), fn ->
  expensive_computation(id)
end)

Options

  • :ttl — time-to-live in milliseconds

delete(key)

@spec delete(term()) :: :ok

Delete a cache entry by key.

get(key)

@spec get(term()) :: term() | nil

Get a cached value by key. Returns nil on cache miss.

has_key?(key)

@spec has_key?(term()) :: boolean()

Check whether a key exists in the cache.

incr(key, amount \\ 1, opts \\ [])

@spec incr(term(), integer(), keyword()) :: integer()

Atomically increment a numeric counter.

Returns the new value. Creates the counter with the :default value if it doesn't exist.

Options

  • :default — initial value if key doesn't exist (default: 0)

put(key, value, opts \\ [])

@spec put(term(), term(), keyword()) :: term()

Store a value in the cache.

Options

  • :ttl — time-to-live in milliseconds (e.g. ttl: :timer.minutes(10))