mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 00:16:23 +02:00
feat: separate flow service from config service with explicit queue
lifecycle management
The flow service is now an independent service that owns the lifecycle
of flow and blueprint queues. System services own their own queues.
Consumers never create queues.
Flow service separation:
- New service at trustgraph-flow/trustgraph/flow/service/
- Uses async ConfigClient (RequestResponse pattern) to talk to config
service
- Config service stripped of all flow handling
Queue lifecycle management:
- PubSubBackend protocol gains create_queue, delete_queue,
queue_exists, ensure_queue — all async
- RabbitMQ: implements via pika with asyncio.to_thread internally
- Pulsar: stubs for future admin REST API implementation
- Consumer _connect() no longer creates queues (passive=True for named
queues)
- System services call ensure_queue on startup
- Flow service creates queues on flow start, deletes on flow stop
- Flow service ensures queues for pre-existing flows on startup
Two-phase flow stop:
- Phase 1: set flow status to "stopping", delete processor config
entries
- Phase 2: retry queue deletion, then delete flow record
Config restructure:
- active-flow config replaced with processor:{name} types
- Each processor has its own config type, each flow variant is a key
- Flow start/stop use batch put/delete — single config push per
operation
- FlowProcessor subscribes to its own type only
Blueprint format:
- Processor entries split into topics and parameters dicts
- Flow interfaces use {"flow": "topic"} instead of bare strings
- Specs (ConsumerSpec, ProducerSpec, etc.) read from
definition["topics"]
Tests updated
92 lines
2.8 KiB
Python
92 lines
2.8 KiB
Python
|
|
from . request_response_spec import RequestResponse, RequestResponseSpec
|
|
from .. schema import ConfigRequest, ConfigResponse, ConfigKey, ConfigValue
|
|
|
|
CONFIG_TIMEOUT = 10
|
|
|
|
|
|
class ConfigClient(RequestResponse):
|
|
|
|
async def _request(self, timeout=CONFIG_TIMEOUT, **kwargs):
|
|
resp = await self.request(
|
|
ConfigRequest(**kwargs),
|
|
timeout=timeout,
|
|
)
|
|
if resp.error:
|
|
raise RuntimeError(
|
|
f"{resp.error.type}: {resp.error.message}"
|
|
)
|
|
return resp
|
|
|
|
async def get(self, type, key, timeout=CONFIG_TIMEOUT):
|
|
"""Get a single config value. Returns the value string or None."""
|
|
resp = await self._request(
|
|
operation="get",
|
|
keys=[ConfigKey(type=type, key=key)],
|
|
timeout=timeout,
|
|
)
|
|
if resp.values and len(resp.values) > 0:
|
|
return resp.values[0].value
|
|
return None
|
|
|
|
async def put(self, type, key, value, timeout=CONFIG_TIMEOUT):
|
|
"""Put a single config value."""
|
|
await self._request(
|
|
operation="put",
|
|
values=[ConfigValue(type=type, key=key, value=value)],
|
|
timeout=timeout,
|
|
)
|
|
|
|
async def put_many(self, values, timeout=CONFIG_TIMEOUT):
|
|
"""Put multiple config values in a single request.
|
|
values is a list of (type, key, value) tuples."""
|
|
await self._request(
|
|
operation="put",
|
|
values=[
|
|
ConfigValue(type=t, key=k, value=v)
|
|
for t, k, v in values
|
|
],
|
|
timeout=timeout,
|
|
)
|
|
|
|
async def delete(self, type, key, timeout=CONFIG_TIMEOUT):
|
|
"""Delete a single config key."""
|
|
await self._request(
|
|
operation="delete",
|
|
keys=[ConfigKey(type=type, key=key)],
|
|
timeout=timeout,
|
|
)
|
|
|
|
async def delete_many(self, keys, timeout=CONFIG_TIMEOUT):
|
|
"""Delete multiple config keys in a single request.
|
|
keys is a list of (type, key) tuples."""
|
|
await self._request(
|
|
operation="delete",
|
|
keys=[
|
|
ConfigKey(type=t, key=k)
|
|
for t, k in keys
|
|
],
|
|
timeout=timeout,
|
|
)
|
|
|
|
async def keys(self, type, timeout=CONFIG_TIMEOUT):
|
|
"""List all keys for a config type."""
|
|
resp = await self._request(
|
|
operation="list",
|
|
type=type,
|
|
timeout=timeout,
|
|
)
|
|
return resp.directory
|
|
|
|
|
|
class ConfigClientSpec(RequestResponseSpec):
|
|
def __init__(
|
|
self, request_name, response_name,
|
|
):
|
|
super(ConfigClientSpec, self).__init__(
|
|
request_name=request_name,
|
|
request_schema=ConfigRequest,
|
|
response_name=response_name,
|
|
response_schema=ConfigResponse,
|
|
impl=ConfigClient,
|
|
)
|