Preview
Preview Feature — This feature is currently in preview and under active development. APIs and functionality may change. We recommend testing thoroughly before using in production.
Job Management
Learn how to monitor, track, pause, resume, and cancel scheduled jobs in Ductape.
Job Lifecycle
Jobs in Ductape go through several states during their lifecycle:
scheduled → queued → running → completed
↘ ↘
failed cancelled
Job States
| State | Description |
|---|---|
scheduled | Job is scheduled but not yet queued for execution |
queued | Job is in the execution queue waiting to run |
running | Job is currently executing |
completed | Job finished successfully |
failed | Job failed after all retry attempts |
cancelled | Job was manually cancelled |
paused | Recurring job is paused and won't run until resumed |
Fetching Jobs
Get a Specific Job
// Fetch job by ID
const job = await ductape.jobs.get('job_abc123');
console.log('Job ID:', job.id);
console.log('Status:', job.status);
console.log('Scheduled At:', new Date(job.scheduled_at));
console.log('Recurring:', job.recurring);
List Jobs
// List all jobs
const jobs = await ductape.jobs.list();
// List with filters
const scheduledJobs = await ductape.jobs.list({
status: 'scheduled',
limit: 100,
offset: 0
});
// List jobs by product
const productJobs = await ductape.jobs.list({
product: 'my-app',
env: 'prd'
});
// List recurring jobs only
const recurringJobs = await ductape.jobs.list({
recurring: true
});
List Options
interface IJobListOptions {
status?: 'scheduled' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused';
recurring?: boolean;
product?: string;
env?: string;
namespace?: string; // 'actions', 'features', 'notifications', etc.
limit?: number;
offset?: number;
from?: number | string; // Start date filter
to?: number | string; // End date filter
}
Job Information
Job Object Structure
interface IJob {
id: string;
status: JobStatus;
namespace: string; // Which dispatch created it
product: string;
env: string;
// Schedule info
scheduled_at: number;
started_at?: number;
completed_at?: number;
// Recurring info
recurring: boolean;
cron?: string;
every?: number;
next_run_at?: number;
execution_count: number;
limit?: number;
end_date?: number;
// Retry info
retries: number;
retry_count: number;
last_error?: string;
// Payload
input: Record<string, unknown>;
result?: Record<string, unknown>;
// Metadata
created_at: number;
updated_at: number;
}
Check Job Status
const job = await ductape.jobs.get('job_abc123');
if (job.status === 'completed') {
console.log('Job completed successfully');
console.log('Result:', job.result);
} else if (job.status === 'failed') {
console.log('Job failed after', job.retry_count, 'attempts');
console.log('Last error:', job.last_error);
} else if (job.status === 'running') {
console.log('Job is currently executing');
}
Cancelling Jobs
Cancel a Single Job
// Cancel a scheduled job
await ductape.jobs.cancel('job_abc123');
// Cancel returns the updated job
const cancelledJob = await ductape.jobs.cancel('job_abc123');
console.log('Status:', cancelledJob.status); // 'cancelled'
Cancel Multiple Jobs
// Cancel all scheduled jobs for a product
const cancelled = await ductape.jobs.cancelMany({
product: 'my-app',
status: 'scheduled'
});
console.log('Cancelled jobs:', cancelled.count);
Cancel with Reason
await ductape.jobs.cancel('job_abc123', {
reason: 'Campaign ended early'
});
Pausing and Resuming Jobs
Pause a Recurring Job
// Pause a recurring job - it won't run until resumed
await ductape.jobs.pause('job_abc123');
const job = await ductape.jobs.get('job_abc123');
console.log('Status:', job.status); // 'paused'
Resume a Paused Job
// Resume a paused job
await ductape.jobs.resume('job_abc123');
const job = await ductape.jobs.get('job_abc123');
console.log('Status:', job.status); // 'scheduled'
console.log('Next run:', new Date(job.next_run_at));
Pause All Jobs for a Product
// Pause all recurring jobs for maintenance
await ductape.jobs.pauseMany({
product: 'my-app',
recurring: true
});
// Resume after maintenance
await ductape.jobs.resumeMany({
product: 'my-app',
status: 'paused'
});
Rescheduling Jobs
Reschedule a Job
// Reschedule a job to a new time
await ductape.jobs.reschedule('job_abc123', {
start_at: Date.now() + 7200000 // 2 hours from now
});
Update Recurring Schedule
// Change the cron schedule for a recurring job
await ductape.jobs.reschedule('job_abc123', {
cron: '0 10 * * *', // Change to 10 AM
tz: 'America/New_York'
});
// Change interval for interval-based job
await ductape.jobs.reschedule('job_abc123', {
every: 7200000 // Change to every 2 hours
});
Monitoring Job Execution
Get Execution History
// Get execution history for a recurring job
const history = await ductape.jobs.getHistory('job_abc123', {
limit: 10
});
for (const execution of history.executions) {
console.log(`Run ${execution.number}:`);
console.log(' Started:', new Date(execution.started_at));
console.log(' Duration:', execution.duration_ms, 'ms');
console.log(' Status:', execution.status);
if (execution.error) {
console.log(' Error:', execution.error);
}
}
Execution History Response
interface IJobHistory {
job_id: string;
total_executions: number;
successful_executions: number;
failed_executions: number;
executions: IJobExecution[];
}
interface IJobExecution {
number: number;
started_at: number;
completed_at?: number;
duration_ms?: number;
status: 'completed' | 'failed';
error?: string;
result?: Record<string, unknown>;
}
Get Job Statistics
// Get statistics for all jobs in a product
const stats = await ductape.jobs.getStats({
product: 'my-app',
env: 'prd'
});
console.log('Total jobs:', stats.total);
console.log('Scheduled:', stats.scheduled);
console.log('Running:', stats.running);
console.log('Completed:', stats.completed);
console.log('Failed:', stats.failed);
console.log('Success rate:', stats.success_rate);
Retrying Failed Jobs
Retry a Failed Job
// Retry a failed job immediately
await ductape.jobs.retry('job_abc123');
// Retry with delay
await ductape.jobs.retry('job_abc123', {
delay: 60000 // Wait 1 minute before retrying
});
Retry Multiple Failed Jobs
// Retry all failed jobs from today
await ductape.jobs.retryMany({
status: 'failed',
from: new Date().setHours(0, 0, 0, 0)
});
Deleting Jobs
Delete Completed Jobs
// Delete a specific job
await ductape.jobs.delete('job_abc123');
// Delete old completed jobs
await ductape.jobs.deleteMany({
status: 'completed',
to: Date.now() - 30 * 24 * 60 * 60 * 1000 // Older than 30 days
});
Job Events and Webhooks
You can configure webhooks to receive notifications about job events:
// Configure job completion webhook
await ductape.jobs.setWebhook({
url: 'https://api.example.com/webhooks/jobs',
events: ['job.completed', 'job.failed'],
secret: 'webhook-secret-key'
});
Webhook Events
| Event | Description |
|---|---|
job.scheduled | Job was scheduled |
job.started | Job execution started |
job.completed | Job completed successfully |
job.failed | Job failed (after retries) |
job.cancelled | Job was cancelled |
job.paused | Recurring job was paused |
job.resumed | Paused job was resumed |
Webhook Payload
interface IJobWebhookPayload {
event: string;
timestamp: number;
job: {
id: string;
status: string;
namespace: string;
product: string;
env: string;
scheduled_at: number;
execution_count?: number;
error?: string;
};
}
Best Practices
1. Use Meaningful Job IDs
When retrieving the job ID from dispatch, store it with context:
const job = await ductape.actions.dispatch({ ... });
// Store job reference with context
await saveJobReference({
job_id: job.job_id,
purpose: 'welcome_email',
user_id: userId,
created_at: Date.now()
});
2. Monitor Critical Jobs
Set up alerts for critical job failures:
const job = await ductape.jobs.get('job_abc123');
if (job.status === 'failed') {
await alertOps({
message: `Critical job ${job.id} failed: ${job.last_error}`,
severity: 'high'
});
}
3. Clean Up Old Jobs
Regularly clean up completed jobs to manage storage:
// Run monthly cleanup
await ductape.jobs.deleteMany({
status: ['completed', 'cancelled', 'failed'],
to: Date.now() - 90 * 24 * 60 * 60 * 1000 // Older than 90 days
});
4. Use Pausing for Maintenance
Pause jobs during system maintenance:
// Before maintenance
await ductape.jobs.pauseMany({ product: 'my-app' });
// ... perform maintenance ...
// After maintenance
await ductape.jobs.resumeMany({
product: 'my-app',
status: 'paused'
});
See Also
- Scheduling Jobs - Create and schedule jobs
- Retry Strategies - Handle job failures
- Examples - Real-world job management patterns