restructure project
This commit is contained in:
143
pkg/file/file.go
Normal file
143
pkg/file/file.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.k-space.ee/k-space/logmower-shipper/pkg/globals"
|
||||
"git.k-space.ee/k-space/logmower-shipper/pkg/lines"
|
||||
m "git.k-space.ee/k-space/logmower-shipper/pkg/mongo"
|
||||
"git.k-space.ee/k-space/logmower-shipper/pkg/sender"
|
||||
"github.com/jtagcat/util"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
mongoOpt "go.mongodb.org/mongo-driver/mongo/options"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
const SendQueueLimit = 1024
|
||||
|
||||
type File struct {
|
||||
*m.File
|
||||
MetricsName string // filepath.Base()
|
||||
}
|
||||
|
||||
// TODO: caller could call duplicate shipFile of same name on file replace: sends might not work properly
|
||||
func (f File) Process(ctx context.Context, db *mongo.Collection, recordLimitBytes int) {
|
||||
lineChan := make(chan lines.Raw)
|
||||
defer close(lineChan)
|
||||
|
||||
dbQueue := make(chan m.Record, SendQueueLimit)
|
||||
go lines.RawC(lineChan).Process(recordLimitBytes, dbQueue)
|
||||
|
||||
waitGo := util.GoWg(func() {
|
||||
sender.Queue(dbQueue).Sender(db, f.MetricsName)
|
||||
})
|
||||
defer waitGo()
|
||||
|
||||
// TODO: better way to kill or wait for sendQueue before retrying (or duplicates?)
|
||||
_ = wait.ManagedExponentialBackoffWithContext(ctx, globals.Backoff(), func() (done bool, _ error) {
|
||||
err := f.trySubmit(ctx, db, lineChan)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
promFileErr.WithLabelValues(f.MetricsName).Add(1)
|
||||
log.Printf("processing file %q: %e", f.MetricsName, err)
|
||||
|
||||
// nil: loop and keep retrying indefinitely
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
// use submitter(), don't use directly
|
||||
func (f File) trySubmit(ctx context.Context, db *mongo.Collection, sendQueue chan<- lines.Raw) error {
|
||||
lFile := lines.File(f) // file.File, but avoiding import cycle
|
||||
|
||||
// TODO: better way for respecting ?killing sender for retry
|
||||
for {
|
||||
if len(sendQueue) == 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
// get files with offset
|
||||
offsetResult, _ := mongoWithErr(db.FindOne(globals.MongoTimeout(ctx),
|
||||
bson.D{{Key: m.RecordKeyHostId, Value: f.Host.Id}, {Key: m.RecordKeyFilePath, Value: f.Path}},
|
||||
&mongoOpt.FindOneOptions{Sort: bson.D{{Key: m.RecordKeyOffset, Value: -1}}}, // sort descending (get largest)
|
||||
))
|
||||
|
||||
offsetResultBytes, err := offsetResult.DecodeBytes()
|
||||
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return fmt.Errorf("retrieving offset from database: %w", err)
|
||||
}
|
||||
|
||||
dbOffset := m.RecordOffsetFromBson(&offsetResultBytes)
|
||||
|
||||
fi, err := os.Stat(f.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting original file size: %w", err)
|
||||
}
|
||||
startSize := fi.Size()
|
||||
|
||||
sctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
promFileInitialSeekSkipped.WithLabelValues(f.MetricsName).Set(float64(dbOffset))
|
||||
|
||||
lineChan, errChan, err := util.TailFile(sctx, f.Path, dbOffset, io.SeekStart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tailing file: %w", err)
|
||||
}
|
||||
|
||||
var catchUpped bool
|
||||
promFileCatchupDone.WithLabelValues(f.MetricsName).Set(0)
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return fmt.Errorf("tailing file: %w", err)
|
||||
|
||||
case line, ok := <-lineChan:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
promFileLineSize.WithLabelValues(f.MetricsName).Observe(float64(len(line.Bytes)))
|
||||
|
||||
if !catchUpped {
|
||||
catchUpped = line.EndOffset >= startSize
|
||||
|
||||
if catchUpped {
|
||||
promFileCatchupDone.WithLabelValues(f.MetricsName).Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
if len(line.Bytes) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
sendQueue <- lines.Raw{
|
||||
File: &lFile,
|
||||
|
||||
Offset: line.EndOffset,
|
||||
B: line.Bytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mongoWithErr[t interface{ Err() error }](mongoWrap t) (t, error) {
|
||||
return mongoWrap, mongoWrap.Err()
|
||||
}
|
||||
|
||||
// func JitterUntilCancelWithContext(pctx context.Context, f func(context.Context, context.CancelFunc), period time.Duration, jitterFactor float64, sliding bool) {
|
||||
// ctx, cancel := context.WithCancel(pctx)
|
||||
// wait.JitterUntil(func() { f(ctx, cancel) }, period, jitterFactor, sliding, ctx.Done())
|
||||
// }
|
35
pkg/file/metrics.go
Normal file
35
pkg/file/metrics.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"git.k-space.ee/k-space/logmower-shipper/pkg/globals"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
promFileInitialSeekSkipped = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: globals.PrometheusPrefix,
|
||||
// Subsystem: "file",
|
||||
Name: "skipped_bytes",
|
||||
Help: "Bytes skipped in file after discovering",
|
||||
}, []string{"filename"})
|
||||
promFileCatchupDone = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: globals.PrometheusPrefix,
|
||||
Subsystem: "file",
|
||||
Name: "catchupped",
|
||||
Help: "(0 or) 1 if initial backlog has been sent; (total <= watcher_file_count)",
|
||||
}, []string{"filename"}) // TODO: rm filename?
|
||||
promFileErr = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: globals.PrometheusPrefix,
|
||||
Subsystem: "file",
|
||||
Name: "errors_count",
|
||||
Help: "Errors while reading file",
|
||||
}, []string{"filename"})
|
||||
promFileLineSize = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: globals.PrometheusPrefix,
|
||||
// Subsystem: "file",
|
||||
Name: "line_size_bytes",
|
||||
Help: "Log line size in bytes",
|
||||
Buckets: []float64{80, 160, 320, 640, 1280},
|
||||
}, []string{"filename"})
|
||||
)
|
Reference in New Issue
Block a user