You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Java vertical of MeshJob Stream subscription mode (#1032 follow-up): MeshJobs.subscribeEvents(jobId, options) -> EventSubscription returning a blocking, closeable Iterator<Map<String, Object>>.
Sibling to #1046/#1047 (Python) and #1048/#1049 (TypeScript). Completes the polyglot trilogy and mirrors the #1043 → #1045 cadence established for recvEvent / sendEvent / postEvent.
Motivation
JobController.recvEvent (consumer side, drained once-per-message) and MeshJobs.postEvent / JobProxy.sendEvent (producer side, fire-and-forget) — both shipped in #1045 — cover the point-to-point event injection contract. They do not cover observer/fan-out patterns where a third party (or the commissioner itself, after the original commission/call returned) wants to watch a job's event stream without consuming from it.
Stream subscription mode adds a non-destructive read API:
Reads the registry's per-job event log (JobEvent ent table, (job_id, seq) indexed).
Resumes from a caller-supplied after cursor — late subscribers replay history; reconnecting subscribers resume from their last seq.
Long-polls the registry so idle subscribers don't busy-loop.
Server-supplied next_after watermark drives cursor advance even on empty-page poll cycles (avoids rescanning when server-side types filter excludes events).
Yields event records ({seq, type, payload, posted_by, created_at, trace_context, job_id}).
This is the symmetric counterpart to postEvent on the producer side: producers push, observers read. Multiple subscribers can observe the same job's events independently — each iterator has its own per-call cursor.
Iteration runs indefinitely until the caller breaks out of the loop or the registry returns JobNotFoundException. There is no automatic terminal-state detection — applications signal end with a sentinel event (mirrors Python/TS behavior).
EventSubscription implements Closeable so callers can release the wrapped resources cleanly in a try-with-resources.
Scope
FFI binding: mesh_job_proxy_list_events(handle, after, types_json, timeout_secs, out_envelope_json) -> int. Out-param JSON envelope {events: [...], next_after: N} mirrors the registry HTTP response shape. Error codes: 0 success, -1 invalid args, -2 JobNotFound, -3 other. Reuses parse_ffi_timeout_secs from java: MeshJob event injection parity — recvEvent/sendEvent/postEvent #1045 with negative-sentinel for "no timeout".
JNR binding: MeshCore.mesh_job_proxy_list_events declaration in MeshCore.java.
Summary
Java vertical of MeshJob Stream subscription mode (#1032 follow-up):
MeshJobs.subscribeEvents(jobId, options) -> EventSubscriptionreturning a blocking, closeableIterator<Map<String, Object>>.Sibling to #1046/#1047 (Python) and #1048/#1049 (TypeScript). Completes the polyglot trilogy and mirrors the #1043 → #1045 cadence established for
recvEvent/sendEvent/postEvent.Motivation
JobController.recvEvent(consumer side, drained once-per-message) andMeshJobs.postEvent/JobProxy.sendEvent(producer side, fire-and-forget) — both shipped in #1045 — cover the point-to-point event injection contract. They do not cover observer/fan-out patterns where a third party (or the commissioner itself, after the originalcommission/callreturned) wants to watch a job's event stream without consuming from it.Stream subscription mode adds a non-destructive read API:
JobEventent table,(job_id, seq)indexed).aftercursor — late subscribers replay history; reconnecting subscribers resume from their last seq.next_afterwatermark drives cursor advance even on empty-page poll cycles (avoids rescanning when server-sidetypesfilter excludes events).{seq, type, payload, posted_by, created_at, trace_context, job_id}).This is the symmetric counterpart to
postEventon the producer side: producers push, observers read. Multiple subscribers can observe the same job's events independently — each iterator has its own per-call cursor.Surface
types: optional filter list (server-side filter via existingListJobEventsquery param).after: cursor; subscribers persist this to resume across restarts (default0).longPoll: controls per-requestwaitparameter. Default 30s.Duration.ZERO≡ tight poll.Iteration runs indefinitely until the caller
breaks out of the loop or the registry returnsJobNotFoundException. There is no automatic terminal-state detection — applications signal end with a sentinel event (mirrors Python/TS behavior).EventSubscriptionimplementsCloseableso callers can release the wrapped resources cleanly in a try-with-resources.Scope
mesh_job_proxy_list_events(handle, after, types_json, timeout_secs, out_envelope_json) -> int. Out-param JSON envelope{events: [...], next_after: N}mirrors the registry HTTP response shape. Error codes: 0 success, -1 invalid args, -2 JobNotFound, -3 other. Reusesparse_ffi_timeout_secsfrom java: MeshJob event injection parity — recvEvent/sendEvent/postEvent #1045 with negative-sentinel for "no timeout".MeshCore.mesh_job_proxy_list_eventsdeclaration inMeshCore.java.MeshJobs.subscribeEventsstatic helper returningEventSubscription(blocking Closeable iterator). Reuses the LinkedHashMap-based LRUJobProxycache from java: MeshJob event injection parity — recvEvent/sendEvent/postEvent #1045 — NO new cache.aftercursor semantics,nextAfterwatermark advance on empty pages, JobProxy cache reuse, malformed-payload rejection (missingseq), Closeable behavior.uc23_meshjob_java— producer runningrun_until_donepushes 3 events; commissioner observer subscribes viasubscribeEventsfromafter=0and asserts each event seq/payload is observed end-to-end. Reuses existinglong-task-provider-java/long-task-consumer-javafixtures (extends withrun_until_done+commission_subscribe_observertools).Acceptance
tsuite run --suite-path tests/integration --uc uc23_meshjob_java --parallel 4→ baseline + 1 new tc27_java pass.JobProxy::list_eventsships in main as of MeshJob: Stream subscription — subscribe_events (Python) #1047.