Skip to content

perf: use per-project Redis pubsub channels for WebSocket updates #78

@JohnRDOrazio

Description

@JohnRDOrazio

Problem

The lint and index WebSocket endpoints (lint/ws, ontology/index-ws) both subscribe to a single shared Redis channel (lint:updates, ontology_index:updates). Every connected client receives every message and filters by project_id in Python:

data = json.loads(message["data"])
if data.get("project_id") == project_id_str:
    await websocket.send_json(data)

With many concurrent connections, every client processes every message even if it's for a different project. This is fine at small scale but becomes wasteful at thousands of concurrent connections.

Proposed optimization

Use per-project Redis channels instead of a single global channel:

# Worker publishes to:
channel = f"lint:updates:{project_id}"

# WebSocket subscribes to:
await pubsub.subscribe(f"lint:updates:{project_id}")

This way each client only receives messages relevant to its project, eliminating the filtering overhead.

Changes needed

Worker (worker.py):

  • Publish to f"lint:updates:{project_id}" and f"ontology_index:updates:{project_id}" instead of the global channels

WebSocket endpoints (lint.py, projects.py):

  • Subscribe to f"{CHANNEL}:{project_id}" instead of the global channel
  • Remove the if data.get("project_id") == project_id_str filter (no longer needed)

Scope

This is a low-priority optimization. The current approach works correctly and performs well for hundreds of projects. Consider implementing when:

  • Concurrent WebSocket connections regularly exceed ~1000
  • Redis pubsub message volume becomes measurable overhead

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions