better-effect
better-effect-mq

Retries and timeouts

Configure durable backoff and cooperative attempt limits.

Retry.never, Retry.fixed, Retry.linear and Retry.exponential produce validated callback-free policy data. The policy is persisted with the Job; Retry.custom remains process-local and is evaluated only by the Worker.

import * as z from 'zod'
import { Codec, JobEncodeFailure, Queue, Retry } from 'better-effect-mq'
import { Schema as CoreSchema } from 'better-effect-schema'
import { Schema } from 'better-effect-schema/zod'

const Jobs = Queue.define('billing')
const ChargeCardPayloadSchema = z.object({ paymentId: z.string().min(1) })
class ChargeCardPayload extends Schema.Class<ChargeCardPayload>('app/ChargeCardPayload')(
  ChargeCardPayloadSchema
) {}
const ChargeCardPayloadCodec = Codec.standardSchema({
  schema: ChargeCardPayload,
  encode: (value) =>
    CoreSchema.encode(ChargeCardPayload, value).mapError(
      (error) => new JobEncodeFailure({ message: error.message, code: 'schema-encode' })
    )
})
const ChargeCard = Jobs.job('charge-card', {
  version: 1,
  payload: ChargeCardPayloadCodec,
  result: Codec.void,
  failure: Codec.standardSchema({ schema: z.object({ code: z.string().min(1) }) }),
  defaults: {
    attempts: 4,
    backoff: Retry.exponential({
      initialDelayMs: 100,
      factor: 2,
      maxDelayMs: 5_000,
      maxAttempts: 4
    }),
    timeoutMs: 30_000
  },
  retryable: ({ code }) => code !== 'card-blocked'
})

attempts includes the first delivery. A typed Result.err is retryable only when the Job policy says so; Job.unrecoverable(value) marks an object failure as terminal. Defects retry by default and can be disabled with retryDefects: false. Decode and encode failures are terminal because the Worker cannot safely invoke the handler or persist its result.

Timeouts abort the attempt cooperatively with JobTimeoutError. The Worker checks the deadline at the settlement submission gate, but a Promise that ignores its signal may continue running outside the supervisor. External APIs must use their own timeout and idempotency controls.

Retry and failure data are bounded JSON-safe DTOs. Causes, stacks, arbitrary exceptions and sensitive payloads do not become persisted failure fields. Use Zod 4 through better-effect-schema with Codec.standardSchema for boundary validation. The trusted JSON escape hatch is reserved for values that are already JSON-safe and need no runtime schema; it is not the default boundary codec.