Skip to Content
⚠️Active Development Notice: TimeTiles is under active development. Information may be placeholder content or not up-to-date.

Background Jobs

TimeTiles uses Payload CMS’s built-in job queue and workflow system for all asynchronous processing. Jobs and workflows are defined in lib/jobs/ and registered in lib/config/payload-shared-config.ts.

Key Behavior

  • Auto-deletion: Completed jobs are automatically deleted (deleteJobOnComplete: true)
  • Workflow-based orchestration: The ingestion pipeline uses 4 Payload Workflows, queued by collection afterChange hooks
  • 3-queue architecture: ingest (user-facing workflows), default (trigger jobs), maintenance (scheduled system jobs)
  • Production: 1 Docker worker container per queue via pnpm payload jobs:run --cron --queue <name>
  • Development: autoRun processes all queues within the Next.js process

Ingest Workflows

The ingestion pipeline is orchestrated by 4 Payload Workflows. Each workflow sequences multiple task handlers into a linear pipeline. See Data Ingestion Pipeline for detailed stage documentation.

WorkflowTriggerPipeline
manual-ingestingest-files afterChange hookdataset-detection, then per-sheet: analyze, detect-schema, validate, create-schema-version, geocode, create-events
scheduled-ingestschedule-manager joburl-fetch, dataset-detection, then per-sheet pipeline
scraper-ingestschedule-manager jobscraper-execution, dataset-detection, then per-sheet pipeline
ingest-processingest-jobs afterChange hook (NEEDS_REVIEW approval)create-schema-version, geocode, create-events

All ingest workflows run on the ingest queue with per-resource concurrency keys (e.g., file:{id}, sched:{id}).

Error Model

  • Throw for transient failures — Payload retries the task
  • Return { needsReview: true } for human review — pipeline pauses for that sheet
  • Return data for success — pipeline continues to next task
  • Multi-sheet files use Promise.allSettled with per-sheet try/catch; individual sheet failures do not block other sheets

Ingest Task Handlers

These task handlers are composed by the workflows above. They are not queued individually.

TaskPurpose
dataset-detectionParse file, create ingestion jobs per sheet
analyze-duplicatesFind internal/external duplicate rows
schema-detectionBuild progressive JSON Schema from data
validate-schemaCompare detected vs existing schema
create-schema-versionPersist approved schema version
geocode-batchGeocode unique locations via providers
create-events-batchCreate event records in database
url-fetchDownload file from URL for scheduled ingest
scraper-executionRun scraper in Podman container

System Jobs

System jobs use Payload’s native schedule property for cron-based scheduling.

JobQueueSchedule
schedule-managerdefaultEvery minute
quota-resetmaintenanceDaily midnight
cache-cleanupmaintenanceEvery 6 hours
schema-maintenancemaintenanceDaily 3:00 AM
audit-log-ip-cleanupmaintenanceDaily 4:00 AM
execute-account-deletionmaintenanceDaily 2:00 AM
data-export-cleanupmaintenanceHourly
cleanup-stuck-scheduled-ingestsmaintenanceHourly
cleanup-stuck-scrapersmaintenanceHourly

Standalone Task Jobs

These are queued on demand (not scheduled):

JobPurposeTrigger
scraper-repo-syncSync scraper manifest from Git repoAdmin action
data-exportGenerate ZIP archive of user dataUser request

Adding a New Job

  1. Create handler in lib/jobs/handlers/my-job.ts
  2. Export job config from lib/jobs/ingest-jobs.ts
  3. Add to ALL_JOBS array in lib/config/payload-shared-config.ts
  4. If it is a workflow task, add it to the appropriate workflow in lib/jobs/workflows/
  5. Create migration if the job needs new fields

Testing Jobs

See Integration Testing Patterns for job testing. Key points:

  • Query pending jobs before running (completedAt: { exists: false })
  • Verify side effects after running (not job records — they’re deleted)
  • Use describe.sequential() for tests that interact with the job queue
  • Use a drain loop with payload.jobs.run() to process chained workflow tasks in tests
Last updated on