Skip to content
This repository was archived by the owner on Apr 9, 2026. It is now read-only.

Commit d155fc7

Browse files
committed
fix(reprocessing-worker): fixes
1 parent c6c71be commit d155fc7

1 file changed

Lines changed: 84 additions & 29 deletions

File tree

reprocessing-worker/main.go

Lines changed: 84 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,66 @@ func main() {
122122
// Update status to running
123123
updateWorkerStatus(dbpool, config, &WorkerStatus{}, "running")
124124

125+
// Get retry attempts from config (default: 3)
126+
retryAttempts := 3
127+
if ra, ok := jobConfig["retry_attempts"].(float64); ok && ra > 0 {
128+
retryAttempts = int(ra)
129+
}
130+
131+
// Get dry run mode from config (default: false)
132+
dryRun := false
133+
if dr, ok := jobConfig["dry_run"].(bool); ok {
134+
dryRun = dr
135+
}
136+
137+
if dryRun {
138+
fmt.Printf("DRY RUN MODE: Messages will be processed but NOT sent to Kafka\n")
139+
}
140+
125141
// Process assigned files
126142
status := &WorkerStatus{}
127143
for _, fileItem := range myFiles {
128-
fmt.Printf("Processing file: %s\n", fileItem.Path)
129-
err := processFile(fileItem, jobConfig, producer, s3Client, streams, config, status, dbpool)
130-
if err != nil {
131-
fmt.Fprintf(os.Stderr, "Error processing file %s: %v\n", fileItem.Path, err)
132-
status.LastError = err.Error()
144+
fmt.Printf("Processing file: %s (retry attempts: %d, dry_run: %v)\n", fileItem.Path, retryAttempts, dryRun)
145+
146+
// Try processing file with retries
147+
var lastErr error
148+
for attempt := 1; attempt <= retryAttempts; attempt++ {
149+
if attempt > 1 {
150+
fmt.Printf("Retry attempt %d/%d for file: %s\n", attempt, retryAttempts, fileItem.Path)
151+
// Wait a bit before retrying (exponential backoff)
152+
time.Sleep(time.Duration(attempt-1) * time.Second)
153+
}
154+
155+
// Save current counts in case we need to rollback on failure
156+
savedTotalLines := status.TotalLines
157+
savedSuccessCount := status.SuccessCount
158+
savedErrorCount := status.ErrorCount
159+
savedSkippedCount := status.SkippedCount
160+
161+
lastErr = processFile(fileItem, jobConfig, producer, s3Client, streams, config, status, dbpool, dryRun)
162+
if lastErr == nil {
163+
// Success - break retry loop
164+
break
165+
}
166+
167+
// Failed - rollback the counts from partial processing
168+
// We want to retry from a clean state
169+
status.TotalLines = savedTotalLines
170+
status.SuccessCount = savedSuccessCount
171+
status.ErrorCount = savedErrorCount
172+
status.SkippedCount = savedSkippedCount
173+
174+
// Log the error but continue with retry
175+
fmt.Fprintf(os.Stdout, "Error processing file %s (attempt %d/%d): %v\n", fileItem.Path, attempt, retryAttempts, lastErr)
176+
}
177+
178+
// If all retries failed, record the error
179+
if lastErr != nil {
180+
fmt.Fprintf(os.Stderr, "Failed to process file %s after %d attempts: %v\n", fileItem.Path, retryAttempts, lastErr)
181+
status.LastError = lastErr.Error()
182+
status.ErrorCount++
133183
}
184+
134185
status.ProcessedFiles++
135186
status.ProcessedBytes += fileItem.Size
136187

@@ -210,9 +261,9 @@ func loadConfig() (*WorkerConfig, error) {
210261
func initKafkaProducer(config *WorkerConfig) (*kafkabase.Producer, error) {
211262
producerConfig := &kafka.ConfigMap{
212263
"bootstrap.servers": config.KafkaBootstrapServers,
213-
"queue.buffering.max.messages": 10000,
214-
"batch.size": 65535,
215-
"linger.ms": 100,
264+
"queue.buffering.max.messages": 200000,
265+
"batch.size": 1048576,
266+
"linger.ms": 1000,
216267
"compression.type": "zstd",
217268
}
218269

@@ -262,7 +313,7 @@ func selectWorkerFiles(files []FileItem, workerIndex, totalWorkers int) []FileIt
262313
return myFiles
263314
}
264315

265-
func processFile(fileItem FileItem, jobConfig map[string]interface{}, producer *kafkabase.Producer, s3Client *s3.Client, streams *Streams, config *WorkerConfig, status *WorkerStatus, dbpool *pgxpool.Pool) error {
316+
func processFile(fileItem FileItem, jobConfig map[string]interface{}, producer *kafkabase.Producer, s3Client *s3.Client, streams *Streams, config *WorkerConfig, status *WorkerStatus, dbpool *pgxpool.Pool, dryRun bool) error {
266317
status.CurrentFile = fileItem.Path
267318

268319
// Get file reader
@@ -297,7 +348,7 @@ func processFile(fileItem FileItem, jobConfig map[string]interface{}, producer *
297348
// Process lines
298349
scanner := bufio.NewScanner(fileReader)
299350
lineNum := int64(0)
300-
batchSize := 100 // Default batch size
351+
batchSize := 1000 // Default batch size
301352
if bs, ok := jobConfig["batch_size"].(float64); ok {
302353
batchSize = int(bs)
303354
}
@@ -328,7 +379,7 @@ func processFile(fileItem FileItem, jobConfig map[string]interface{}, producer *
328379
batch = append(batch, message)
329380

330381
if len(batch) >= batchSize {
331-
if err := sendBatch(batch, producer, config.KafkaTopicName, jobConfig, streams, status); err != nil {
382+
if err := sendBatch(batch, producer, config.KafkaTopicName, jobConfig, streams, status, dryRun); err != nil {
332383
fmt.Fprintf(os.Stderr, "Failed to send batch: %v\n", err)
333384
status.ErrorCount += int64(len(batch))
334385
} else {
@@ -337,15 +388,15 @@ func processFile(fileItem FileItem, jobConfig map[string]interface{}, producer *
337388
batch = batch[:0]
338389
}
339390

340-
// Update status periodically (every 1000 lines)
341-
if lineNum%1000 == 0 {
391+
// Update status periodically (every 100000 lines)
392+
if lineNum%100000 == 0 {
342393
updateWorkerStatus(dbpool, config, status, "running")
343394
}
344395
}
345396

346397
// Send remaining batch
347398
if len(batch) > 0 {
348-
if err := sendBatch(batch, producer, config.KafkaTopicName, jobConfig, streams, status); err != nil {
399+
if err := sendBatch(batch, producer, config.KafkaTopicName, jobConfig, streams, status, dryRun); err != nil {
349400
fmt.Fprintf(os.Stderr, "Failed to send final batch: %v\n", err)
350401
status.ErrorCount += int64(len(batch))
351402
} else {
@@ -379,17 +430,16 @@ func downloadS3File(s3Client *s3.Client, s3Path string) (io.ReadCloser, error) {
379430
func shouldProcessMessage(message map[string]interface{}, jobConfig map[string]interface{}) bool {
380431
// Apply filters from job config
381432
if streamIds, ok := jobConfig["stream_ids"].([]interface{}); ok && len(streamIds) > 0 {
382-
origin, ok := message["origin"].(map[string]interface{})
433+
origin, ok := message["origin"].(*jsonorder.OrderedMap[string, interface{}])
383434
if !ok {
384435
return false
385436
}
386437

387-
sourceId, _ := origin["source_id"].(string)
388-
slug, _ := origin["slug"].(string)
389-
438+
sourceId := origin.GetS("sourceId")
439+
slug := origin.GetS("slug")
390440
found := false
391441
for _, sid := range streamIds {
392-
if sidStr, ok := sid.(string); ok && (sidStr == sourceId || sidStr == slug) {
442+
if sid == sourceId || sid == slug {
393443
found = true
394444
break
395445
}
@@ -398,10 +448,9 @@ func shouldProcessMessage(message map[string]interface{}, jobConfig map[string]i
398448
return false
399449
}
400450
}
401-
402451
// Add date filtering if needed
403-
if dateFrom, ok := jobConfig["date_from"].(string); ok && dateFrom != "" {
404-
messageCreated, _ := message["message_created"].(string)
452+
if dateFrom, ok := jobConfig["date_from"].(string); ok && dateFrom != "" && dateFrom != "0001-01-01T00:00:00Z" {
453+
messageCreated, _ := message["messageCreated"].(string)
405454
if messageCreated != "" {
406455
msgTime, err := time.Parse(time.RFC3339Nano, messageCreated)
407456
if err == nil {
@@ -413,8 +462,8 @@ func shouldProcessMessage(message map[string]interface{}, jobConfig map[string]i
413462
}
414463
}
415464

416-
if dateTo, ok := jobConfig["date_to"].(string); ok && dateTo != "" {
417-
messageCreated, _ := message["message_created"].(string)
465+
if dateTo, ok := jobConfig["date_to"].(string); ok && dateTo != "" && dateTo != "0001-01-01T00:00:00Z" {
466+
messageCreated, _ := message["messageCreated"].(string)
418467
if messageCreated != "" {
419468
msgTime, err := time.Parse(time.RFC3339Nano, messageCreated)
420469
if err == nil {
@@ -425,11 +474,10 @@ func shouldProcessMessage(message map[string]interface{}, jobConfig map[string]i
425474
}
426475
}
427476
}
428-
429477
return true
430478
}
431479

432-
func sendBatch(batch []map[string]interface{}, producer *kafkabase.Producer, topic string, jobConfig map[string]interface{}, streams *Streams, status *WorkerStatus) error {
480+
func sendBatch(batch []map[string]interface{}, producer *kafkabase.Producer, topic string, jobConfig map[string]interface{}, streams *Streams, status *WorkerStatus, dryRun bool) error {
433481
for _, message := range batch {
434482
// Get connection IDs from job config or repository
435483
connectionIds := []string{}
@@ -443,10 +491,10 @@ func sendBatch(batch []map[string]interface{}, producer *kafkabase.Producer, top
443491

444492
// If no connection IDs provided, look them up from repository
445493
if len(connectionIds) == 0 && streams != nil {
446-
origin, _ := message["origin"].(map[string]interface{})
494+
origin, _ := message["origin"].(*jsonorder.OrderedMap[string, interface{}])
447495
if origin != nil {
448-
sourceId, _ := origin["source_id"].(string)
449-
slug, _ := origin["slug"].(string)
496+
sourceId := origin.GetS("sourceId")
497+
slug := origin.GetS("slug")
450498
streamId := sourceId
451499
if streamId == "" {
452500
streamId = slug
@@ -483,6 +531,13 @@ func sendBatch(batch []map[string]interface{}, producer *kafkabase.Producer, top
483531
return err
484532
}
485533

534+
// Skip Kafka send if in dry run mode
535+
if dryRun {
536+
// In dry run mode, just validate that we can marshal the message
537+
// but don't actually send to Kafka
538+
continue
539+
}
540+
486541
err = producer.ProduceAsync(topic, uuid.New(), messageBytes, headers, kafka.PartitionAny, "", false)
487542
if err != nil {
488543
return err

0 commit comments

Comments
 (0)