Redis Caching Strategy in the Plane Backend: Architecture and Implementation

Plane implements a dual-layer Redis caching strategy that combines Django's high-level cache framework via django-redis for HTTP responses and database queries, alongside a raw Redis client for real-time collaboration features and background task processing.

The open-source project management platform uses Redis as its sole caching infrastructure to optimize API performance and support collaborative editing. The backend code in the makeplane/plane repository structures this strategy around two distinct access patterns: declarative view caching through decorators and imperative direct access for WebSocket servers and async workers.

Cache Architecture Overview

Plane's Redis caching strategy operates through two complementary layers that share the same Redis instance but serve different architectural purposes.

Django Cache Backend Layer

This layer handles HTTP-level caching for REST API responses, database query results, throttling counters, and temporary authentication tokens like magic links. The implementation uses the django-redis library configured as Django's default cache backend in apps/api/plane/settings/local.py and apps/api/plane/settings/common.py.

Raw Redis Client Layer

This layer provides low-level access for the Hocuspocus real-time collaboration server and custom background tasks requiring direct Redis commands such as Pub/Sub messaging or atomic operations. The client is instantiated through the redis_instance() factory function.

Configuration and Environment Setup

The Redis connection is configured through environment variables and initialized across multiple settings modules.

Django Cache Configuration

In apps/api/plane/settings/local.py, the cache backend is defined to use django_redis.cache.RedisCache:

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": REDIS_URL,
        "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
    }
}

The REDIS_URL environment variable specifies the connection string, while REDIS_SSL enables TLS encryption when required. This configuration allows Django's standard cache API to store and retrieve serialized data transparently.

Raw Client Initialization

For components requiring direct Redis access, apps/api/plane/settings/redis.py provides the redis_instance() function:

from plane.settings.redis import redis_instance

def publish_event(channel: str, payload: dict) -> None:
    r = redis_instance()
    r.publish(channel, json.dumps(payload))

This function returns a redis.Redis object that respects SSL configuration settings, ensuring consistent connection handling across the application.

Key Generation and Storage Strategy

The caching implementation uses deterministic key generation to prevent collisions and enable targeted invalidation. Located in apps/api/plane/utils/cache.py, the generate_cache_key() function constructs keys that incorporate the request path, query parameters, and optionally, the authenticated user's token.

This approach ensures that cached responses remain specific to the exact request context, supporting both global and per-user cache scopes depending on the endpoint requirements.

HTTP Response Caching with Decorators

Plane implements a decorator-based caching pattern that simplifies adding cache logic to API views without redundant boilerplate code.

Using @cache_response

The cache_response decorator from plane/utils/cache.py wraps Django REST framework views to automatically cache JSON responses:

from plane.utils.cache import cache_response
from django.views.decorators.cache import cache_control
from django.utils.decorators import method_decorator

class WorkspaceDetail(APIView):
    @cache_response(timeout=60 * 30, user=True)   # 30-minute per-user cache

    @method_decorator(cache_control(private=True, max_age=60))
    def get(self, request, pk):
        workspace = Workspace.objects.get(pk=pk)
        return Response(WorkspaceSerializer(workspace).data)

The decorator accepts parameters for timeout (in seconds), path (custom cache key path), and user (boolean indicating whether to include user-specific identifiers in the key). When user=True, the cache key incorporates the authentication header to create isolated cache entries per user.

According to the source code in apps/api/plane/license/api/views/instance.py, system-wide endpoints like instance configuration use @cache_response(60 * 60 * 2, user=False) to cache responses for two hours across all users.

Cache Invalidation Patterns

The strategy includes explicit invalidation mechanisms to ensure data consistency when mutations occur.

Explicit Invalidation with @invalidate_cache

The invalidate_cache decorator removes cached entries when data changes, forcing subsequent requests to recalculate fresh responses:

from plane.utils.cache import invalidate_cache

class WorkspaceUpdate(APIView):
    @invalidate_cache(path="/api/workspaces/", user=False)
    def post(self, request):
        # …perform update…

        return Response(status=204)

This pattern appears throughout the API layer, including in apps/api/plane/app/views/workspace/member.py, where workspace membership changes trigger cache invalidation for affected endpoints. The decorator accepts the same path and user parameters as cache_response, ensuring that invalidation targets exactly the keys that were previously cached.

Raw Redis Operations for Real-Time Features

Beyond HTTP caching, Plane uses the raw Redis client for the Hocuspocus collaboration server defined in apps/live/src/extensions/redis.ts. This TypeScript extension connects to the same Redis instance to synchronize document states across distributed server nodes.

Background tasks and async workers also utilize redis_instance() for direct operations such as:

  • Publishing real-time notifications through Pub/Sub channels
  • Implementing distributed rate limiting
  • Managing task queues and locks

Summary

  • Plane uses a unified Redis instance configured through django-redis in apps/api/plane/settings/local.py, serving both high-level Django caching and low-level direct access.
  • HTTP caching relies on custom decorators (@cache_response, @invalidate_cache) defined in apps/api/plane/utils/cache.py that generate deterministic keys based on request paths and user contexts.
  • Per-user and global caching scopes are supported through the user parameter, allowing fine-grained control over cache isolation.
  • Raw Redis access via redis_instance() in apps/api/plane/settings/redis.py supports real-time collaboration through the Hocuspocus server and custom background tasks.
  • Explicit invalidation ensures cache consistency by removing entries immediately after data mutations occur.

Frequently Asked Questions

How does Plane handle cache key collisions between different users?

Plane's generate_cache_key function in apps/api/plane/utils/cache.py incorporates the authentication header into the cache key when the user=True parameter is passed to the @cache_response decorator. This creates unique cache entries per user for the same endpoint path, preventing cross-user data leakage while allowing shared global caches for public data when user=False.

What is the difference between the Django cache backend and the raw Redis client in Plane?

The Django cache backend, configured as django_redis.cache.RedisCache, provides high-level key-value storage accessed through Django's standard cache API and powers the @cache_response decorator for HTTP caching. The raw Redis client returned by redis_instance() offers direct access to Redis commands like PUBLISH, SUBSCRIBE, and atomic operations, used by the real-time collaboration server in apps/live/src/extensions/redis.ts and background workers.

How does Plane invalidate cached data when database records change?

Plane uses the @invalidate_cache decorator applied to mutation endpoints (POST, PUT, DELETE). When these endpoints execute, the decorator removes the specific cache keys matching the path and user parameters before the view returns. This explicit invalidation strategy ensures that subsequent GET requests generate fresh data rather than serving stale cached responses.

Can Plane's Redis caching work with SSL/TLS encryption?

Yes, the redis_instance() function in apps/api/plane/settings/redis.py checks for the REDIS_SSL environment variable when constructing the Redis connection. When enabled, the function configures the redis.Redis client with appropriate SSL parameters, ensuring encrypted connections for both the Django cache backend and raw client operations in production environments.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →