Skip to content

Migration Calculator

Thinking about switching to OJS? This page helps you estimate migration effort, understand concept mappings, and plan your transition step by step.

OJS follows familiar job queue patterns. If you’ve used any background job framework, most concepts map directly.

Your FrameworkOJS EquivalentNotes
Job / Task / Worker classJob handler functionOJS uses plain functions, not classes
perform(args) / process(data)handler(ctx)Access args via ctx.job.args
Queue nameQueue (server-managed)Named queues, "default" by default
Enqueue / dispatch / sendclient.enqueue(type, args)Explicit job type string
Batch insertclient.enqueueBatch(jobs)Atomic batch insertion
Retry policyRetry policy on job envelopePer-job, not global; server-managed
Dead letter / dead setDead letter queue (discarded state)Structured error info per attempt
Cron / recurring jobclient.registerCron(...)Built-in, no separate process needed
Workflow / pipelinechain, group, batchThree composable primitives
Middleware / pluginMiddleware chainnext() pattern, per-worker
SidekiqOJSKey Difference
include Sidekiq::Jobworker.register("type", handler)Functions, not classes
perform_async(args...)client.enqueue(type, args)Both use JSON arrays for args
sidekiq_options queue: "email"enqueue(..., queue: "email")Queue is per-enqueue, not per-class
sidekiq_retry_in blockRetry policy on the job envelopeConfigurable per-job
perform_in(5.minutes)enqueue(..., delay: "5m")Uses scheduled_at attribute
Dead set / Retries tabDead letter queue + discarded stateStructured error history
Sidekiq Pro Batchesbatch(jobs, callbacks)Free in OJS core
Sidekiq Enterprise unique jobsunique_key on job envelopeFree in OJS core
BullMQOJSKey Difference
Queue (client-side)Queue (server-managed)No client-side queue objects
queue.add(name, data)client.enqueue(type, args)data (object) → args (array)
queue.addBulk(jobs)client.enqueueBatch(jobs)Same pattern
WorkerOJSWorkerPolls for jobs, runs handlers
job.datactx.job.argsObject → array
job.attemptsMadectx.attemptOJS is 1-indexed
FlowProducerclient.workflow(chain(...))Chain, group, batch primitives
QueueSchedulerBuilt into OJS serverNo separate scheduler process
CeleryOJSKey Difference
@app.task decorator@worker.register()Decorated async functions
task.delay(args)client.enqueue(type, args)Explicit type strings
CELERY_BROKER_URL + CELERY_RESULT_BACKENDOJS_URLOne URL, not two
celery worker CLIawait worker.start()Workers embedded in your app
task.retry()Server-side retry policyNo client-side retry logic
chord(group, callback)batch(jobs, callbacks)Same fan-out/fan-in pattern
chain(task1.s(), task2.s())chain(step1, step2)Sequential pipelines
Pickle serialization (default)JSON (always)No security risk from deserialization

Regardless of which framework you’re migrating from, keep these OJS-specific patterns in mind:

OJS uses args (an ordered array) instead of payload or data (an object). This matches the function call mental model:

// OJS job envelope
{
"type": "email.send",
"args": ["user@example.com", "welcome", { "name": "Alice" }]
}

2. The server manages state, not the client

Section titled “2. The server manages state, not the client”

OJS has a dedicated server process. Queues, retries, scheduling, and lifecycle transitions are all server-side. Your SDK is a thin HTTP/gRPC client.

OJS has a formally specified 8-state lifecycle: scheduled → available → pending → active → completed | retryable | cancelled | discarded. This is more granular than most frameworks, giving you better observability.

4. Job type is a string, not a class reference

Section titled “4. Job type is a string, not a class reference”

Jobs are identified by a type string (e.g., "email.send") rather than a class name or module path. This enables cross-language processing.

Use this checklist to plan and execute your migration. Typical timeline: 1–8 weeks depending on scale.

  • Inventory your jobs — List all job types, their frequency, and their current retry/scheduling configuration.
  • Identify dependencies — Map which jobs trigger other jobs (workflows, chains, callbacks).
  • Choose your OJS backend — Redis (low-latency), PostgreSQL (transactional), NATS (cloud-native), Kafka (streaming), or SQS (AWS-native).
  • Review the OJS core concepts — Understand the envelope format and 8-state lifecycle.
  • Deploy an OJS server — Use the Quickstart guide or Deployment guide.
  • Install the OJS SDK in your application’s language.
  • Set up monitoring — OJS exposes Prometheus metrics and supports OpenTelemetry. Connect to your existing observability stack.
  • Configure retry policies — Map your existing retry settings to OJS per-job retry policies.
  • Start with low-risk jobs — Pick 2–3 non-critical job types for the first migration.
  • Rewrite handlers — Convert job classes/decorators to OJS handler functions (see concept mappings above).
  • Update producers — Replace perform_async() / queue.add() / task.delay() with client.enqueue().
  • Run in parallel — Run both old and new systems simultaneously during migration. Use feature flags to route traffic.
  • Validate — Compare job completion rates, error rates, and latency between old and new systems.
  • Migrate remaining jobs — Once confident, migrate the rest of your job types.

Phase 4: Workflows & Advanced Features (Weeks 5–7)

Section titled “Phase 4: Workflows & Advanced Features (Weeks 5–7)”
  • Migrate workflows — Convert Sidekiq Pro batches, BullMQ flows, or Celery canvas to OJS chain/group/batch.
  • Migrate cron jobs — Replace sidekiq-cron, celery-beat, or BullMQ repeatable jobs with OJS cron registration.
  • Set up unique jobs — Replace uniqueness plugins with OJS’s built-in unique_key support.
  • Configure rate limiting — If applicable, set up OJS per-queue rate limiting.
  • Drain the old queue — Let remaining jobs complete on the old system.
  • Remove old framework dependencies — Uninstall Sidekiq/BullMQ/Celery and related gems/packages.
  • Update deployment config — Remove old queue workers/processes from your deployment.
  • Document the new architecture — Update runbooks, on-call guides, and dashboards.

Use the interactive calculator below to estimate your potential cost savings.

Your current setup

100,000
5

Estimated comparison

Current estimated cost
$850/mo
Temporal Cloud: ~$0.00025/action × 100K jobs/day
OJS self-hosted cost
$120/mo
ECS Fargate (2 vCPU) + RDS PostgreSQL (db.t3.medium)
Annual savings
$8,760
Multi-language bonus: Eliminate 1 additional job queue library
Migration effort: ~2-4 weeks with OJS migration tools
OJS license cost: Free forever (Apache 2.0)
SystemCost Model
Temporal CloudBase fee (~$200/mo) + ~$0.00025 per action
Sidekiq Pro$1,950/year license + Redis hosting
Sidekiq Enterprise$3,950/year license + Redis hosting
BullMQ Pro~$50/mo license + Redis hosting
CustomEstimated maintenance overhead per engineer

OJS runs on infrastructure you likely already have. Typical costs:

ProviderComputeDatabaseTotal
AWSECS Fargate (2 vCPU, 4GB): ~$70/moRDS PostgreSQL (db.t3.medium): ~$50/mo~$120/mo
GCPCloud Run: ~$60/moCloud SQL: ~$45/mo~$105/mo
AzureContainer Instances: ~$65/moAzure Database: ~$55/mo~$120/mo
  • Migration engineering time — Typically 1–8 weeks depending on scale (see checklist above)
  • Training time — OJS follows familiar patterns; most teams are productive within days
  • Monitoring costs — OJS exports Prometheus metrics natively; use your existing stack