SlackBot.Cache.Adapter behaviour (slack_bot_ws v0.1.0-rc.2)
View SourceBehaviour for pluggable cache backends.
SlackBot uses a cache to store Slack workspace metadata (users, channels) and telemetry snapshots. The default ETS adapter works well for single-node deployments. Implement this behaviour when you need:
- Multi-node coordination - Share cache across multiple BEAM nodes
- External requirements - Integrate with existing Redis, Postgres, etc.
- Custom eviction - Implement domain-specific TTL or LRU strategies
- Persistence - Survive node restarts without re-fetching from Slack
When to Use Custom Adapters
Stick with ETS (default) when:
- Running a single bot process
- Cache data can be rebuilt quickly from Slack APIs
- You want zero external dependencies
Consider a custom adapter when:
- Running multiple bot instances that should share cache
- You need cache persistence across restarts
- Your infrastructure already provides Redis, Memcached, etc.
- You want centralized observability of cache contents
Implementation Requirements
Your adapter must:
- Return child specs from
child_specs/2for any processes it needs - Handle all cache operations defined in the callbacks
- Be safe for concurrent access from multiple processes
- Respect the configuration passed in opts
Example: Redis Adapter
defmodule MyApp.RedisCache do
@behaviour SlackBot.Cache.Adapter
def child_specs(config, opts) do
redis_opts = Keyword.get(opts, :redis, [])
name = redis_name(config)
[
{Redix, [name: name] ++ redis_opts}
]
end
def channels(config, opts) do
config
|> redis_name()
|> Redix.command!(["SMEMBERS", "slackbot:channels"])
end
def users(config, opts) do
# Implementation: fetch user map from Redis hash
end
# ... implement remaining callbacks
endThen configure:
config :my_app, MyApp.SlackBot,
cache: {:adapter, MyApp.RedisCache, redis: [host: "localhost"]}See Also
SlackBot.Cache- Public cache API for querying cached dataSlackBot.EventBuffer.Adapter- Similar behaviour for event dedupe- Source on GitHub - Reference implementations
Summary
Callbacks
@callback channels( SlackBot.Config.t(), keyword() ) :: [String.t()]
@callback child_specs( SlackBot.Config.t(), keyword() ) :: [Supervisor.child_spec()]
@callback metadata( SlackBot.Config.t(), keyword() ) :: map()
@callback mutate(SlackBot.Config.t(), keyword(), SlackBot.Cache.cache_op()) :: :ok
@callback user_entry(SlackBot.Config.t(), keyword(), String.t()) :: {:ok, %{data: map(), expires_at: integer()}} | :not_found
@callback users( SlackBot.Config.t(), keyword() ) :: map()