TransactionalStore

Optional capability: enqueue participating in a caller-supplied transaction of type TX. The job is visible to workers if and only if the transaction commits. The transaction is passed explicitly because thread-local transaction state does not survive coroutine thread hops.

Functions

Link copied to clipboard
abstract suspend fun claim(queues: List<QueueName>, kinds: Set<String>, limit: Int, lease: Duration, worker: WorkerId): List<ClaimedJob>

Claims up to limit due jobs for worker under a lease of lease, scanning queues in list order (declared order is priority) and returning only jobs whose kind is in kinds. Rows of any other kind are left untouched: not claimed, not transitioned, not counted against limit. An empty kinds returns an empty list. The filter is what lets a worker that does not bind a kind (an older version mid-rollout, a fleet that drains a shared queue for a subset of kinds) leave that job for a worker that does.

Link copied to clipboard
abstract suspend fun completeFire(scheduleId: String, fence: Long, runs: List<NewJob>, nextFireAt: Instant?): List<JobId>?

Applies one claimed fire ATOMICALLY: inserts runs with enqueue semantics (including the uniqueKey rule), advances the schedule's nextFireAt to nextFireAt (null makes the schedule dormant), sets lastFiredAt to the store's clock, and releases the lease, all in one transaction. Compare-and-set on fence: returns null and changes NOTHING, inserts no runs, when the stored fence differs. On success returns the run ids in runs order, existing ids on uniqueKey dedup, exactly like enqueue. An empty runs is a legal completion (a misfire handled with Skip, for example) and returns an empty list.

Link copied to clipboard
abstract suspend fun dueSchedules(ids: Set<String>, limit: Int, lease: Duration, worker: WorkerId): List<ScheduleFire>

Claims up to limit due schedules for worker under a lease of lease, soonest next-fire first. A schedule is due when its stored nextFireAt is non-null and not in the future by the STORE's own clock; no SPI method accepts caller-supplied time for comparisons, because the caller's clock is never authoritative. Only rows whose id is in ids are returned; other rows are left untouched, so a node only ever fires schedules it holds a definition for (the schedule analogue of claim's kind filter). An empty ids returns an empty list.

Link copied to clipboard
abstract suspend fun enqueue(jobs: List<NewJob>): List<JobId>

Persists jobs and returns their ids in the same order. A job whose NewJob.runAt is not in the future by the store's clock is stored as JobState.Enqueued (due, visible to workers); a future NewJob.runAt is stored as JobState.Scheduled (waiting for its time). Promoting Scheduled to Enqueued when that time arrives is the store's concern; the runtime never depends on it, because claim decides due-ness by time, not state. A uniqueKey conflict with a non-terminal job returns that job's id (see interface KDoc).

abstract suspend fun enqueue(tx: TX, jobs: List<NewJob>): List<JobId>
Link copied to clipboard
abstract suspend fun heartbeat(ids: List<JobId>, worker: WorkerId, extend: Duration)

Extends the lease of running jobs. Ignores ids whose lease this worker no longer holds, including the worker's OWN ids whose lease has already expired: an expired lease is not held, even by its former owner. Reviving an expired job goes through claim, never through heartbeat.

Link copied to clipboard
abstract suspend fun sweep(retention: RetentionPolicy)

Deletes terminal runs past their retention. Runs as a built-in recurring job.

Link copied to clipboard
abstract suspend fun transition(id: JobId, from: JobState, to: JobState, fence: Long? = null, error: JobError? = null): Boolean

Compare-and-set state transition. See interface KDoc for fence semantics.

Link copied to clipboard
abstract suspend fun upsertSchedules(schedules: List<ScheduleSpec>)

Registers or reconciles code-defined schedules, once per runtime start. Per spec: an unknown ScheduleSpec.id inserts the row with nextFireAt = fireAt; a known id with an EQUAL fingerprint updates nothing (timing state is preserved across restarts); a known id with a DIFFERENT fingerprint overwrites kind, fingerprint and description, sets nextFireAt = fireAt, clears any lease, AND bumps the fence: a claim taken under the old definition must not be completable, because its runs and next fire time were computed from a definition that no longer exists. Last writer wins; concurrent upserts from nodes running different code versions are expected during rolling deploys. Upsert never deletes: a schedule removed from code keeps its row and simply stops being fired, because no node passes its id to dueSchedules.