mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-09-07 18:23:46 +00:00
TransactionID Pubsub problem
This commit is contained in:
@@ -63,6 +63,7 @@ def map_analytics_event_to_tim_flat_payload(
|
|||||||
# Tracking
|
# Tracking
|
||||||
"eventType": event.get("eventType") or event_type,
|
"eventType": event.get("eventType") or event_type,
|
||||||
"traceId": _first(data, "traceId", "trace_id"),
|
"traceId": _first(data, "traceId", "trace_id"),
|
||||||
|
"transactionId": _first(data, "transactionId", "transaction_id", "transactionID"),
|
||||||
"spanId": _first(data, "spanId", "span_id"),
|
"spanId": _first(data, "spanId", "span_id"),
|
||||||
"parentSpanId": _first(data, "parentSpanId", "parent_span_id"),
|
"parentSpanId": _first(data, "parentSpanId", "parent_span_id"),
|
||||||
"eventName": _first(data, "eventName", "name"),
|
"eventName": _first(data, "eventName", "name"),
|
||||||
|
|||||||
@@ -101,10 +101,24 @@ def _safe_part(value: Any, fallback: str) -> str:
|
|||||||
return text.replace(" ", "_").replace("/", "_").replace("\\", "_")
|
return text.replace(" ", "_").replace("/", "_").replace("\\", "_")
|
||||||
|
|
||||||
|
|
||||||
def build_sequence_key(agent_id: str | None, session_id: str) -> str:
|
def build_sequence_key(
|
||||||
|
agent_id: str | None,
|
||||||
|
session_id: str | None,
|
||||||
|
transaction_id: str | None = None,
|
||||||
|
) -> str:
|
||||||
|
"""Build the counter key, preferring transaction isolation.
|
||||||
|
|
||||||
|
``session_id`` remains as a compatibility fallback for older producers that
|
||||||
|
do not yet send ``transactionId``. New events must use one counter per
|
||||||
|
transaction so concurrent requests in the same session do not share a
|
||||||
|
sequence.
|
||||||
|
"""
|
||||||
agent = _safe_part(agent_id or os.getenv("AGENT_NAME"), "agent")
|
agent = _safe_part(agent_id or os.getenv("AGENT_NAME"), "agent")
|
||||||
|
if transaction_id:
|
||||||
|
transaction = _safe_part(transaction_id, "unknown_transaction")
|
||||||
|
return f"{_key_prefix()}:{agent}:transaction:{transaction}"
|
||||||
session = _safe_part(session_id, "unknown_session")
|
session = _safe_part(session_id, "unknown_session")
|
||||||
return f"{_key_prefix()}:{agent}:{session}"
|
return f"{_key_prefix()}:{agent}:session:{session}"
|
||||||
|
|
||||||
|
|
||||||
async def _next_sequence_redis(key: str, ttl_seconds: int) -> int | None:
|
async def _next_sequence_redis(key: str, ttl_seconds: int) -> int | None:
|
||||||
@@ -137,7 +151,8 @@ _mongo_index_lock = asyncio.Lock()
|
|||||||
def _next_sequence_mongodb_sync(
|
def _next_sequence_mongodb_sync(
|
||||||
key: str,
|
key: str,
|
||||||
agent_id: str | None,
|
agent_id: str | None,
|
||||||
session_id: str,
|
session_id: str | None,
|
||||||
|
transaction_id: str | None,
|
||||||
ttl_seconds: int,
|
ttl_seconds: int,
|
||||||
) -> int | None:
|
) -> int | None:
|
||||||
uri = _mongo_uri()
|
uri = _mongo_uri()
|
||||||
@@ -157,6 +172,8 @@ def _next_sequence_mongodb_sync(
|
|||||||
"$set": {
|
"$set": {
|
||||||
"agentId": agent_id or os.getenv("AGENT_NAME") or "agent",
|
"agentId": agent_id or os.getenv("AGENT_NAME") or "agent",
|
||||||
"sessionId": session_id,
|
"sessionId": session_id,
|
||||||
|
"transactionId": transaction_id,
|
||||||
|
"sequenceScope": "transaction" if transaction_id else "session",
|
||||||
"updatedAt": now,
|
"updatedAt": now,
|
||||||
},
|
},
|
||||||
"$setOnInsert": {
|
"$setOnInsert": {
|
||||||
@@ -214,7 +231,8 @@ async def _ensure_mongo_ttl_index_once(ttl_seconds: int) -> None:
|
|||||||
async def _next_sequence_mongodb(
|
async def _next_sequence_mongodb(
|
||||||
key: str,
|
key: str,
|
||||||
agent_id: str | None,
|
agent_id: str | None,
|
||||||
session_id: str,
|
session_id: str | None,
|
||||||
|
transaction_id: str | None,
|
||||||
ttl_seconds: int,
|
ttl_seconds: int,
|
||||||
) -> int | None:
|
) -> int | None:
|
||||||
if not _mongo_uri():
|
if not _mongo_uri():
|
||||||
@@ -226,6 +244,7 @@ async def _next_sequence_mongodb(
|
|||||||
key,
|
key,
|
||||||
agent_id,
|
agent_id,
|
||||||
session_id,
|
session_id,
|
||||||
|
transaction_id,
|
||||||
ttl_seconds,
|
ttl_seconds,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -239,33 +258,26 @@ async def _next_sequence_memory(key: str) -> int:
|
|||||||
return _memory_counters[key]
|
return _memory_counters[key]
|
||||||
|
|
||||||
|
|
||||||
async def next_sequence(agent_id: str | None, session_id: str | None) -> int | None:
|
async def next_sequence(
|
||||||
"""Return the next per-agent/per-session observer sequence.
|
agent_id: str | None,
|
||||||
|
session_id: str | None,
|
||||||
|
transaction_id: str | None = None,
|
||||||
|
) -> int | None:
|
||||||
|
"""Return the next observer sequence isolated by transaction.
|
||||||
|
|
||||||
Shared backends:
|
The preferred scope is ``agent_id + transaction_id``. ``session_id`` is
|
||||||
- Redis: atomic INCR, selected by PUBSUB_SEQUENCE_PROVIDER=redis.
|
used only as a backward-compatible fallback when the producer does not send
|
||||||
- MongoDB: atomic find_one_and_update/$inc, selected by
|
a transaction identifier. Redis and MongoDB increments remain atomic across
|
||||||
PUBSUB_SEQUENCE_PROVIDER=mongodb. This mirrors the TIM legacy behavior.
|
Kubernetes replicas.
|
||||||
|
|
||||||
Provider selection:
|
|
||||||
- auto (default): Redis when configured; otherwise MongoDB when configured;
|
|
||||||
otherwise memory fallback when enabled.
|
|
||||||
- redis: Redis only, then memory fallback when enabled.
|
|
||||||
- mongodb/mongo: MongoDB only, then memory fallback when enabled.
|
|
||||||
- memory: in-process only.
|
|
||||||
- none: disabled.
|
|
||||||
|
|
||||||
If session_id is absent or the shared backend fails and memory fallback is
|
|
||||||
disabled, None is returned so the payload remains valid without sequence.
|
|
||||||
"""
|
"""
|
||||||
if not sequence_enabled() or not session_id:
|
if not sequence_enabled() or (not transaction_id and not session_id):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
provider = _sequence_provider()
|
provider = _sequence_provider()
|
||||||
if provider == "none":
|
if provider == "none":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
key = build_sequence_key(agent_id, session_id)
|
key = build_sequence_key(agent_id, session_id, transaction_id)
|
||||||
ttl_seconds = _ttl_seconds()
|
ttl_seconds = _ttl_seconds()
|
||||||
value: int | None = None
|
value: int | None = None
|
||||||
|
|
||||||
@@ -275,12 +287,16 @@ async def next_sequence(agent_id: str | None, session_id: str | None) -> int | N
|
|||||||
if provider == "redis":
|
if provider == "redis":
|
||||||
value = await _next_sequence_redis(key, ttl_seconds)
|
value = await _next_sequence_redis(key, ttl_seconds)
|
||||||
elif provider == "mongodb":
|
elif provider == "mongodb":
|
||||||
value = await _next_sequence_mongodb(key, agent_id, session_id, ttl_seconds)
|
value = await _next_sequence_mongodb(
|
||||||
|
key, agent_id, session_id, transaction_id, ttl_seconds
|
||||||
|
)
|
||||||
else: # auto
|
else: # auto
|
||||||
if _redis_url():
|
if _redis_url():
|
||||||
value = await _next_sequence_redis(key, ttl_seconds)
|
value = await _next_sequence_redis(key, ttl_seconds)
|
||||||
if value is None and _mongo_uri():
|
if value is None and _mongo_uri():
|
||||||
value = await _next_sequence_mongodb(key, agent_id, session_id, ttl_seconds)
|
value = await _next_sequence_mongodb(
|
||||||
|
key, agent_id, session_id, transaction_id, ttl_seconds
|
||||||
|
)
|
||||||
|
|
||||||
if value is not None:
|
if value is not None:
|
||||||
return value
|
return value
|
||||||
@@ -296,8 +312,13 @@ async def ensure_sequence(payload: dict[str, Any]) -> dict[str, Any]:
|
|||||||
if payload.get("sequence") is not None:
|
if payload.get("sequence") is not None:
|
||||||
return payload
|
return payload
|
||||||
session_id = payload.get("sessionId") or payload.get("session_id")
|
session_id = payload.get("sessionId") or payload.get("session_id")
|
||||||
|
transaction_id = (
|
||||||
|
payload.get("transactionId")
|
||||||
|
or payload.get("transaction_id")
|
||||||
|
or payload.get("transactionID")
|
||||||
|
)
|
||||||
agent_id = payload.get("agentId") or payload.get("agent_id") or os.getenv("AGENT_NAME")
|
agent_id = payload.get("agentId") or payload.get("agent_id") or os.getenv("AGENT_NAME")
|
||||||
seq = await next_sequence(agent_id, session_id)
|
seq = await next_sequence(agent_id, session_id, transaction_id)
|
||||||
if seq is not None:
|
if seq is not None:
|
||||||
payload["sequence"] = seq
|
payload["sequence"] = seq
|
||||||
return payload
|
return payload
|
||||||
|
|||||||
Reference in New Issue
Block a user