refactor: implement loaderAccumulator for batch processing; streamline error handling in Consume
This commit is contained in:
@@ -13,6 +13,62 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type loaderAccumulator struct {
|
||||||
|
batchSize int
|
||||||
|
rows []models.UnknownRowValues
|
||||||
|
parents []models.BatchRef
|
||||||
|
pendingDone int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loaderAccumulator) add(batch models.Batch) {
|
||||||
|
a.rows = append(a.rows, batch.Rows...)
|
||||||
|
a.parents = append(a.parents, models.BatchRef{Id: batch.Id})
|
||||||
|
a.pendingDone++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loaderAccumulator) ready() bool {
|
||||||
|
return len(a.rows) >= a.batchSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loaderAccumulator) drainPending(wg *sync.WaitGroup) {
|
||||||
|
for range a.pendingDone {
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendLoadError(
|
||||||
|
ctx context.Context,
|
||||||
|
err error,
|
||||||
|
retryConfig config.RetryConfig,
|
||||||
|
failedBatchesCount *int32,
|
||||||
|
chErrorsOut chan<- custom_errors.JobError,
|
||||||
|
) bool {
|
||||||
|
atomic.AddInt32(failedBatchesCount, 1)
|
||||||
|
|
||||||
|
var jobErr custom_errors.JobError
|
||||||
|
if je, ok := errors.AsType[*custom_errors.JobError](err); ok {
|
||||||
|
jobErr = *je
|
||||||
|
} else {
|
||||||
|
jobErr = custom_errors.JobError{ShouldCancelJob: false, Msg: err.Error(), Prev: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return false
|
||||||
|
case chErrorsOut <- jobErr:
|
||||||
|
}
|
||||||
|
|
||||||
|
if atomic.LoadInt32(failedBatchesCount) > int32(retryConfig.MaxFailedBatchesLoad) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case chErrorsOut <- custom_errors.JobError{ShouldCancelJob: true, Msg: "Max failed batches (load) reached"}:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (gl *GenericLoader) Consume(
|
func (gl *GenericLoader) Consume(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
tableInfo config.TargetTableInfo,
|
tableInfo config.TargetTableInfo,
|
||||||
@@ -29,58 +85,29 @@ func (gl *GenericLoader) Consume(
|
|||||||
return col.Name()
|
return col.Name()
|
||||||
})
|
})
|
||||||
|
|
||||||
var accRows []models.UnknownRowValues
|
acc := &loaderAccumulator{batchSize: batchSize}
|
||||||
var parentBatches []models.BatchRef
|
defer acc.drainPending(wgActiveBatches)
|
||||||
pendingDone := 0
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
for range pendingDone {
|
|
||||||
wgActiveBatches.Done()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
flush := func() bool {
|
flush := func() bool {
|
||||||
if len(accRows) == 0 {
|
if len(acc.rows) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
count := len(parentBatches)
|
count := len(acc.parents)
|
||||||
superBatch := models.Batch{
|
superBatch := models.Batch{
|
||||||
Id: uuid.New(),
|
Id: uuid.New(),
|
||||||
ParentBatches: parentBatches,
|
ParentBatches: acc.parents,
|
||||||
Rows: accRows,
|
Rows: acc.rows,
|
||||||
}
|
}
|
||||||
processedRows, err := gl.ProcessBatchWithRetries(ctx, tableInfo, colNames, retryConfig, superBatch)
|
processedRows, err := gl.ProcessBatchWithRetries(ctx, tableInfo, colNames, retryConfig, superBatch)
|
||||||
for range count {
|
for range count {
|
||||||
wgActiveBatches.Done()
|
wgActiveBatches.Done()
|
||||||
}
|
}
|
||||||
pendingDone -= count
|
acc.pendingDone -= count
|
||||||
accRows = nil
|
acc.rows = nil
|
||||||
parentBatches = nil
|
acc.parents = nil
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
atomic.AddInt32(failedBatchesCount, 1)
|
return sendLoadError(ctx, err, retryConfig, failedBatchesCount, chErrorsOut)
|
||||||
if jobError, ok := errors.AsType[*custom_errors.JobError](err); ok {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return false
|
|
||||||
case chErrorsOut <- *jobError:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return false
|
|
||||||
case chErrorsOut <- custom_errors.JobError{ShouldCancelJob: false, Msg: err.Error(), Prev: err}:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if atomic.LoadInt32(failedBatchesCount) > int32(retryConfig.MaxFailedBatchesLoad) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
case chErrorsOut <- custom_errors.JobError{ShouldCancelJob: true, Msg: "Max failed batches (load) reached"}:
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
current := atomic.LoadInt64(rowsLoaded)
|
current := atomic.LoadInt64(rowsLoaded)
|
||||||
@@ -90,13 +117,10 @@ func (gl *GenericLoader) Consume(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if ctx.Err() != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
case batch, ok := <-chBatchesIn:
|
case batch, ok := <-chBatchesIn:
|
||||||
if !ok {
|
if !ok {
|
||||||
flush()
|
flush()
|
||||||
@@ -106,45 +130,20 @@ func (gl *GenericLoader) Consume(
|
|||||||
if batchSize <= 0 {
|
if batchSize <= 0 {
|
||||||
processedRows, err := gl.ProcessBatchWithRetries(ctx, tableInfo, colNames, retryConfig, batch)
|
processedRows, err := gl.ProcessBatchWithRetries(ctx, tableInfo, colNames, retryConfig, batch)
|
||||||
wgActiveBatches.Done()
|
wgActiveBatches.Done()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
atomic.AddInt32(failedBatchesCount, 1)
|
if !sendLoadError(ctx, err, retryConfig, failedBatchesCount, chErrorsOut) {
|
||||||
if jobError, ok := errors.AsType[*custom_errors.JobError](err); ok {
|
return
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case chErrorsOut <- *jobError:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case chErrorsOut <- custom_errors.JobError{ShouldCancelJob: false, Msg: err.Error(), Prev: err}:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if atomic.LoadInt32(failedBatchesCount) > int32(retryConfig.MaxFailedBatchesLoad) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case chErrorsOut <- custom_errors.JobError{ShouldCancelJob: true, Msg: "Max failed batches (load) reached"}:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
current := atomic.LoadInt64(rowsLoaded)
|
current := atomic.LoadInt64(rowsLoaded)
|
||||||
logrus.Debugf("Rows loaded: +%v [current=%v] (%s.%s)", processedRows, current, tableInfo.Schema, tableInfo.Table)
|
logrus.Debugf("Rows loaded: +%v [current=%v] (%s.%s)", processedRows, current, tableInfo.Schema, tableInfo.Table)
|
||||||
atomic.AddInt64(rowsLoaded, int64(processedRows))
|
atomic.AddInt64(rowsLoaded, int64(processedRows))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingDone++
|
acc.add(batch)
|
||||||
accRows = append(accRows, batch.Rows...)
|
if acc.ready() {
|
||||||
parentBatches = append(parentBatches, models.BatchRef{Id: batch.Id})
|
|
||||||
|
|
||||||
if len(accRows) >= batchSize {
|
|
||||||
if !flush() {
|
if !flush() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user