heroagent/pkg/servers/heroagent/jobs.go
2025-05-24 07:24:17 +04:00

132 lines
3.2 KiB
Go

package heroagent
import (
"context"
"fmt"
"log"
"sync"
"time"
)
// JobStatus represents the status of a job
type JobStatus string
const (
// JobStatusNew indicates a newly created job
JobStatusNew JobStatus = "new"
// JobStatusActive indicates a job that is currently being processed
JobStatusActive JobStatus = "active"
// JobStatusError indicates a job that encountered an error
JobStatusError JobStatus = "error"
// JobStatusDone indicates a job that has been completed successfully
JobStatusDone JobStatus = "done"
)
// Job represents a job to be processed
type Job struct {
JobID uint32 `json:"jobid"`
Topic string `json:"topic"`
Params string `json:"params"`
Status JobStatus `json:"status"`
TimeScheduled int64 `json:"time_scheduled"`
TimeStart int64 `json:"time_start"`
TimeEnd int64 `json:"time_end"`
Error string `json:"error"`
Result string `json:"result"`
}
// RedisConnection wraps Redis connection details
type RedisConnection struct {
TCPPort int
UnixSocketPath string
}
// JobWorker represents a worker that processes jobs
type JobWorker struct {
id int
jobMgr *JobManager
ctx context.Context
wg *sync.WaitGroup
}
// run is the main worker loop
func (w *JobWorker) run() {
log.Printf("Worker %d started", w.id)
ticker := time.NewTicker(w.jobMgr.config.QueuePollInterval)
defer ticker.Stop()
for {
select {
case <-w.ctx.Done():
log.Printf("Worker %d stopping", w.id)
return
case <-ticker.C:
// Check for jobs in Redis
if err := w.checkForJobs(); err != nil {
log.Printf("Worker %d error checking for jobs: %v", w.id, err)
}
}
}
}
// checkForJobs checks for jobs in Redis
func (w *JobWorker) checkForJobs() error {
// Get list of queues
queues, err := w.jobMgr.redisMgr.ListQueues()
if err != nil {
return fmt.Errorf("failed to list queues: %w", err)
}
// Check each queue for jobs
for _, topic := range queues {
// Try to fetch a job from the queue
job, err := w.jobMgr.redisMgr.FetchNextJob(topic)
if err != nil {
// If queue is empty, continue to next queue
continue
}
// Process the job
if err := w.processJob(job); err != nil {
log.Printf("Error processing job %d: %v", job.JobID, err)
}
// Only process one job at a time
return nil
}
return nil
}
// processJob processes a job
func (w *JobWorker) processJob(job *Job) error {
log.Printf("Worker %d processing job %d", w.id, job.JobID)
// Update job status to active
job.Status = JobStatusActive
job.TimeStart = time.Now().Unix()
// Update job in both OurDB and Redis
if err := w.jobMgr.updateJobInBothStores(job); err != nil {
return fmt.Errorf("failed to update job status: %w", err)
}
// Simulate job processing
// In a real implementation, this would execute the job based on its parameters
time.Sleep(1 * time.Second)
// Complete the job
job.Status = JobStatusDone
job.TimeEnd = time.Now().Unix()
job.Result = fmt.Sprintf("Job %d processed successfully", job.JobID)
// Update job in OurDB and remove from Redis
if err := w.jobMgr.completeJobProcessing(job); err != nil {
return fmt.Errorf("failed to complete job: %w", err)
}
log.Printf("Worker %d completed job %d", w.id, job.JobID)
return nil
}