Pub/sub abstraction: decouple from Pulsar (#751)

Remove Pulsar-specific concepts from application code so that
the pub/sub backend is swappable via configuration.

Rename translators:
- to_pulsar/from_pulsar → decode/encode across all translator
  classes, dispatch handlers, and tests (55+ files)
- from_response_with_completion → encode_with_completion
- Remove pulsar.schema.Record from translator base class

Queue naming (CLASS:TOPICSPACE:TOPIC):
- Replace topic() helper with queue() using new format:
  flow:tg:name, request:tg:name, response:tg:name, state:tg:name
- Queue class implies persistence/TTL (no QoS in names)
- Update Pulsar backend map_topic() to parse new format
- Librarian queues use flow class (persistent, for chunking)
- Config push uses state class (persistent, last-value)
- Remove 15 dead topic imports from schema files
- Update init_trustgraph.py namespace: config → state

Confine Pulsar to pulsar_backend.py:
- Delete legacy PulsarClient class from pubsub.py
- Move add_args to add_pubsub_args() with standalone flag
  for CLI tools (defaults to localhost)
- PulsarBackendConsumer.receive() catches _pulsar.Timeout,
  raises standard TimeoutError
- Remove Pulsar imports from: async_processor, flow_processor,
  log_level, all 11 client files, 4 storage writers, gateway
  service, gateway config receiver
- Remove log_level/LoggerLevel from client API
- Rewrite tg-monitor-prompts to use backend abstraction
- Update tg-dump-queues to use add_pubsub_args

Also: pubsub-abstraction.md tech spec covering problem statement,
design goals, as-is requirements, candidate broker assessment,
approach, and implementation order.
This commit is contained in:
cybermaggedon 2026-04-01 20:16:53 +01:00 committed by GitHub
parent dbf8daa74a
commit 4fb0b4d8e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 1269 additions and 788 deletions

View file

@ -1,23 +1,26 @@
def topic(queue_name, qos='q1', tenant='tg', namespace='flow'):
def queue(topic, cls='flow', topicspace='tg'):
"""
Create a generic topic identifier that can be mapped by backends.
Create a queue identifier in CLASS:TOPICSPACE:TOPIC format.
Args:
queue_name: The queue/topic name
qos: Quality of service
- 'q0' = best-effort (no ack)
- 'q1' = at-least-once (ack required)
- 'q2' = exactly-once (two-phase ack)
tenant: Tenant identifier for multi-tenancy
namespace: Namespace within tenant
topic: The logical queue name (e.g. 'config', 'librarian')
cls: Queue class determining operational characteristics:
- 'flow' = persistent processing pipeline queue
- 'request' = non-persistent, short TTL request queue
- 'response' = non-persistent, short TTL response queue
- 'state' = persistent, last-value state broadcast
topicspace: Deployment isolation prefix (default: 'tg')
Returns:
Generic topic string: qos/tenant/namespace/queue_name
Queue identifier string: cls:topicspace:topic
Examples:
topic('my-queue') # q1/tg/flow/my-queue
topic('config', qos='q2', namespace='config') # q2/tg/config/config
queue('text-completion-request')
# flow:tg:text-completion-request
queue('config', cls='request')
# request:tg:config
queue('config', cls='state')
# state:tg:config
"""
return f"{qos}/{tenant}/{namespace}/{queue_name}"
return f"{cls}:{topicspace}:{topic}"

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass
from ..core.metadata import Metadata
from ..core.topic import topic
############################################################################

View file

@ -2,7 +2,6 @@ from dataclasses import dataclass, field
from ..core.metadata import Metadata
from ..core.primitives import Term, RowSchema
from ..core.topic import topic
############################################################################

View file

@ -2,7 +2,6 @@ from dataclasses import dataclass, field
from ..core.primitives import Term, Triple
from ..core.metadata import Metadata
from ..core.topic import topic
############################################################################

View file

@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from ..core.primitives import Triple, Error
from ..core.topic import topic
from ..core.topic import queue
from ..core.metadata import Metadata
from .document import Document, TextDocument
from .graph import Triples
@ -52,9 +52,5 @@ class KnowledgeResponse:
triples: Triples | None = None
graph_embeddings: GraphEmbeddings | None = None
knowledge_request_queue = topic(
'knowledge', qos='q0', namespace='request'
)
knowledge_response_queue = topic(
'knowledge', qos='q0', namespace='response',
)
knowledge_request_queue = queue('knowledge', cls='request')
knowledge_response_queue = queue('knowledge', cls='response')

View file

@ -1,6 +1,5 @@
from dataclasses import dataclass
from ..core.topic import topic
############################################################################

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass, field
from ..core.metadata import Metadata
from ..core.topic import topic
############################################################################

View file

@ -2,7 +2,6 @@ from dataclasses import dataclass, field
from ..core.metadata import Metadata
from ..core.primitives import RowSchema
from ..core.topic import topic
############################################################################

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass, field
from ..core.metadata import Metadata
from ..core.topic import topic
############################################################################

View file

@ -2,7 +2,6 @@
from dataclasses import dataclass, field
from typing import Optional
from ..core.topic import topic
from ..core.primitives import Error
############################################################################

View file

@ -2,7 +2,7 @@ from dataclasses import dataclass, field
from datetime import datetime
from ..core.primitives import Error
from ..core.topic import topic
from ..core.topic import queue
############################################################################
@ -50,10 +50,6 @@ class CollectionManagementResponse:
# Topics
collection_request_queue = topic(
'collection', qos='q0', namespace='request'
)
collection_response_queue = topic(
'collection', qos='q0', namespace='response'
)
collection_request_queue = queue('collection', cls='request')
collection_response_queue = queue('collection', cls='response')

View file

@ -1,7 +1,7 @@
from dataclasses import dataclass, field
from ..core.topic import topic
from ..core.topic import queue
from ..core.primitives import Error
############################################################################
@ -60,15 +60,9 @@ class ConfigPush:
version: int = 0
config: dict[str, dict[str, str]] = field(default_factory=dict)
config_request_queue = topic(
'config', qos='q0', namespace='request'
)
config_response_queue = topic(
'config', qos='q0', namespace='response'
)
config_push_queue = topic(
'config', qos='q2', namespace='config'
)
config_request_queue = queue('config', cls='request')
config_response_queue = queue('config', cls='response')
config_push_queue = queue('config', cls='state')
############################################################################

View file

@ -1,7 +1,7 @@
from dataclasses import dataclass, field
from ..core.topic import topic
from ..core.topic import queue
from ..core.primitives import Error
############################################################################
@ -61,12 +61,8 @@ class FlowResponse:
# Everything
error: Error | None = None
flow_request_queue = topic(
'flow', qos='q0', namespace='request'
)
flow_response_queue = topic(
'flow', qos='q0', namespace='response'
)
flow_request_queue = queue('flow', cls='request')
flow_response_queue = queue('flow', cls='response')
############################################################################

View file

@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from ..core.primitives import Triple, Error
from ..core.topic import topic
from ..core.topic import queue
from ..core.metadata import Metadata
# Note: Document imports will be updated after knowledge schemas are converted
@ -220,9 +220,5 @@ class LibrarianResponse:
# FIXME: Is this right? Using persistence on librarian so that
# message chunking works
librarian_request_queue = topic(
'librarian', qos='q1', namespace='request'
)
librarian_response_queue = topic(
'librarian', qos='q1', namespace='response',
)
librarian_request_queue = queue('librarian-request', cls='flow')
librarian_response_queue = queue('librarian-response', cls='flow')

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass, field
from ..core.topic import topic
from ..core.primitives import Error
############################################################################

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass
from ..core.primitives import Error, Term, Triple
from ..core.topic import topic
from ..core.metadata import Metadata
############################################################################

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass, field
from ..core.primitives import Error
from ..core.topic import topic
############################################################################

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass, field
from ..core.primitives import Error
from ..core.topic import topic
############################################################################

View file

@ -1,7 +1,7 @@
from dataclasses import dataclass, field
from ..core.primitives import Error, Term, Triple
from ..core.topic import topic
from ..core.topic import queue
############################################################################
@ -69,12 +69,8 @@ class DocumentEmbeddingsResponse:
error: Error | None = None
chunks: list[ChunkMatch] = field(default_factory=list)
document_embeddings_request_queue = topic(
"document-embeddings-request", qos='q0', tenant='trustgraph', namespace='flow'
)
document_embeddings_response_queue = topic(
"document-embeddings-response", qos='q0', tenant='trustgraph', namespace='flow'
)
document_embeddings_request_queue = queue('document-embeddings', cls='request')
document_embeddings_response_queue = queue('document-embeddings', cls='response')
############################################################################
@ -104,9 +100,5 @@ class RowEmbeddingsResponse:
error: Error | None = None
matches: list[RowIndexMatch] = field(default_factory=list)
row_embeddings_request_queue = topic(
"row-embeddings-request", qos='q0', tenant='trustgraph', namespace='flow'
)
row_embeddings_response_queue = topic(
"row-embeddings-response", qos='q0', tenant='trustgraph', namespace='flow'
)
row_embeddings_request_queue = queue('row-embeddings', cls='request')
row_embeddings_response_queue = queue('row-embeddings', cls='response')

View file

@ -1,5 +1,4 @@
from dataclasses import dataclass
from ..core.topic import topic
from ..core.primitives import Error, Term
############################################################################

View file

@ -2,7 +2,6 @@ from dataclasses import dataclass, field
from typing import Optional
from ..core.primitives import Error
from ..core.topic import topic
############################################################################

View file

@ -1,7 +1,6 @@
from dataclasses import dataclass, field
from ..core.primitives import Error
from ..core.topic import topic
############################################################################