Enhance TED scraper and worker initialization with startup catch-up logic
continuous-integration/drone/push Build is passing

- Introduced a mutex to ensure only one TED scraper run executes at a time, preventing concurrent executions during startup and scheduled runs.
- Implemented a mechanism to check if today's TED scrape has already been completed during startup, logging appropriate messages for both completed and new runs.
- Added startup catch-up logic for tender translations and unprocessed notices, ensuring that any missed tasks are executed without blocking the application startup.

This update improves the reliability and efficiency of the TED scraper and worker processes, ensuring that all necessary tasks are completed after a server restart.
This commit is contained in:
Mazyar
2026-06-28 00:05:10 +03:30
parent 69445130ce
commit 582f8b5c02
6 changed files with 197 additions and 8 deletions
+58 -7
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"sync"
"time"
"tm/internal/notice"
"tm/pkg/config"
@@ -14,6 +15,9 @@ import (
"tm/ted"
)
// tedScraperRunMu ensures only one TED scraper run executes at a time (startup catch-up and cron).
var tedScraperRunMu sync.Mutex
// Init Application Configuration
func InitConfig() (*Config, error) {
conf, err := config.LoadConfig(".", &Config{})
@@ -101,22 +105,64 @@ func InitTEDScraper(config Config, mongoManager *mongo.ConnectionManager, appLog
// start TED scraper job with error handling
scheduler := schedule.NewCronScheduler(appLogger, mongoManager, noticeRepo)
dailyJobTracker := schedule.NewDailyJobTracker(mongoManager, appLogger)
runDailyScrape := func(trigger string) {
tedScraperRunMu.Lock()
defer tedScraperRunMu.Unlock()
function := func() {
ctx := context.Background()
today := time.Now().Local()
if trigger == "startup" {
completed, checkErr := dailyJobTracker.IsCompleted(ctx, schedule.TEDScraperJobName, today)
if checkErr != nil {
appLogger.Error("Failed to check daily scrape completion status", map[string]interface{}{
"error": checkErr.Error(),
"date": today.Format("02/01/2006"),
})
} else if completed {
appLogger.Info("Startup catch-up skipped: today's TED scrape already completed", map[string]interface{}{
"date": today.Format("02/01/2006"),
})
return
}
appLogger.Info("Running startup catch-up for today's TED scrape", map[string]interface{}{
"date": today.Format("02/01/2006"),
})
} else {
appLogger.Info("Running scheduled TED scrape", map[string]interface{}{
"date": today.Format("02/01/2006"),
})
}
err := tedScraper.Run(ctx, nil, nil)
if err != nil {
appLogger.Error("Scheduled scraper run failed", map[string]interface{}{
"error": err.Error(),
appLogger.Error("TED scraper run failed", map[string]interface{}{
"trigger": trigger,
"date": today.Format("02/01/2006"),
"error": err.Error(),
})
// Don't exit - continue running for next scheduled execution
} else {
appLogger.Info("Scheduled scraper run completed successfully", map[string]interface{}{})
return
}
if markErr := dailyJobTracker.MarkCompleted(ctx, schedule.TEDScraperJobName, today); markErr != nil {
appLogger.Error("Failed to mark daily scrape as completed", map[string]interface{}{
"trigger": trigger,
"date": today.Format("02/01/2006"),
"error": markErr.Error(),
})
}
appLogger.Info("TED scraper run completed successfully", map[string]interface{}{
"trigger": trigger,
"date": today.Format("02/01/2006"),
})
}
err := scheduler.AddJob(schedule.Job{
Name: "TED Scraper Job",
Func: function,
Func: func() { runDailyScrape("scheduled") },
Expr: config.TED.ScrapingInterval,
})
if err != nil {
@@ -126,6 +172,11 @@ func InitTEDScraper(config Config, mongoManager *mongo.ConnectionManager, appLog
return nil
}
// After a server restart, resume today's scrape if it was interrupted or never ran.
go func() {
runDailyScrape("startup")
}()
// Start the scheduler
scheduler.Start()