### Starting a Cache Source: https://context7.com/whitfin/cachex/llms.txt Demonstrates how to start a Cachex instance, either manually or supervised, with various configuration options for features like expiration, hooks, and distribution. ```APIDOC ## Starting a Cache `Cachex.start_link/2` starts a supervised cache tree linked to the calling process. `Cachex.start/2` does the same but without linking. Both accept a unique atom name and a keyword list of options. All features (expiration, hooks, warmers, routers, commands, compression, ordering, transactions) are configured here. ```elixir # Add to mix.exs defp deps do [{:cachex, "~> 4.0"}] end # --- Option 1: start manually in iex or tests --- {:ok, _pid} = Cachex.start_link(:my_cache) # --- Option 2: supervised in Application.start/2 --- children = [ {Cachex, [:my_cache]}, # default options {Cachex, [:my_cache, [compressed: true]]} # with custom options ] Supervisor.start_link(children, strategy: :one_for_one) # --- Option 3: full configuration --- import Cachex.Spec {:ok, _pid} = Cachex.start_link(:my_cache, [ # ETS compression (reduces memory, slight CPU cost) compressed: true, # ETS ordering (log(n) inserts/lookups but ordered traversal) ordered: false, # Global expiration: cleanup every 30s, default TTL 60s, lazy checking on expiration: expiration( interval: :timer.seconds(30), default: :timer.seconds(60), lazy: true ), # Stats collection hook hooks: [ hook(module: Cachex.Stats) ], # Cache warmers warmers: [ warmer( module: MyApp.DatabaseWarmer, state: MyApp.Repo, interval: :timer.minutes(5), required: true, # block startup until first warm completes name: MyApp.DatabaseWarmer ) ], # Distributed routing router: router( module: Cachex.Router.Ring, options: [monitor: true] # auto-add/remove nodes on nodeup/nodedown ), # Custom domain commands commands: [ last: command(type: :read, execute: &List.last/1), lpop: command(type: :write, execute: fn ([h | t]) -> {h, t} ([]) -> {nil, []} end) ], # Enable transactions at startup transactions: false ]) ``` ``` -------------------------------- ### Start Cachex Manually or Supervised Source: https://context7.com/whitfin/cachex/llms.txt Demonstrates starting Cachex instances either manually in `iex` or tests, or as part of an application's supervision tree. ```elixir {:ok, _pid} = Cachex.start_link(:my_cache) ``` ```elixir children = [ {Cachex, [:my_cache]}, {Cachex, [:my_cache, [compressed: true]]} ] Supervisor.start_link(children, strategy: :one_for_one) ``` -------------------------------- ### Start Cachex Manually in IEx Source: https://github.com/whitfin/cachex/blob/main/docs/overview.md Start a Cachex cache instance manually within an `iex` session using `Cachex.start_link/2`. This is useful for testing. ```elixir Cachex.start_link(:my_cache) # with default options ``` ```elixir Cachex.start_link(:my_cache, []) # with custom options ``` -------------------------------- ### Configure Cachex with Stats Hook Source: https://github.com/whitfin/cachex/blob/main/docs/management/stats-gathering.md Initialize a cache with the `Cachex.Stats` hook to enable statistics tracking. This example demonstrates starting a cache, inserting data, generating hits and misses, and then retrieving the statistics. ```elixir import Cachex.Spec # create a cache with stats Cachex.start(:my_cache, hooks: [ hook(module: Cachex.Stats) ] ) # insert 100 keys for i <- 1..100 do Cachex.put(:my_cache, i, i) end # generate both a cache hit and a miss 1 = Cachex.get(:my_cache, 1) nil = Cachex.get(:my_cache, 101) # print stats :my_cache |> Cachex.stats() |> IO.inspect() ``` -------------------------------- ### Starting Cachex with a Custom Router Source: https://github.com/whitfin/cachex/blob/main/docs/routing/cache-routers.md Demonstrates how to start a Cachex instance using a custom router module. This replaces the default routing logic with your own implementation. ```elixir # for records import Cachex.Spec # create a cache with our router Cachex.start(:my_cache, [ router: router(module: MyCustomRouter) ]) ``` -------------------------------- ### Initialize Cache with Expiration Source: https://github.com/whitfin/cachex/blob/main/docs/warming/reactive-warming.md This example demonstrates how to start a Cachex cache with a default expiration time of 5 minutes using `Cachex.start_link/2` and `Cachex.Spec.expiration/1`. This setup is useful for managing cache entries that should automatically be removed after a set period. ```elixir # need our records import Cachex.Spec # initialize our cache with expiration set Cachex.start_link(:cache, [ expiration: expiration(default: :timer.minutes(5)) ]) ``` -------------------------------- ### Full Cachex Configuration Example Source: https://context7.com/whitfin/cachex/llms.txt Illustrates a comprehensive configuration for Cachex, including compression, ordering, expiration, hooks, warmers, routing, commands, and transactions. ```elixir import Cachex.Spec {:ok, _pid} = Cachex.start_link(:my_cache, [ compressed: true, ordered: false, expiration: expiration( interval: :timer.seconds(30), default: :timer.seconds(60), lazy: true ), hooks: [ hook(module: Cachex.Stats) ], warmers: [ warmer( module: MyApp.DatabaseWarmer, state: MyApp.Repo, interval: :timer.minutes(5), required: true, name: MyApp.DatabaseWarmer ) ], router: router( module: Cachex.Router.Ring, options: [monitor: true] ), commands: [ last: command(type: :read, execute: &List.last/1), lpop: command(type: :write, execute: fn ([h | t]) -> {h, t} ([]) -> {nil, []} end) ], transactions: false ]) ``` -------------------------------- ### Cachex v2 Fallback Configuration Examples Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v2.md Demonstrates various ways to configure fallback functions in Cachex v2.x, including options with no state, a function, and with state. ```elixir [ fallback: fn(key) -> do_fallback(key) end ] ``` ```elixir [ fallback: [ action: fn(key) -> do_fallback(key) end ] ] ``` ```elixir [ fallback: [ state: db_client, action: fn(key, client) -> retrieve_from_db(client, key) end ] ] ``` ```elixir [ fallback: [ state: db_client ] ] ``` -------------------------------- ### Start Cachex in Supervision Tree Source: https://github.com/whitfin/cachex/blob/main/docs/overview.md Integrate Cachex into your application's supervision tree by adding it to the `children` list in `lib/my_app/application.ex`. This is recommended for production applications. ```elixir children = [ {Cachex, [:my_cache]}, # with default options {Cachex, [:my_cache, []]} # with custom options ] ``` -------------------------------- ### Start Cache with Ring Router Source: https://github.com/whitfin/cachex/blob/main/docs/routing/distributed-caches.md Use this to create a cache with the default ring router. Ensure `Cachex.Spec` is imported for the `router` record. ```elixir import Cachex.Spec # create a cache with a ring router Cachex.start(:my_cache, [ router: router(module: Cachex.Router.Ring) ]) ``` -------------------------------- ### Example Cache Statistics Output Source: https://github.com/whitfin/cachex/blob/main/docs/management/stats-gathering.md This is an example of the statistics map returned by `Cachex.stats/2`. Note that the output format is not part of the Public API and may change in future versions. ```elixir %{ meta: %{creation_date: 1726777631670}, hits: 1, misses: 1, hit_rate: 50.0, miss_rate: 50.0, calls: %{get: 2, put: 100}, operations: 102, writes: 100 } ``` -------------------------------- ### Start Cache with Local Router Source: https://github.com/whitfin/cachex/blob/main/docs/routing/cache-routers.md Configure a cache to use the `Cachex.Router.Local` module, which routes keys only to the local node. This is the default behavior. ```elixir import Cachex.Spec # create a cache with a router Cachex.start(:my_cache, [ router: router(module: Cachex.Router.Local) ]) ``` -------------------------------- ### Start Cache with Jump Consistent Hash Router and Options Source: https://github.com/whitfin/cachex/blob/main/docs/routing/cache-routers.md Configure a cache to use the `Cachex.Router.Jump` module, passing custom options like a list of nodes. This router is recommended for dynamically sized clusters and mimics Cachex v3.x behavior. ```elixir import Cachex.Spec # create a cache with a router Cachex.start(:my_cache, [ router: router( module: Cachex.Router.Jump, options: [ nodes: [self()] ] ) ]) ``` -------------------------------- ### Get with Custom Default Source: https://context7.com/whitfin/cachex/llms.txt Retrieves a value from the cache, returning a custom default if the key is not found. ```elixir "unknown" = Cachex.get(:cache, "missing", "unknown") ``` -------------------------------- ### Configure Cache Startup with API Data Warmer Source: https://github.com/whitfin/cachex/blob/main/docs/warming/proactive-warming.md Initialize the cache with the PackageWarmer, setting a 5-minute refresh interval and marking it as required for startup. This ensures all package data is loaded into the cache immediately upon application start. ```elixir # need our records import Cachex.Spec ``` -------------------------------- ### Manual Reactive Warming Example Source: https://github.com/whitfin/cachex/blob/main/docs/warming/reactive-warming.md Demonstrates a manual approach to lazily loading a missing value into the cache. This method can lead to multiple database calls if concurrent requests occur before the value is cached. ```elixir Cachex.start(:cache) case Cachex.get(:cache, "key") do {:ok, nil} -> value = call_database_with_network_delay("key") Cachex.put(:cache, "key", value) value value -> value end ``` -------------------------------- ### Get data from cache Source: https://github.com/whitfin/cachex/blob/main/docs/overview.md Use `Cachex.get/2` to retrieve data from a cache. If the key does not exist or has expired, it will return `nil`. ```elixir nil = Cachex.get(:my_cache, "secret_mission") ``` -------------------------------- ### Attach a Hook to a Cache Source: https://github.com/whitfin/cachex/blob/main/docs/extensions/execution-lifecycle.md Start a Cachex cache with custom hooks using the `:hooks` option in `Cachex.start_link/2`. This option takes a list of `Cachex.Spec.hook` records, specifying the module that implements the hook. ```elixir # need the records import Cachex.Spec # create a cache with our hook Cachex.start_link(:my_cache, [ hooks: [ hook(module: MyProject.MyHook) ] ]) ``` -------------------------------- ### Configure Cache Startup with a Warmer Source: https://github.com/whitfin/cachex/blob/main/docs/warming/proactive-warming.md Assign a warmer during cache startup using the :warmers option. Control the refresh frequency with the :interval option. Set :required to true to ensure the cache supervision tree is not considered started until the warmer runs successfully. ```elixir # for warmer() import Cachex.Spec # define the cache with our warmer Cachex.start_link(:cache, [ warmers: [ warmer( state: connection, module: MyProject.DatabaseWarmer, interval: :timer.seconds(30), required: true ) ] ]) ``` -------------------------------- ### Basic Cache Operations Source: https://github.com/whitfin/cachex/blob/main/docs/overview.md Perform fundamental cache operations: starting a cache, putting a key-value pair, checking for existence, retrieving a value, deleting a key, and verifying deletion. ```elixir # create a default cache in our shell session {:ok, _pid} = Cachex.start_link(:my_cache) # place a "my_value" string against the key "my_key" :ok = Cachex.put(:my_cache, "my_key", "my_value") # verify that the key exists under the key name true = Cachex.exists?(:my_cache, "my_key") # verify that "my_value" is returned when we retrieve "my_value" = Cachex.get(:my_cache, "my_key") # remove the "my_key" key from the cache :ok = Cachex.del(:my_cache, "my_key") # verify that the key no longer exists false = Cachex.exists?(:my_cache, "my_key") # verify that "my_value" is no longer returned nil = Cachex.get(:my_cache, "my_key") ``` -------------------------------- ### Start Cache with Monitored Ring Router Source: https://github.com/whitfin/cachex/blob/main/docs/routing/distributed-caches.md Configure a ring router to monitor cluster events for automatic node redistribution. This is generally recommended for dynamic clusters. ```elixir import Cachex.Spec # create a cache with a ring router Cachex.start(:my_cache, [ router: router(module: Cachex.Router.Ring, options: [ monitor: true ]) ]) ``` -------------------------------- ### Define an API Data Warmer with Cachex.Warmer Source: https://github.com/whitfin/cachex/blob/main/docs/warming/proactive-warming.md Implement Cachex.Warmer to fetch all packages from a database, map them into cache pairs using API paths as keys, and return them for caching. This example demonstrates warming both a root API path and individual package paths. ```elixir defmodule MyProject.PackageWarmer do @moduledoc """ Module to warm the packages API. """ use Cachex.Warmer @doc """ Executes this cache warmer. """ def execute(_) do # load all of the packages from the database packages = Repo.all(from p in Package) # create pairs from the API path and the package package_pairs = Enum.map(packages, fn(package) -> { "/api/v1/packages/#{package.id}", package } end) # return pairs for the root, as well as all single packages { :ok, [ { "/api/v1/packages", packages } | package_pairs ] } end end ``` -------------------------------- ### Cachex.execute/3 with Potential Modifications Source: https://github.com/whitfin/cachex/blob/main/docs/general/batching-actions.md Demonstrates that even within a `Cachex.execute/3` block, other processes can modify cache keys. This example shows a value being set, a pause, and then the value being retrieved, which might have been altered by external processes during the pause. ```elixir # start our execution block Cachex.execute(:my_cache, fn cache -> # set a base value in the cache Cachex.put(cache, "key", "value") # we're paused but other changes can happen :timer.sleep(5000) # this may have have been set elsewhere Cachex.get(cache, "key") end) ``` -------------------------------- ### Define a Cachex Hook Source: https://github.com/whitfin/cachex/blob/main/docs/extensions/execution-lifecycle.md Implement the `Cachex.Hook` behaviour to create custom hooks. Use `handle_notify/3` to process cache actions and results, and `handle_call/3` for custom query logic. The `init/1` callback is used for initial setup. ```elixir defmodule MyProject.MyHook do @moduledoc "" A small hook to log all actions and store the most recent. "" use Cachex.Hook # Initialization. def init(_), do: { :ok, nil } # Log the action and result, then store the action. def handle_notify(action, result, _last) do IO.puts("Action: #{action}") IO.puts("Result: #{result}") { :ok, action } end # Provide access to the last executed cache action. def handle_call(:last_action, _ctx, last), do: { :reply, last, last } end ``` -------------------------------- ### Distributed Routing with Cachex.Router Source: https://context7.com/whitfin/cachex/llms.txt Configure how keys are routed across cluster nodes using different router strategies. Local is the default for single-node setups. Jump Consistent Hash is suitable for static clusters, while Ring is recommended for dynamic clusters with auto node add/remove capabilities. ```elixir import Cachex.Spec # ---- Local only (default, single node) ---- {:ok, _} = Cachex.start_link(:cache, [ router: router(module: Cachex.Router.Local) ]) # ---- Jump Consistent Hash (static cluster, Cachex v3 compatible) ---- {:ok, _} = Cachex.start_link(:cache, [ router: router( module: Cachex.Router.Jump, options: [nodes: [:node1@host, :node2@host, :node3@host]] ) ]) # ---- Ring router (dynamic cluster, auto node add/remove) ---- {:ok, _} = Cachex.start_link(:cache, [ router: router( module: Cachex.Router.Ring, options: [monitor: true] # listens for :nodeup / :nodedown events ) ]) # In distributed mode, all actions are cluster-wide by default. # Use :local to restrict to the local node. local_size = Cachex.size(:cache, local: true) # Cross-slot operations are disallowed across nodes: # Cachex.put_many(:cache, [{"k1",1},{"k2",2}]) => {:error, :cross_slot} # Use single-key calls or ensure keys hash to the same node. ``` -------------------------------- ### Define and Attach Custom Commands Source: https://github.com/whitfin/cachex/blob/main/docs/extensions/custom-commands.md Define custom :read and :write commands and attach them to a cache at startup using `Cachex.start_link/2`. ```elixir import Cachex.Spec last = &List.last/1 lpop = fn ([ head | tail ]) -> { head, tail } ([ ] = list) -> { nil, list } end Cachex.start_link(:my_cache, [ commands: [ last: command(type: :read, execute: last), lpop: command(type: :write, execute: lpop) ] ]) ``` -------------------------------- ### Cachex.stats/1 - Get Cache Statistics Source: https://context7.com/whitfin/cachex/llms.txt Retrieves statistics for a given cache. Returns an error if the stats hook is absent. ```elixir Cachex.stats(:cache) ``` -------------------------------- ### Get! Bang Variant Source: https://context7.com/whitfin/cachex/llms.txt Retrieves a value from the cache, raising an error if the key is not found. This variant unwraps the result. ```elixir "elixir" = Cachex.get!(:cache, "lang") ``` -------------------------------- ### Demonstrate Cache Contention Handling Source: https://github.com/whitfin/cachex/blob/main/docs/warming/reactive-warming.md Compares manual warming with Cachex.fetch/4 under concurrent access. The manual approach results in multiple warmers executing, while Cachex.fetch/4 ensures only one warmer runs, queuing subsequent requests. ```elixir Cachex.start(:cache) # run manually for _ <- 1..10 do spawn(fn -> case Cachex.get(:cache, "key1") do {:ok, nil} -> IO.puts("Running warmer after get/2") value = :timer.sleep(1000) Cachex.put(:cache, "key1", value) value value -> value end end) end # run via fetch/4 for _ <- 1..10 do spawn(fn -> Cachex.fetch(:cache, "key2", fn key -> IO.puts("Running warmer in fetch/4") value = :timer.sleep(1000) value end) end) end ``` -------------------------------- ### Cachex.size/2 Source: https://context7.com/whitfin/cachex/llms.txt Get the total number of entries in the cache. By default, it includes expired-but-not-yet-purged entries. Pass `expired: false` to exclude them. ```APIDOC ## `Cachex.size/2` — Cache Entry Count Returns the total number of entries in the cache. By default includes expired-but-not-yet-purged entries (O(1)). Pass `expired: false` to exclude them (involves an ETS scan). ```elixir {:ok, _} = Cachex.start_link(:cache) :ok = Cachex.put(:cache, "a", 1) :ok = Cachex.put(:cache, "b", 2) :ok = Cachex.put(:cache, "c", 3, expire: 1) :timer.sleep(2) 3 = Cachex.size(:cache) # includes the expired (not yet purged) "c" 2 = Cachex.size(:cache, expired: false) # only live entries ``` ``` -------------------------------- ### Run Cachex Benchmarks Source: https://github.com/whitfin/cachex/blob/main/README.md Execute Cachex benchmarks using mix bench. Environment variables can enable compressed or transactional tests. ```bash mix bench ``` ```bash CACHEX_BENCH_COMPRESS=true mix bench ``` ```bash CACHEX_BENCH_TRANSACTIONS=true mix bench ``` -------------------------------- ### Initialize Cache with Warmers Source: https://github.com/whitfin/cachex/blob/main/docs/warming/proactive-warming.md Configure a cache with a specific warmer module, interval, and requirement during startup. ```elixir Cachex.start_link(:cache, [ warmers: [ warmer( module: MyProject.PackageWarmer, interval: :timer.minutes(5), required: true ) ] ]) ``` -------------------------------- ### Load Cache from Disk with Cachex Source: https://github.com/whitfin/cachex/blob/main/docs/general/local-persistence.md Use `Cachex.restore/3` to merge data from a file into the cache. Existing keys will be overwritten. Expired records are not loaded. Use `Cachex.clear/2` first if an exact cache match is desired. ```elixir # optionally clean your cache first amount = Cachex.clear(:my_cache) # then you can load the existing save into your cache ^amount = Cachex.restore(:my_cache, "/tmp/my_cache.dat") ``` -------------------------------- ### Run Cachex Tests and Checks Source: https://github.com/whitfin/cachex/blob/main/README.md Execute unit tests, credo linting, and coveralls for code coverage analysis. ```bash mix test # --exclude=distributed to skip slower tests ``` ```bash mix credo ``` ```bash mix coveralls ``` ```bash mix coveralls.html && open cover/excoveralls.html ``` -------------------------------- ### Get Cache Entry Count with Cachex.size/2 Source: https://context7.com/whitfin/cachex/llms.txt Returns the total number of entries in the cache. By default, it includes expired-but-not-yet-purged entries (O(1)). Pass `expired: false` to exclude them, which involves an ETS scan. ```elixir 3 = Cachex.size(:cache) # includes the expired (not yet purged) "c" ``` ```elixir 2 = Cachex.size(:cache, expired: false) # only live entries ``` -------------------------------- ### Save Cache to Disk with Cachex Source: https://github.com/whitfin/cachex/blob/main/docs/general/local-persistence.md Use `Cachex.save/3` to write the cache to a file. Compression is handled automatically. The internal file format should not be relied upon. Options can be passed as a keyword list. ```elixir :ok = Cachex.save(:my_cache, "/tmp/my_cache.dat") ``` -------------------------------- ### Demonstrate Cachex.fetch/4 Return Types Source: https://github.com/whitfin/cachex/blob/main/docs/warming/reactive-warming.md This snippet shows the different return types from `Cachex.fetch/4`, including `:commit` for new data, `:ok` for existing data, and `:ignore` for data not to be cached. It also demonstrates defining warming functions using shorthand and inline syntax, with and without expiration options. ```elixir # start an empty cache Cachex.start(:cache) # defining a function alias using shorthand syntax { :commit, 4 } = Cachex.fetch(:cache, "key1", &String.length/1) { :ok, 4 } = Cachex.fetch(:cache, "key1", &String.length/1) # defining an inline function using `:commit` syntax { :commit, 4 } = Cachex.fetch(:cache, "key2", fn key -> { :commit, String.length(key) } end) # defining an inline function using `:commit` syntax, with options { :commit, 4 } = Cachex.fetch(:cache, "key3", fn key -> { :commit, String.length(key), expire: :timer.seconds(60) } end) # define a function which doesn't save the result (i.e. in case of error) { :ignore, 4 } = Cachex.fetch(:cache, "key4", fn key -> { :ignore, String.length(key) } end) ``` -------------------------------- ### Invoke Custom Command - Get Last Element Source: https://github.com/whitfin/cachex/blob/main/docs/extensions/custom-commands.md Use Cachex.invoke/3 to call a custom command that retrieves the last element from a list stored under a specific key. The fourth parameter for options is reserved for future use. ```elixir nil = Cachex.invoke(:my_cache, :last, "my_list") ``` -------------------------------- ### Cachex.execute/3 vs Individual Calls Source: https://github.com/whitfin/cachex/blob/main/docs/general/batching-actions.md Compares the standard method of executing multiple cache actions individually with the optimized approach using `Cachex.execute/3`. The batched approach offers significant performance gains. ```elixir # standard way to execute several actions r1 = Cachex.get(:my_cache, "key1") r2 = Cachex.get(:my_cache, "key2") r3 = Cachex.get(:my_cache, "key3") # using Cachex.execute/3 to optimize the batch of calls {r1, r2, r3} = Cachex.execute(:my_cache, fn cache -> # execute our batch of actions r1 = Cachex.get(cache, "key1") r2 = Cachex.get(cache, "key2") r3 = Cachex.get(cache, "key3") # pass back all results as a tuple {r1, r2, r3} end) ``` -------------------------------- ### Cache Options: Stats and Limit Migration Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v4.md Migrate from v3's :stats and :limit options to v4's explicit :hooks configuration. Use Cachex.Stats for stats and Cachex.Limit.Scheduled for limits, providing necessary arguments. ```elixir Cachex.start_link(:my_cache, [ stats: true, limit: limit( size: 500, policy: Cachex.Policy.LRW, reclaim: 0.5, options: [] ) ]) ``` ```elixir Cachex.start_link(:my_cache, [ hooks: [ hook(module: Cachex.Stats), hook(module: Cachex.Limit.Scheduled, args: { 500, # setting cache max size [], # options for `Cachex.prune/3` [] # options for `Cachex.Limit.Scheduled` }) ] ]) ``` -------------------------------- ### Cache Size Retrieval in Cachex v4 Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v4.md Use `Cachex.size/2` to get the total cache entry count. To include unpurged expired records, pass `expired: true`. To ignore expired but unremoved entries, pass `expired: false`. ```elixir Cachex.size(:my_cache) Cachex.size(:my_cache, expired: true) # ignores expired but unremoved entries Cachex.size(:my_cache, expired: false) ``` -------------------------------- ### GenServer handle_info/2 Callback Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v2.md Update `handle_info/2` to return `{ :noreply, new_state }` instead of `{ :ok, new_state }` to align with GenServer callback conventions. ```elixir def handle_info(message, state) do # ... {:noreply, new_state} end ``` -------------------------------- ### Fetch Data with Reactive Warming Source: https://github.com/whitfin/cachex/blob/main/docs/warming/reactive-warming.md Use Cachex.fetch/4 to retrieve data and cache it. The provided function is executed at most once within the specified interval, reducing pressure on backing systems. ```elixir Cachex.fetch(:cache, "/api/v1/packages", fn -> { :commit, Repo.all(from p in Package) } end) ``` -------------------------------- ### Cachex v2.x vs v3.x Fallback Behavior Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v3.md Compares the v2.x approach of using the `:fallback` option with `get/3` to the v3.x `fetch/4` function for fallback caching. The `fetch/4` function is dedicated to lazy evaluation and is more explicit. ```elixir # v2.x using the :fallback option to `get/3`. Cachex.get(:my_cache, "key", fallback: &String.reverse/1) ``` ```elixir # v3.x using the `fetch/4` signature. Cachex.fetch(:my_cache, "key", &String.reverse/1) ``` -------------------------------- ### Write Command with {:commit, tuple} Source: https://github.com/whitfin/cachex/blob/main/docs/extensions/custom-commands.md Implement a write command that returns a {:commit, {return_value, new_cache_value}} tuple for uniform handling. ```elixir lpop = fn ([ head | tail ]) -> {:commit, {head, tail}} (_) -> {:ignore, nil} end ``` -------------------------------- ### Define a Database Warmer with Cachex.Warmer Source: https://github.com/whitfin/cachex/blob/main/docs/warming/proactive-warming.md Implement the Cachex.Warmer behaviour to fetch data from a database and map it into cache pairs using the 'id' field as the key. The 'execute/1' function should return {:ok, pairs} or {:ok, pairs, options}. ```elixir defmodule MyProject.DatabaseWarmer do @moduledoc """ Dummy warmer which caches database rows. """ use Cachex.Warmer @doc """ Executes this cache warmer with a connection. """ def execute(connection) do connection |> Database.query |> handle_results end # ignores the warmer result in case of error defp handle_results({ :error, _reason }), do: :ignore # maps the results into pairs to store defp handle_results({ :ok, rows }) do { :ok, Enum.map(rows, fn(row) -> { row.id, row } end) } end end ``` -------------------------------- ### List All Keys with Cachex.keys/2 Source: https://context7.com/whitfin/cachex/llms.txt Returns an unordered list of all non-expired keys in the cache. Useful for iterating over available keys or for debugging. ```elixir keys = Cachex.keys(:cache) true = Enum.sort(keys) == ["x", "y", "z"] ``` ```elixir [] = Cachex.keys(:cache) ``` -------------------------------- ### Cachex.export/2 and Cachex.import/3 Source: https://context7.com/whitfin/cachex/llms.txt Exports all entries as a list of raw `entry` tuples, and imports them back. Useful for cache-to-cache migration or debugging. ```APIDOC ## `Cachex.export/2` and `Cachex.import/3` — Raw Entry Transfer Exports all entries as a list of raw `entry` tuples, and imports them back. Useful for cache-to-cache migration or debugging. ```elixir {:ok, _} = Cachex.start_link(:cache) {:ok, _} = Cachex.start_link(:cache2) Cachex.put(:cache, "x", 42) entries = Cachex.export(:cache) IO.inspect(entries) # => [{:entry, "x", 1726777631670, nil, 42}] 1 = Cachex.import(:cache2, entries) 42 = Cachex.get(:cache2, "x") ``` ``` -------------------------------- ### Cachex.Warmer - Proactive Cache Warming Source: https://context7.com/whitfin/cachex/llms.txt A behaviour for periodically populating a cache from a backing store. Warmers run on an interval and can be flagged as `:required` to block cache startup until the first warming completes. ```elixir defmodule MyApp.PackageWarmer do use Cachex.Warmer # `state` is whatever you pass as `state:` in the warmer() record def execute(_state) do case MyApp.Repo.all(MyApp.Package) do [] -> :ignore # nothing to cache right now packages -> pairs = Enum.map(packages, fn p -> {"/api/v1/packages/#{p.id}", p} end) # return with a shared TTL {:ok, [{"/api/v1/packages", packages} | pairs], [expire: :timer.minutes(10)]} end end end # Register the warmer import Cachex.Spec {:ok, _} = Cachex.start_link(:cache, [ warmers: [ warmer( module: MyApp.PackageWarmer, interval: :timer.minutes(5), required: true # block until first warm finishes ) ] ]) # Trigger manual re-warm (e.g., after a DB migration) Cachex.warm(:cache, wait: true) # Re-warm only a subset of warmers Cachex.warm(:cache, only: [MyApp.PackageWarmer], wait: false) ``` -------------------------------- ### Cachex.invoke/5 - Custom Commands Source: https://context7.com/whitfin/cachex/llms.txt Invokes a named custom command registered at cache startup. `:read` commands transform and return the cached value; `:write` commands transform the value and write it back. ```elixir import Cachex.Spec {:ok, _} = Cachex.start_link(:my_cache, [ commands: [ last: command(type: :read, execute: &List.last/1), lpop: command(type: :write, execute: fn ([h | t]) -> {h, t} ([]) -> {nil, []} end) ] ]) :ok = Cachex.put(:my_cache, "items", [1, 2, 3]) 3 = Cachex.invoke(:my_cache, :last, "items") 1 = Cachex.invoke(:my_cache, :lpop, "items") 2 = Cachex.invoke(:my_cache, :lpop, "items") 3 = Cachex.invoke(:my_cache, :lpop, "items") nil = Cachex.invoke(:my_cache, :lpop, "items") # empty list nil = Cachex.invoke(:my_cache, :last, "items") # empty list # default returned when key is missing :default = Cachex.invoke(:my_cache, :last, "no_such_key", :default) ``` -------------------------------- ### Cachex.save/3 and Cachex.restore/3 - Local Persistence Source: https://context7.com/whitfin/cachex/llms.txt Serializes the entire cache to a file using Erlang External Term Format and restores it later. `restore/3` merges into the existing cache; expired entries are not restored. ```elixir {:ok, _} = Cachex.start_link(:cache) Cachex.put_many(:cache, [{"key1","v1"},{"key2","v2"}]) # save to disk :ok = Cachex.save(:cache, "/tmp/my_cache.dat") # clear and restore Cachex.clear(:cache) 0 = Cachex.size(:cache) 2 = Cachex.restore(:cache, "/tmp/my_cache.dat") 2 = Cachex.size(:cache) "v1" = Cachex.get(:cache, "key1") ``` -------------------------------- ### Custom Redis-like Router Implementation Source: https://github.com/whitfin/cachex/blob/main/docs/routing/cache-routers.md A demonstration router implementing Redis-like hashing logic. It uses CRC16 to map keys to hash slots and distributes them across available nodes. ```elixir defmodule MyCustomRouter do @moduledoc """ A very simple demonstration router based on Redis. """ use Cachex.Router # our available slots @max_slots 16384 @doc """ Initialize the router state. This will return a list of connected nodes in our cluster. """ def init(_cache, _options), do: [node() | :erlang.nodes(:connected)] @doc """ Retrieve the nodes in our router state. As our state is just a list of nodes, this is returned as-is. """ def nodes(nodes), do: nodes @doc """ Routes a key to a node in the router state. This will implement our main logic, returning the name of a node that the provided key should be routed over to. """ def route(nodes, key) do # generate our CRC16 value crc_for_key = CRC.crc_16(key) # calculate the number of slots per node slots_per_node = trunc(16384 / length(nodes)) # create groups of slots to compare with slots_for_nodes = 0..(@max_slots - 1) |> Enum.chunk_every(slots_per_node) |> Enum.with_index() # convert our CRC16 to a slot in the cluster slot_for_key = rem(crc_for_key, @max_slots) # locate the group which contains our slot {_group, idx} = Enum.find(slots_for_nodes, fn {slots, _idx} -> Enum.member?(slots, slot_for_key) end) # return the node name Enum.at(nodes, idx) end end ``` -------------------------------- ### Cachex handle_notify/3 Callback Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v2.md The `handle_notify/2` callback has been removed. Always use `handle_notify/3`, which now receives results as the second argument. Results are always `nil` in `:pre` hooks. ```elixir # old format def handle_notify(msg, state) # new format def handle_notify(msg, result, state) ``` -------------------------------- ### Cachex.save/3 and Cachex.restore/3 Source: https://context7.com/whitfin/cachex/llms.txt Serializes the entire cache to a file using Erlang External Term Format and restores it later. `restore/3` merges into the existing cache. ```APIDOC ## `Cachex.save/3` and `Cachex.restore/3` — Local Persistence Serialises the entire cache to a file using the Erlang External Term Format, and restores it later. `restore/3` merges into the existing cache (use `clear/2` first for a clean load). Expired entries are not restored. ```elixir {:ok, _} = Cachex.start_link(:cache) Cachex.put_many(:cache, [{"key1","v1"},{"key2","v2"}]) # save to disk :ok = Cachex.save(:cache, "/tmp/my_cache.dat") # clear and restore Cachex.clear(:cache) 0 = Cachex.size(:cache) 2 = Cachex.restore(:cache, "/tmp/my_cache.dat") 2 = Cachex.size(:cache) "v1" = Cachex.get(:cache, "key1") ``` ``` -------------------------------- ### Add Cachex Dependency to mix.exs Source: https://context7.com/whitfin/cachex/llms.txt Include Cachex as a dependency in your project's `mix.exs` file. ```elixir defp deps do [{:cachex, "~> 4.0"}] end ``` -------------------------------- ### Set Expiration with Cachex.put/4 Source: https://github.com/whitfin/cachex/blob/main/docs/management/expiring-records.md Use the `:expire` option with `Cachex.put/4` for direct expiration setting. This is the most performant option. ```elixir Cachex.put(:my_cache, "key", "value", expire: :timer.seconds(60)) ``` -------------------------------- ### Write an Entry with Cachex.put/4 Source: https://context7.com/whitfin/cachex/llms.txt Stores a key/value pair in the cache. Supports overwriting existing values and setting per-key Time-To-Live (TTL) using the `:expire` option. ```elixir {:ok, _} = Cachex.start_link(:cache) :ok = Cachex.put(:cache, "user:1", %{name: "Alice", role: :admin}) :ok = Cachex.put(:cache, "session:abc", %{user_id: 1}, expire: :timer.seconds(5)) ttl = Cachex.ttl(:cache, "session:abc") IO.inspect(ttl) :ok = Cachex.put(:cache, {:tuple_key, 42}, [1, 2, 3]) ``` -------------------------------- ### Configure LRU Caching with Cachex Source: https://github.com/whitfin/cachex/blob/main/docs/management/limiting-caches.md Use Cachex.Limit.Accessed hook before LRW hooks to enable LRU style pruning. This configuration sets a maximum of 500 entries and uses LRW eviction. ```elixir import Cachex.Spec # maximum 500 entries, LRW eviction, default trim Cachex.start(:my_cache, hooks: [ hook(module: Cachex.Limit.Accessed), hook(module: Cachex.Limit.Scheduled, args: { 500, # setting cache max size [], # options for `Cachex.prune/3` [] # options for `Cachex.Limit.Scheduled` }) ] ) ``` -------------------------------- ### Cache Limiters with Cachex.Limit Source: https://context7.com/whitfin/cachex/llms.txt Configure automatic cache size management using lifecycle hooks. Scheduled prunes on a timer, Evented prunes after each write, and Accessed updates access times for LRU-style eviction. Ensure the 'Accessed' hook comes first when used with other hooks. ```elixir import Cachex.Spec # ---- LRW, scheduled pruning every 3s ---- {:ok, _} = Cachex.start_link(:lrw_cache, [ hooks: [ hook(module: Cachex.Limit.Scheduled, args: { 500, # max entries [reclaim: 0.2], # prune options: evict 20% extra [frequency: :timer.seconds(3)] # hook options }) ] ]) # ---- LRW, event-driven (more precise, higher overhead) ---- {:ok, _} = Cachex.start_link(:evented_cache, [ hooks: [ hook(module: Cachex.Limit.Evented, args: { 500, # max entries [] # prune options }) ] ]) # ---- LRU (update access time on every read) ---- {:ok, _} = Cachex.start_link(:lru_cache, [ hooks: [ hook(module: Cachex.Limit.Accessed), # must come first hook(module: Cachex.Limit.Scheduled, args: { 500, [], [] }) ] ]) # Manual prune (no hook required) 50 = Cachex.prune(:lrw_cache, 50, reclaim: 0) ``` -------------------------------- ### GenServer handle_call/3 Callback Source: https://github.com/whitfin/cachex/blob/main/docs/migrations/migrating-to-v2.md When implementing hooks, update `handle_call/2` to `handle_call/3`. The new second parameter is the call context, and the return tuple changes from `{ :ok, reply, new_state }` to `{ :reply, reply, new_state }`. ```elixir def handle_call(request, _from, state) do # ... {:reply, response, new_state} end ``` -------------------------------- ### Create Basic Cache Stream Source: https://github.com/whitfin/cachex/blob/main/docs/general/streaming-records.md Generates a stream of all non-expired entries in a cache. Requires Cachex.Spec to be imported for pattern matching entry fields. ```elixir import Cachex.Spec # store some values in the cache Cachex.start(:my_cache) Cachex.put(:my_cache, "one", 1) Cachex.put(:my_cache, "two", 2) Cachex.put(:my_cache, "three", 3) # create our cache stream of all records stream = Cachex.stream(:my_cache) # sum up all the cache record values, which == 6 Enum.reduce(stream, 0, fn entry(value: value), total -> total + value end) ``` -------------------------------- ### Provisioning Cache State to Hooks Source: https://github.com/whitfin/cachex/blob/main/docs/extensions/execution-lifecycle.md Use the `provisions/0` callback to request specific cache internals, like the cache state, to be provided to your hook. The `handle_provision/2` callback receives these provisions as `{ type, value }` tuples. ```elixir defmodule MyProject.MyHook do use Cachex.Hook # Initialization. def init(_), do: { :ok, nil } # Request the cache state as a provision. def provisions, do: [ :cache ] # Receive a cache state and store it for later. def handle_provision({ :cache, cache }, _cache), do: { :noreply, cache } end ``` -------------------------------- ### Cachex.stats/2 Source: https://context7.com/whitfin/cachex/llms.txt Returns a map of runtime statistics for the cache. ```APIDOC ## `Cachex.stats/2` — Cache Statistics Returns a map of runtime statistics for the cache. Requires the `Cachex.Stats` hook to be registered at cache startup. ### Description This function retrieves a map containing various runtime statistics about the cache's performance and state. To use this function, the `Cachex.Stats` hook must be enabled when the cache is started. ### Usage Example ```elixir import Cachex.Spec {:ok, _} = Cachex.start_link(:cache, [hooks: [hook(module: Cachex.Stats)]]) for i <- 1..100, do: Cachex.put(:cache, i, i) Cachex.get(:cache, 1) # hit Cachex.get(:cache, 999) # miss stats = Cachex.stats(:cache) IO.inspect(stats) # => %{ # meta: %{creation_date: 1726777631670}, ``` ```