Skip to content

Upstream CAP scheduler request: expose a completion-guarantee hook on .as(name) singleton scheduling #1022

Description

@jung-thomas

Context

Filed alongside #1021, which describes how a synchronous throw inside a scheduled job's handler wedges CAP 10's srv.schedule(...).as(jobName) singleton lock. The workaround is a manual DELETE against CDS_OUTBOX_MESSAGES, plus a catch-block in runWithLock that does the same. Neither is what we want long-term — this is CAP-framework behavior.

Ask (to upstream at SAP Support Portal, component BC-XS-CDX-NJS)

Two options, either would fix it:

  1. Guaranteed status-flip on throw. The CAP scheduling internals should wrap the handler invocation in a try/finally and always transition the CDS_OUTBOX_MESSAGES row out of processing — to error (if retries make sense) or delete it. Callers should not have to reach into the internal outbox schema to unstick themselves.
  2. Publish a documented hook. Alternatively, expose a way for the handler (or the enclosing service) to explicitly signal "tick complete" that the framework can call on the exception path. That gives userland the ability to write the belt-and-suspenders catch itself without depending on unwritten schema invariants.

Option 1 is preferred; option 2 is acceptable.

Why this matters

  • The current behavior fails-open silently: no JobLastRun row, no PipelineLog, just a wedged tick that never fires again until manually cleared.
  • The failure mode is easy to hit — any resolver/config precondition inside the handler that throws synchronously before the first await will do it.
  • The pre-CAP-10 chassis (JobLocks) didn't have this problem because we owned lock lifecycle in runWithLock's finally-block. chore: replace node-cron scheduler with CAP 10 Scheduling API #958's migration to .as(name) moved lock lifecycle into the framework, which is fine, but now the framework has to guarantee the finally semantics we used to guarantee ourselves.

Draft SAP Support text

Component: BC-XS-CDX-NJS

Title: CAP 10 scheduling API — .as(jobName) singleton wedges on synchronous throw inside handler

Description: A scheduled job registered via srv.schedule('cron.foo', {}).every('13 2 * * *').as('foo') — where the on('cron.foo') handler throws synchronously (e.g. from a config-resolver precondition) — leaves the corresponding CDS_OUTBOX_MESSAGES row at STATUS='processing' indefinitely. Subsequent scheduled ticks do not fire; the framework treats the slot as occupied. Manual DELETE against CDS_OUTBOX_MESSAGES is the only recovery.

Expected: framework flips the row out of processing on any exit path (success OR throw), analogous to the try/finally pattern userland used before .as() locking existed.

Repro: register any .as(name) scheduled job whose handler throw new Error(...) before the first await. Wait through the next scheduled fire time. Observe: no JobLastRun row for the second tick, no PipelineLog, and SELECT STATUS FROM CDS_OUTBOX_MESSAGES WHERE TASK='<name>' still shows processing from the first tick.

Owner

Once someone with SAP Support access has bandwidth, paste the draft into launchpad.support.sap.com and link the incident number back to this issue.

Priority: medium — we have the workaround. But it's the kind of framework quirk that will bite every other CAP 10 project that has scheduled jobs, so worth surfacing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BlockedWaiting on someone or something elseenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions