Skip to content

Separate Job from Worker#15

Open
reneklacan wants to merge 17 commits intomainfrom
reneklacan/separate-job-worker
Open

Separate Job from Worker#15
reneklacan wants to merge 17 commits intomainfrom
reneklacan/separate-job-worker

Conversation

@reneklacan
Copy link
Copy Markdown
Member

@reneklacan reneklacan commented Mar 25, 2026

Summary

Introduces Job/Worker separation — job data (the serialized payload) is now defined in a separate struct from the worker (the processing logic).

  • Job trait (data) + Worker<Args> trait (processing) replace the old combined Worker trait
  • JobContext replaces Context<T> — no more generic context parameter
  • ContextValue::new(x) replaces Context::value(x)
  • FromContext trait for injecting app state into workers (auto-derived for unit and single-field structs)
  • #[oxanus(job = MyJobType)] attribute on #[derive(oxanus::Worker)] — defaults to {WorkerName}Job by convention
  • Cron queue is now required at compile time — previously compiled but panicked at runtime when omitted
  • Processable/BoxedProcessable removed from public API (internal only)
  • job_envelope::Job struct renamed to JobData to avoid collision with the new Job trait
  • Migration guide added in MIGRATION.md

Migration

0.9 0.10
Worker trait combines data + processing Job trait (data) + Worker<Args> trait (processing)
Context<T> (generic over app context) JobContext (no generic)
Context::value(x) ContextValue::new(x)
#[derive(Serialize, Deserialize, oxanus::Worker)] on one struct #[derive(Serialize, Deserialize)] on job, #[derive(oxanus::Worker)] on worker
config.register_worker::<W>() config.register_worker::<W, J>()
storage.enqueue(queue, MyWorker { .. }) storage.enqueue(queue, MyJob { .. })

See MIGRATION.md for the full step-by-step guide.

Test plan

  • All 38 unit + integration tests pass
  • Clippy clean, fmt clean
  • All examples updated and compile
  • Benchmarks updated

Note

High Risk
Large, breaking refactor across core scheduling/execution, macro expansion, and public APIs (enqueue, register_worker, context), with potential for subtle runtime/migration regressions despite broad test updates.

Overview
Introduces a breaking Job/Worker split: job payloads now implement a new Job trait, processing logic moves to Worker<Args>, and process receives (&Args, &JobContext) instead of &Context<T>.

Updates the runtime and registration pipeline to construct workers from app state via FromContext (new ContextValue::new wrapper), register workers as register_worker::<W, A>(), and enqueue only job structs (with job_envelope::Job renamed to JobData). Cron configuration is tightened so queue is required at compile time and cron args must deserialize from {}.

Refreshes macros, docs, examples, benches, web UI, and tests for the new API, and adds MIGRATION.md plus version bumps to 0.10.0.

Reviewed by Cursor Bugbot for commit f07f433. Bugbot is set up for automated code reviews on this repo. Configure here.

Split the combined Worker struct into separate Job (serialized payload)
and Worker (processing logic) types. Workers now receive app context via
FromContext trait instead of through the generic Context<T> parameter.
Comment thread oxanus-macros/src/worker.rs
Comment thread oxanus/src/worker_registry.rs
Cron jobs are enqueued with empty JSON, so the args type must be
deserializable from {}. This was previously validated at startup
via validate_cron_worker but was lost during the Job/Worker split.
Comment thread oxanus-macros/src/worker.rs Outdated
Consolidate should_resurrect to live only on the Job trait, removing the
duplicate from Worker. Worker::to_config now reads resurrect from
Args::should_resurrect() via an Args: Job bound.
Comment thread oxanus-macros/src/worker.rs
Comment thread oxanus-macros/src/worker.rs
- Skip positional placeholders (starting with digit) and deduplicate named placeholders
- Use emit_error instead of abort in FromContext expansion to allow continued compilation
Rename job_envelope::Job struct to JobData to avoid collision with the
new worker::Job trait. Also simplify the digit check in placeholder
extraction.
- Remove Processable/BoxedProcessable from public exports (internal only)
- Use Job::worker_name() instead of duplicate type_name::<W>() in config
- Remove redundant to_value(json!({})) double serialization in new_cron
- Use schedules map directly instead of building intermediate HashSet in catalog()
- Rename #[oxanus(args = ...)] attribute to #[oxanus(job = ...)]
- Default job type to {WorkerName}Job when not explicitly set
- Rename BoundJob.args field to BoundJob.job
- Remove explicit job attribute from examples/tests where convention matches
- Update MIGRATION.md and README to reflect new convention
cursor[bot]

This comment was marked as resolved.

The queue field in #[oxanus(cron(...))] was Optional, causing a runtime
panic when omitted. Making it required moves this to a compile error.
FooWorker now defaults to FooJob instead of FooWorkerJob. The macro
strips a trailing "Worker" from the struct name before appending "Job".
Allow max_retries and retry_delay to vary based on job data by passing
&job to both Worker trait methods.
…ob-worker

# Conflicts:
#	Cargo.lock
#	oxanus-web/Cargo.toml
#	oxanus/Cargo.toml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant