View Source Mentat (Mentat v0.7.3)

Elixir CI Module Version Hex Docs Total Download License Last Updated

Provides a super simple cache with ttls.

Usage

A cache must be given a name when its started.

Mentat.start_link(name: :my_cache)

After its been started you can store and retrieve values:

Mentat.put(:my_cache, user_id, user)
user = Mentat.get(:my_cache, user_id)

TTLs

Both put and fetch operations allow you to specify the key's TTL. If no TTL is provided then the TTL is set to :infinity. TTL times are always in milliseconds.

Mentat.put(:my_cache, :key, "value", [ttl: 5_000])

Mentat.fetch(:my_cache, :key, [ttl: 5_000], fn key ->
  {:commit, "value"}
end)

Default TTLs

You can set a global TTL for all new keys

Limits

Mentat supports optional limits per cache.

Mentat.start_link(name: LimitedCache, limit: [size: 100])

When the limit is reached, the janitor will asynchronously reclaim a percentage of the keys.

Telemetry

Mentat publishes multiple telemetry events.

  • [:mentat, :get] - executed after retrieving a value from the cache. Measurements are:

    • :status - Can be either :hit or :miss depending on if the key was found in the cache.

    Metadata are:

    • :key - The key requested
    • :cache - The cache name
  • [:mentat, :put] - executed when putting a key into the cache. No measurements are provided.

    Metadata are:

    • :key - The key requested
    • :cache - The name of the cache
  • [:mentat, :janitor, :cleanup] - executed after old keys are cleaned from the cache. Measurements are:

    • :duration - the time it took to clean up the old keys. Time is in :native units.
    • total_removed_keys - The count of keys removed from the cache.

    Metadata are:

    • cache - The cache name.

Contracts

Mentat supports Oath contracts. This helps ensure that you're using Mentat correctly and that Mentat is returning what you expect. You can enable contracts by setting

config :oath,
  enable_contracts: true

And then recompiling Mentat in dev and test environments:

MIX_ENV=dev mix deps.compile mentat --force
MIX_ENV=test mix deps.compile mentat --force

Installation

def deps do
  [
    {:mentat, "~> 0.7"}
  ]
end

Should I use this?

There are (many) other caching libraries out there that provide many more features than Mentat. But, it turns out, I don't need most of those features. Mentat is intended to be very small while still providing the necessary components. The test suite is sparse, but we've been using this implementation in production for a while now so I feel pretty confident in it.

Summary

Functions

Deletes a key from the cache

Fetches a value or executes the fallback function. The function can return either {:commit, term()} or {:ignore, term()}. If {:commit, term()} is returned, the value will be stored in the cache before its returned. See the "TTLs" section for a list of options.

Retrieves a value from a the cache. Returns nil if the key is not found.

Callback implementation for Supervisor.init/1.

Returns a list of all keys. By default this function only returns keys that have no exceeded their TTL. You can pass the all: true option to the function in order to return all present keys, which may include keys that have exceeded their TTL but have not been purged yet.

Removes all keys from the cache.

Puts a new key into the cache. See the "TTLs" section for a list of options.

Starts a new cache.

Updates a keys inserted at time. This is useful in conjunction with limits when you want to evict the oldest keys. Returns true if the key was found and false if it was not.

Types

@type cache_opts() :: Keyword.t()
@type key() :: term()
@type name() :: atom()
@type put_opts() :: [{:ttl, pos_integer() | :infinity}]
@type value() :: term()

Functions

@spec delete(name(), key()) :: true

Deletes a key from the cache

Link to this function

fetch(cache, key, opts \\ [], fallback)

View Source
@spec fetch(
  name(),
  key(),
  put_opts(),
  (key() -> {:commit, value()} | {:ignore, value()})
) :: value()

Fetches a value or executes the fallback function. The function can return either {:commit, term()} or {:ignore, term()}. If {:commit, term()} is returned, the value will be stored in the cache before its returned. See the "TTLs" section for a list of options.

Since fetch is logically the same as a get (and also a put if {:commit, term()} is returned from the fallback function), it shares the same Telemetry events ([:mentat, :get] and [:mentat, :put]).

Example

Mentat.fetch(:cache, user_id, fn user_id ->
  case get_user(user_id) do
    {:ok, user} ->
      {:commit, user}

    error ->
      {:ignore, error}
  end
end)
@spec get(name(), key()) :: value()

Retrieves a value from a the cache. Returns nil if the key is not found.

Callback implementation for Supervisor.init/1.

Returns a list of all keys. By default this function only returns keys that have no exceeded their TTL. You can pass the all: true option to the function in order to return all present keys, which may include keys that have exceeded their TTL but have not been purged yet.

@spec purge(name()) :: true

Removes all keys from the cache.

Link to this function

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

View Source
@spec put(name(), key(), value(), put_opts()) :: value() | no_return()

Puts a new key into the cache. See the "TTLs" section for a list of options.

@spec start_link(cache_opts()) :: Supervisor.on_start()

Starts a new cache.

Options:

  • :name - the cache name as an atom. required.
  • :cleanup_interval - How often the janitor process will remove old keys. Defaults to 5_000.
  • :ets_args - Additional arguments to pass to :ets.new/2.
  • :ttl - The default ttl for all keys. Default :infinity.
  • :limit - Limits to the number of keys a cache will store. Defaults to :none.
    • :size - The maximum number of values to store in the cache.
    • :reclaim - The percentage of keys to reclaim if the limit is exceeded. Defaults to 0.1.
@spec touch(name(), key()) :: boolean()

Updates a keys inserted at time. This is useful in conjunction with limits when you want to evict the oldest keys. Returns true if the key was found and false if it was not.