submit: complete local sender refactor
This commit is contained in:
		| @@ -57,6 +57,7 @@ var App = &cli.App{ | |||||||
| 		&cli.StringFlag{Name: "log-directory", Usage: "Directory to watch for logs", Value: "/var/log/containers"}, | 		&cli.StringFlag{Name: "log-directory", Usage: "Directory to watch for logs", Value: "/var/log/containers"}, | ||||||
| 		&cli.IntFlag{Name: "max-record-size", Value: 128 * 1024, Usage: "Maximum record size in bytes"},            // TODO: | 		&cli.IntFlag{Name: "max-record-size", Value: 128 * 1024, Usage: "Maximum record size in bytes"},            // TODO: | ||||||
| 		&cli.BoolFlag{Name: "normalize-log-level", Usage: "Normalize log.level values to Syslog defined keywords"}, // TODO: | 		&cli.BoolFlag{Name: "normalize-log-level", Usage: "Normalize log.level values to Syslog defined keywords"}, // TODO: | ||||||
|  | 		&cli.BoolFlag{Name: "delete-after-read", Usage: "Delete log file when it is synced to mongo, and no new lines to read", Value: false}, | ||||||
| 		// &cli.BoolFlag{Name: "parse-json"}, //TODO: | 		// &cli.BoolFlag{Name: "parse-json"}, //TODO: | ||||||
| 		&cli.StringFlag{Category: "k8s metadata", Name: "pod-namespace", EnvVars: []string{"KUBE_POD_NAMESPACE"}}, // TODO: | 		&cli.StringFlag{Category: "k8s metadata", Name: "pod-namespace", EnvVars: []string{"KUBE_POD_NAMESPACE"}}, // TODO: | ||||||
| 		&cli.StringFlag{Category: "k8s metadata", Name: "node-name", EnvVars: []string{"KUBE_NODE_NAME"}, Required: true}, | 		&cli.StringFlag{Category: "k8s metadata", Name: "node-name", EnvVars: []string{"KUBE_NODE_NAME"}, Required: true}, | ||||||
| @@ -147,7 +148,7 @@ var App = &cli.App{ | |||||||
|  |  | ||||||
| 					wg.Add(1) | 					wg.Add(1) | ||||||
| 					go func() { | 					go func() { | ||||||
| 						state.shipFile(ctx.Context, absPath) | 						state.shipFile(ctx.Context, absPath, ctx.Bool("delete-after-read")) | ||||||
| 						wg.Done() | 						wg.Done() | ||||||
| 					}() | 					}() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,9 +41,8 @@ var ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	SendQueueLimit = 1024 | 	MaxBatchItems = 100 | ||||||
| 	MaxBatchItems  = 100 | 	MaxBatchTime  = time.Second | ||||||
| 	MaxBatchTime   = time.Second |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/jtagcat/util" | 	"github.com/jtagcat/util" | ||||||
| 	prom "github.com/prometheus/client_golang/prometheus" | 	prom "github.com/prometheus/client_golang/prometheus" | ||||||
| @@ -43,7 +44,10 @@ type ( | |||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (s *submitter) shipFile(ctx context.Context, name string) { | const SendQueueLimit = 1024 | ||||||
|  |  | ||||||
|  | // TODO: caller may call duplicate shipFile of same name on file replace; sends might not work properly | ||||||
|  | func (s *submitter) shipFile(ctx context.Context, name string, deleteAfterRead bool) { | ||||||
| 	baseName := filepath.Base(name) | 	baseName := filepath.Base(name) | ||||||
|  |  | ||||||
| 	sigCatchupped := make(chan struct{}, 1) | 	sigCatchupped := make(chan struct{}, 1) | ||||||
| @@ -54,18 +58,40 @@ func (s *submitter) shipFile(ctx context.Context, name string) { | |||||||
| 		promCatchupDone.WithLabelValues(baseName).Add(1) | 		promCatchupDone.WithLabelValues(baseName).Add(1) | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	// TODO: restarting before mongo sendQueue finishes will result in duplicates | 	sendChan := make(chan mLog, SendQueueLimit) | ||||||
| 	wait.ManagedExponentialBackoffWithContext(ctx, defaultBackoff(), func() (done bool, _ error) { | 	synced := s.sender(name, sendChan) | ||||||
| 		if err := s.shipFileRoutine(ctx, name, sigCatchupped); err != nil { |  | ||||||
| 			promFileErr.WithLabelValues(baseName).Add(1) | 	deleteOk := func() bool { | ||||||
| 			s.l.Error("shipping file", zap.String("filename", baseName), zap.Error(err)) | 		if deleteAfterRead && synced() { | ||||||
| 			return false, nil // nil since we want it to loop and keep retrying indefinitely | 			return true | ||||||
| 		} | 		} | ||||||
| 		return true, nil | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: better way to kill or wait for mongo sendQueue before retrying (or duplicates?) | ||||||
|  | 	wait.ManagedExponentialBackoffWithContext(ctx, defaultBackoff(), func() (done bool, _ error) { | ||||||
|  | 		// | ||||||
|  | 		err := s.shipFileRoutine(ctx, name, deleteOk, sendChan, sigCatchupped) | ||||||
|  | 		if err == nil { | ||||||
|  | 			return true, nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		promFileErr.WithLabelValues(baseName).Add(1) | ||||||
|  | 		s.l.Error("shipping file", zap.String("filename", baseName), zap.Error(err)) | ||||||
|  | 		return false, nil // nil since we want to loop and keep retrying indefinitely | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *submitter) shipFileRoutine(ctx context.Context, name string, sigCatchupped chan<- struct{}) error { | func (s *submitter) shipFileRoutine(ctx context.Context, name string, deleteOk func() bool, sendQueue chan<- mLog, sigCatchupped chan<- struct{}) error { | ||||||
|  | 	// TODO: better way for respecting ?killing sender for retry | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		if len(sendQueue) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		time.Sleep(time.Second) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Initialize in case of new file | 	// Initialize in case of new file | ||||||
| 	log := mLog{ | 	log := mLog{ | ||||||
| 		HostInfo: s.hostInfo, | 		HostInfo: s.hostInfo, | ||||||
| @@ -73,7 +99,7 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, sigCatchup | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// get files with offset | 	// get files with offset | ||||||
| 	offsetResult, err := mWithErr(s.db.FindOne(mongoTimeoutCtx(ctx), | 	offsetResult, err := mongoWithErr(s.db.FindOne(mongoTimeoutCtx(ctx), | ||||||
| 		bson.D{{Key: "hostinfo.id", Value: s.hostInfo.id}, {Key: "filename", Value: name}}, | 		bson.D{{Key: "hostinfo.id", Value: s.hostInfo.id}, {Key: "filename", Value: name}}, | ||||||
| 		&mongoOpt.FindOneOptions{Sort: bson.D{{Key: "offset", Value: -1}}}, // sort descending (get largest) | 		&mongoOpt.FindOneOptions{Sort: bson.D{{Key: "offset", Value: -1}}}, // sort descending (get largest) | ||||||
| 	)) | 	)) | ||||||
| @@ -82,7 +108,6 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, sigCatchup | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// offsetResult.DecodeBytes() //TODO: check for extra fields | 	// offsetResult.DecodeBytes() //TODO: check for extra fields | ||||||
|  |  | ||||||
| 	if err := offsetResult.Decode(&log); err != nil && !errors.Is(err, mongo.ErrNoDocuments) { | 	if err := offsetResult.Decode(&log); err != nil && !errors.Is(err, mongo.ErrNoDocuments) { | ||||||
| 		return fmt.Errorf("decoding mongo offset: %w", err) | 		return fmt.Errorf("decoding mongo offset: %w", err) | ||||||
| 	} | 	} | ||||||
| @@ -98,10 +123,12 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, sigCatchup | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("tailing file: %w", err) | 		return fmt.Errorf("tailing file: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
| 		case err := <-errChan: | 		case err := <-errChan: | ||||||
| 			return fmt.Errorf("tailing file: %w", err) | 			return fmt.Errorf("tailing file: %w", err) | ||||||
|  |  | ||||||
| 		case line := <-lineChan: | 		case line := <-lineChan: | ||||||
| 			if line.EndOffset > startSize { | 			if line.EndOffset > startSize { | ||||||
| 				select { | 				select { | ||||||
| @@ -111,8 +138,7 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, sigCatchup | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			select { | 			select { | ||||||
| 			// TODO: use per-file batch senders for file deletion mongo synced, and better error handling; #3 | 			case sendQueue <- mLog{ | ||||||
| 			case s.sendQueue <- mLog{ |  | ||||||
| 				HostInfo: s.hostInfo, | 				HostInfo: s.hostInfo, | ||||||
| 				Filename: *line.Filename, | 				Filename: *line.Filename, | ||||||
| 				Offset:   line.EndOffset, | 				Offset:   line.EndOffset, | ||||||
| @@ -121,14 +147,17 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, sigCatchup | |||||||
| 			default: | 			default: | ||||||
| 				promShipperDropped.WithLabelValues(*line.Filename).Add(1) | 				promShipperDropped.WithLabelValues(*line.Filename).Add(1) | ||||||
| 			} | 			} | ||||||
| 			// TODO: |  | ||||||
| 			// default: | 			// no new lines | ||||||
| 			// 	return nil | 		default: | ||||||
|  | 			if deleteOk() { | ||||||
|  | 				return os.Remove(name) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func mWithErr[t interface{ Err() error }](mongoWrap t) (t, error) { | func mongoWithErr[t interface{ Err() error }](mongoWrap t) (t, error) { | ||||||
| 	return mongoWrap, mongoWrap.Err() | 	return mongoWrap, mongoWrap.Err() | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user