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

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions