Pulse supports a two-phase job pattern for complex workflows that need to spawn child tasks and aggregate results.
Jobs track their phase using JobMetadata:
type JobMetadata struct {
Phase string `json:"phase,omitempty"` // "ingest" or "aggregate"
// Other metadata fields as needed
}
if job.Metadata != nil && job.Metadata.Phase == "aggregate" {
// Aggregate phase logic
} else {
// Ingest phase logic (default)
}
Jobs maintain parent-child relationships through:
parent_job_id field in the async_ix_jobs tableLocation: pulse/async/queue.go:DeleteJobWithChildren()
When a parent job is deleted:
cancelled with reason parent job deletedRace condition protection: Before enqueueing children, the parent checks it still exists in the database, which prevents enqueueing tasks after parent deletion during execution.
Location: pulse/async/queue.go:cancelOrphanedChildren()
When a parent job completes or fails:
parent job completedBehavior:
Location: pulse/async/error.go:RetryableError()
Failed tasks can be retried automatically (max 2 retries = 3 total attempts):
retry_count and re-queues job꩜ Retry 1/2: operation failed | job:JB_abc123꩜ Max retries exceeded (2): operation failed | job:JB_abc123Database tracking: Each retry attempt updates the job record with retry count and error details, providing full audit trail.
1. Parent job starts (ingest phase)
↓
2. Creates N child jobs
↓
3. Parent pauses/waits
↓
4. Children complete
↓
5. Parent resumes (aggregate phase)
↓
6. Parent aggregates results
↓
7. Parent completes
No special configuration required. The pattern is implemented through job handler logic and JobMetadata.
Parent-child job hierarchy:
TestParentJobHierarchy - pulse/async/job_test.goTestTASBotParentJobHierarchy - pulse/async/store_test.go