108 lines
2.4 KiB
Go
108 lines
2.4 KiB
Go
package sender
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"git.k-space.ee/k-space/logmower-shipper/pkg/globals"
|
|
m "git.k-space.ee/k-space/logmower-shipper/pkg/mongo"
|
|
"github.com/jtagcat/util"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
const (
|
|
MaxBatchItems = 10000
|
|
MaxBatchTime = 5 * time.Second
|
|
)
|
|
|
|
type Queue <-chan m.Record
|
|
|
|
func (queue Queue) Sender(db *mongo.Collection, metricsFilename string, cancelOnError func()) {
|
|
batched := make(chan []m.Record)
|
|
|
|
// metrics for batcher and queue
|
|
go func() {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
go func() {
|
|
for {
|
|
promShipperQueued.WithLabelValues(metricsFilename).Set(float64(
|
|
len(queue)))
|
|
|
|
timer := time.NewTimer(time.Second)
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-timer.C:
|
|
}
|
|
}
|
|
}()
|
|
|
|
util.Batch(MaxBatchItems, MaxBatchTime, queue, batched)
|
|
// returns when sendQueue is closed
|
|
}()
|
|
|
|
for {
|
|
promShipperSynced.WithLabelValues(metricsFilename).Set(1)
|
|
|
|
batch, ok := <-batched
|
|
if !ok {
|
|
return
|
|
}
|
|
promShipperBatchSizeResult.Observe(float64(len(batch)))
|
|
|
|
promShipperSynced.WithLabelValues(metricsFilename).Set(0)
|
|
|
|
result, err := insertManyWithSimulate(db, batch)
|
|
|
|
var succeedCount int
|
|
if result != nil {
|
|
succeedCount = len(result.InsertedIDs)
|
|
}
|
|
promShipperDbSent.WithLabelValues(metricsFilename).Add(float64(succeedCount))
|
|
|
|
if err != nil {
|
|
promShipperDbSendError.WithLabelValues(metricsFilename).Add(1)
|
|
|
|
if succeedCount == len(batch) {
|
|
log.Printf("all insertions in batch were successful, yet failure in database: %e", err)
|
|
|
|
cancelOnError()
|
|
return
|
|
}
|
|
|
|
firstFailed := &batch[succeedCount] // (len-1)+1
|
|
log.Printf("failure in inserting %q record with offset %d to database: %e",
|
|
firstFailed.Path, firstFailed.Offset, err)
|
|
|
|
cancelOnError()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func insertManyWithSimulate(db *mongo.Collection, batch []m.Record) (*mongo.InsertManyResult, error) {
|
|
if !globals.Simulate {
|
|
var batchBson []interface{} // mongo does not like typing
|
|
for _, b := range batch {
|
|
batchBson = append(batchBson, b.ToBson())
|
|
}
|
|
|
|
tru := true
|
|
return db.InsertMany(globals.MongoTimeout(context.Background()), batchBson, &options.InsertManyOptions{Ordered: &tru})
|
|
}
|
|
|
|
fmt.Printf("simulating successful database bulk write: %v", batch)
|
|
|
|
var res mongo.InsertManyResult
|
|
for range batch {
|
|
res.InsertedIDs = append(res.InsertedIDs, nil)
|
|
}
|
|
|
|
return &res, nil
|
|
}
|