Skip to content

MeshJob: Stream subscription mode — subscribeEvents iterator (Java) #1050

Description

@dhyansraj

Summary

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.

Surface

try (EventSubscription sub = MeshJobs.subscribeEvents(
        jobId,
        SubscribeOptions.builder()
            .types(List.of("progress"))
            .after(0)
            .longPoll(Duration.ofSeconds(30))
            .build())) {
    while (sub.hasNext()) {
        Map<String, Object> event = sub.next();
        if (isTerminal(event)) break;
    }
}
  • types: optional filter list (server-side filter via existing ListJobEvents query param).
  • after: cursor; subscribers persist this to resume across restarts (default 0).
  • longPoll: controls per-request wait parameter. Default 30s. Duration.ZERO ≡ tight poll.

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.
  • Java SDK: MeshJobs.subscribeEvents static helper returning EventSubscription (blocking Closeable iterator). Reuses the LinkedHashMap-based LRU JobProxy cache from java: MeshJob event injection parity — recvEvent/sendEvent/postEvent #1045 — NO new cache.
  • Unit tests: filter pass-through, after cursor semantics, nextAfter watermark advance on empty pages, JobProxy cache reuse, malformed-payload rejection (missing seq), Closeable behavior.
  • Integration test: tc27_java in uc23_meshjob_java — producer running run_until_done pushes 3 events; commissioner observer subscribes via subscribeEvents from after=0 and asserts each event seq/payload is observed end-to-end. Reuses existing long-task-provider-java / long-task-consumer-java fixtures (extends with run_until_done + commission_subscribe_observer tools).

Acceptance

  • tsuite run --suite-path tests/integration --uc uc23_meshjob_java --parallel 4 → baseline + 1 new tc27_java pass.
  • Java unit tests cover the surface (filter, cursor, watermark, cache reuse, malformed payload).
  • Rust core unchanged — JobProxy::list_events ships in main as of MeshJob: Stream subscription — subscribe_events (Python) #1047.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions