Skip to content

Support combining :key and :query options in cache_evict decorator #245

@cabol

Description

@cabol

Enhancement: Support combining :key and :query options in cache_evict decorator

Summary

Add support for using both :key and :query options together in the @decorate cache_evict decorator. This enhancement allows developers to evict both specific cache entries and entries matching a query pattern in a single operation.

Current Behavior

Previously, when both :key and :query options were provided to the cache_evict decorator, the :query option would override the :key option, making it impossible to evict specific keys alongside query-based eviction in a single decorator call.

New Behavior

When both :key and :query options are provided, the decorator now executes both evictions in the following order:

  1. Query-based eviction - First, all entries matching the query are evicted
  2. Key-based eviction - Then, the specific key(s) are evicted

Motivation

This enhancement addresses common use cases where developers need to evict hierarchical or related data:

  • User logout: Evict the user's cache entry and all their session entries
  • Category deletion: Evict both the category entry (by ID and slug) and all products in that category
  • Cleanup operations: Remove a primary record and all its dependent cached records

Without this feature, developers would need to:

  • Use multiple decorator calls, or
  • Manually evict keys before/after the decorated function, or
  • Create complex queries that include specific keys

Examples

Note: The query examples below assume you are using the Nebulex.Adapters.Local adapter, which uses ETS match specifications. If you're using a different adapter (e.g., Redis, Partitioned, etc.), the query syntax will vary according to that adapter's documentation.

Example 1: User logout with session cleanup

@decorate cache_evict(key: user_id, query: &query_for_user_sessions/1)
def logout_user(user_id) do
  # Evicts the user entry and all session entries for this user
  UserSessions.delete_all_for_user(user_id)
end

defp query_for_user_sessions(%{args: [user_id]} = _context) do
  # Return a query that matches all session entries for the given user
  [
    {
      {:entry, :"$1", %{user_id: :"$2", type: "session"}, :_, :_},
      [{:"=:=", :"$2", user_id}],
      [true]
    }
  ]
end

Example 2: Category deletion with products

@decorate cache_evict(
  key: {:in, [category.id, category.slug]},
  query: &query_for_category_products/1
)
def delete_category(category) do
  # Evicts both category cache entries (by id and slug) and all
  # product entries within that category
  Products.delete_all_for_category(category.id)
end

defp query_for_category_products(%{args: [category]} = _context) do
  [
    {
      {:entry, :"$1", %{category_id: :"$2"}, :_, :_},
      [{:"=:=", :"$2", category.id}],
      [true]
    }
  ]
end

Implementation Details

  • Updated action_block/6 in Nebulex.Caching.Decorators to properly quote and handle the combined case
  • Updated eval_key/2 to handle the {:"$nbx_query", q, k} tuple for combined eviction
  • Updated do_evict/4 to execute query-based eviction first, then key-based eviction
  • Added comprehensive tests validating the new behavior
  • Updated documentation in both Nebulex.Caching.Options and Nebulex.Caching.Decorators
  • The implementation is adapter-agnostic and works with any cache adapter that supports query-based operations
  • Documentation examples use Nebulex.Adapters.Local (ETS match specifications) for illustration purposes

Documentation

The following documentation has been added/updated:

  1. Options documentation (Nebulex.Caching.Options): Updated the :query option to explain the combined behavior
  2. Decorator documentation (Nebulex.Caching.Decorators):
    • Added a new subsection "Combining :key and :query" with detailed examples
    • Added examples in the main Examples section
    • Updated best practices to include guidance on using both options for hierarchical data

Breaking Changes

None. This is a backward-compatible enhancement. Existing code using only :key or only :query continues to work exactly as before.

Testing

  • Added test case: test "with both key and query"
  • Added test case: test "with both key (multiple) and query"
  • All existing tests continue to pass
  • Documentation builds successfully without warnings

Related Issues

This enhancement builds upon the query support added in #243 and external reference eviction support added in #244.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions