mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Robert's changes + async file write
This commit is contained in:
parent
fe639b545f
commit
b9ae36c3c3
131
storage/remote/marker_file_handler.go
Normal file
131
storage/remote/marker_file_handler.go
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-kit/log"
|
||||||
|
"github.com/go-kit/log/level"
|
||||||
|
"github.com/prometheus/prometheus/tsdb/fileutil"
|
||||||
|
"github.com/prometheus/prometheus/tsdb/wlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MarkerFileHandler interface {
|
||||||
|
wlog.Marker
|
||||||
|
MarkSegment(segment int)
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type markerFileHandler struct {
|
||||||
|
segmentToMark chan int
|
||||||
|
quit chan struct{}
|
||||||
|
|
||||||
|
logger log.Logger
|
||||||
|
|
||||||
|
lastMarkedSegmentFilePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ MarkerFileHandler = (*markerFileHandler)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMarkerFileHandler(logger log.Logger, walDir, markerId string) (MarkerFileHandler, error) {
|
||||||
|
markerDir := filepath.Join(walDir, "remote", markerId)
|
||||||
|
|
||||||
|
dir := filepath.Join(walDir, "remote", markerId)
|
||||||
|
if err := os.MkdirAll(dir, 0o777); err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating segment marker folder %q: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mfh := &markerFileHandler{
|
||||||
|
segmentToMark: make(chan int, 1),
|
||||||
|
quit: make(chan struct{}),
|
||||||
|
logger: logger,
|
||||||
|
lastMarkedSegmentFilePath: filepath.Join(markerDir, "segment"),
|
||||||
|
}
|
||||||
|
|
||||||
|
go mfh.markSegmentAsync()
|
||||||
|
|
||||||
|
return mfh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastMarkedSegment implements wlog.Marker.
|
||||||
|
func (mfh *markerFileHandler) LastMarkedSegment() int {
|
||||||
|
bb, err := ioutil.ReadFile(mfh.lastMarkedSegmentFilePath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
level.Warn(mfh.logger).Log("msg", "marker segment file does not exist", "file", mfh.lastMarkedSegmentFilePath)
|
||||||
|
return -1
|
||||||
|
} else if err != nil {
|
||||||
|
level.Error(mfh.logger).Log("msg", "could not access segment marker file", "file", mfh.lastMarkedSegmentFilePath, "err", err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSegment, err := strconv.Atoi(string(bb))
|
||||||
|
if err != nil {
|
||||||
|
level.Error(mfh.logger).Log("msg", "could not read segment marker file", "file", mfh.lastMarkedSegmentFilePath, "err", err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if savedSegment < 0 {
|
||||||
|
level.Error(mfh.logger).Log("msg", "invalid segment number inside marker file", "file", mfh.lastMarkedSegmentFilePath, "segment number", savedSegment)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedSegment
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkSegment implements MarkerHandler.
|
||||||
|
func (mfh *markerFileHandler) MarkSegment(segment int) {
|
||||||
|
var (
|
||||||
|
segmentText = strconv.Itoa(segment)
|
||||||
|
tmp = mfh.lastMarkedSegmentFilePath + ".tmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := os.WriteFile(tmp, []byte(segmentText), 0o666); err != nil {
|
||||||
|
level.Error(mfh.logger).Log("msg", "could not create segment marker file", "file", tmp, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := fileutil.Replace(tmp, mfh.lastMarkedSegmentFilePath); err != nil {
|
||||||
|
level.Error(mfh.logger).Log("msg", "could not replace segment marker file", "file", mfh.lastMarkedSegmentFilePath, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
level.Debug(mfh.logger).Log("msg", "updated segment marker file", "file", mfh.lastMarkedSegmentFilePath, "segment", segment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop implements MarkerHandler.
|
||||||
|
func (mfh *markerFileHandler) Stop() {
|
||||||
|
level.Debug(mfh.logger).Log("msg", "waiting for marker file handler to shut down...")
|
||||||
|
mfh.quit <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mfh *markerFileHandler) markSegmentAsync() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case segmentToMark := <-mfh.segmentToMark:
|
||||||
|
if segmentToMark >= 0 {
|
||||||
|
var (
|
||||||
|
segmentText = strconv.Itoa(segmentToMark)
|
||||||
|
tmp = mfh.lastMarkedSegmentFilePath + ".tmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := os.WriteFile(tmp, []byte(segmentText), 0o666); err != nil {
|
||||||
|
level.Error(mfh.logger).Log("msg", "could not create segment marker file", "file", tmp, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := fileutil.Replace(tmp, mfh.lastMarkedSegmentFilePath); err != nil {
|
||||||
|
level.Error(mfh.logger).Log("msg", "could not replace segment marker file", "file", mfh.lastMarkedSegmentFilePath, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
level.Debug(mfh.logger).Log("msg", "updated segment marker file", "file", mfh.lastMarkedSegmentFilePath, "segment", segmentToMark)
|
||||||
|
}
|
||||||
|
case <-mfh.quit:
|
||||||
|
level.Debug(mfh.logger).Log("msg", "quitting marker handler")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
storage/remote/marker_handler.go
Normal file
112
storage/remote/marker_handler.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/tsdb/wlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MarkerHandler interface {
|
||||||
|
wlog.Marker
|
||||||
|
UpdatePendingData(dataCount, dataSegment int)
|
||||||
|
ProcessConsumedData(data map[int]int)
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type markerHandler struct {
|
||||||
|
markerFileHandler MarkerFileHandler
|
||||||
|
pendingDataMut sync.Mutex
|
||||||
|
latestDataSegment int
|
||||||
|
lastMarkedSegment int
|
||||||
|
pendingDataSegments map[int]int // Map of segment index to pending count
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ MarkerHandler = (*markerHandler)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMarkerHandler(mfh MarkerFileHandler) MarkerHandler {
|
||||||
|
mh := &markerHandler{
|
||||||
|
latestDataSegment: -1,
|
||||||
|
lastMarkedSegment: -1,
|
||||||
|
pendingDataSegments: make(map[int]int),
|
||||||
|
markerFileHandler: mfh,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the last marked segment from disk (if it exists).
|
||||||
|
if lastSegment := mh.markerFileHandler.LastMarkedSegment(); lastSegment >= 0 {
|
||||||
|
mh.lastMarkedSegment = lastSegment
|
||||||
|
}
|
||||||
|
|
||||||
|
return mh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mh *markerHandler) LastMarkedSegment() int {
|
||||||
|
return mh.markerFileHandler.LastMarkedSegment()
|
||||||
|
}
|
||||||
|
|
||||||
|
// updatePendingData updates a counter for how much data is yet to be sent from each segment.
|
||||||
|
// "dataCount" will be added to the segment with ID "dataSegment".
|
||||||
|
func (mh *markerHandler) UpdatePendingData(dataCount, dataSegment int) {
|
||||||
|
mh.pendingDataMut.Lock()
|
||||||
|
defer mh.pendingDataMut.Unlock()
|
||||||
|
|
||||||
|
mh.pendingDataSegments[dataSegment] += dataCount
|
||||||
|
|
||||||
|
// We want to save segments whose data has been fully processed by the
|
||||||
|
// QueueManager. To avoid too much overhead when appending data, we'll only
|
||||||
|
// examine pending data per segment whenever a new segment is detected.
|
||||||
|
//
|
||||||
|
// This could cause our saved segment to lag behind multiple segments
|
||||||
|
// depending on the rate that new segments are created and how long
|
||||||
|
// data in other segments takes to be processed.
|
||||||
|
if dataSegment <= mh.latestDataSegment {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've received data for a new segment. We'll use this as a signal to see
|
||||||
|
// if older segments have been fully processed.
|
||||||
|
//
|
||||||
|
// We want to mark the highest segment which has no more pending data and is
|
||||||
|
// newer than our last mark.
|
||||||
|
mh.latestDataSegment = dataSegment
|
||||||
|
|
||||||
|
var markableSegment int
|
||||||
|
for segment, count := range mh.pendingDataSegments {
|
||||||
|
if segment >= dataSegment || count > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if segment > markableSegment {
|
||||||
|
markableSegment = segment
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the pending map: the current segment has been completely
|
||||||
|
// consumed and doesn't need to be considered for marking again.
|
||||||
|
delete(mh.pendingDataSegments, segment)
|
||||||
|
}
|
||||||
|
|
||||||
|
if markableSegment > mh.lastMarkedSegment {
|
||||||
|
mh.markerFileHandler.MarkSegment(markableSegment)
|
||||||
|
mh.lastMarkedSegment = markableSegment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processConsumedData updates a counter for how many samples have been sent for each segment.
|
||||||
|
// "data" is a map of segment index to consumed data count (e.g. number of samples).
|
||||||
|
func (mh *markerHandler) ProcessConsumedData(data map[int]int) {
|
||||||
|
mh.pendingDataMut.Lock()
|
||||||
|
defer mh.pendingDataMut.Unlock()
|
||||||
|
|
||||||
|
for segment, count := range data {
|
||||||
|
if _, tracked := mh.pendingDataSegments[segment]; !tracked {
|
||||||
|
//TODO: How is it possible to not track it? This should log an error?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mh.pendingDataSegments[segment] -= count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mh *markerHandler) Stop() {
|
||||||
|
mh.markerFileHandler.Stop()
|
||||||
|
}
|
|
@ -413,6 +413,8 @@ type QueueManager struct {
|
||||||
seriesSegmentMtx sync.Mutex // Covers seriesSegmentIndexes - if you also lock seriesMtx, take seriesMtx first.
|
seriesSegmentMtx sync.Mutex // Covers seriesSegmentIndexes - if you also lock seriesMtx, take seriesMtx first.
|
||||||
seriesSegmentIndexes map[chunks.HeadSeriesRef]int
|
seriesSegmentIndexes map[chunks.HeadSeriesRef]int
|
||||||
|
|
||||||
|
markerHandler MarkerHandler
|
||||||
|
|
||||||
shards *shards
|
shards *shards
|
||||||
numShards int
|
numShards int
|
||||||
reshardChan chan int
|
reshardChan chan int
|
||||||
|
@ -426,6 +428,10 @@ type QueueManager struct {
|
||||||
highestRecvTimestamp *maxTimestamp
|
highestRecvTimestamp *maxTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ wlog.WriteTo = (*QueueManager)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// NewQueueManager builds a new QueueManager and starts a new
|
// NewQueueManager builds a new QueueManager and starts a new
|
||||||
// WAL watcher with queue manager as the WriteTo destination.
|
// WAL watcher with queue manager as the WriteTo destination.
|
||||||
// The WAL watcher takes the dir parameter as the base directory
|
// The WAL watcher takes the dir parameter as the base directory
|
||||||
|
@ -449,6 +455,7 @@ func NewQueueManager(
|
||||||
sm ReadyScrapeManager,
|
sm ReadyScrapeManager,
|
||||||
enableExemplarRemoteWrite bool,
|
enableExemplarRemoteWrite bool,
|
||||||
enableNativeHistogramRemoteWrite bool,
|
enableNativeHistogramRemoteWrite bool,
|
||||||
|
markerHandler MarkerHandler,
|
||||||
) *QueueManager {
|
) *QueueManager {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.NewNopLogger()
|
logger = log.NewNopLogger()
|
||||||
|
@ -461,6 +468,7 @@ func NewQueueManager(
|
||||||
})
|
})
|
||||||
|
|
||||||
logger = log.With(logger, remoteName, client.Name(), endpoint, client.Endpoint())
|
logger = log.With(logger, remoteName, client.Name(), endpoint, client.Endpoint())
|
||||||
|
|
||||||
t := &QueueManager{
|
t := &QueueManager{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
flushDeadline: flushDeadline,
|
flushDeadline: flushDeadline,
|
||||||
|
@ -476,6 +484,8 @@ func NewQueueManager(
|
||||||
seriesSegmentIndexes: make(map[chunks.HeadSeriesRef]int),
|
seriesSegmentIndexes: make(map[chunks.HeadSeriesRef]int),
|
||||||
droppedSeries: make(map[chunks.HeadSeriesRef]struct{}),
|
droppedSeries: make(map[chunks.HeadSeriesRef]struct{}),
|
||||||
|
|
||||||
|
markerHandler: markerHandler,
|
||||||
|
|
||||||
numShards: cfg.MinShards,
|
numShards: cfg.MinShards,
|
||||||
reshardChan: make(chan int),
|
reshardChan: make(chan int),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
|
@ -490,7 +500,7 @@ func NewQueueManager(
|
||||||
highestRecvTimestamp: highestRecvTimestamp,
|
highestRecvTimestamp: highestRecvTimestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.watcher = wlog.NewWatcher(watcherMetrics, readerMetrics, logger, client.Name(), t, dir, enableExemplarRemoteWrite, enableNativeHistogramRemoteWrite)
|
t.watcher = wlog.NewWatcher(watcherMetrics, readerMetrics, logger, client.Name(), t, t.markerHandler, dir, enableExemplarRemoteWrite, enableNativeHistogramRemoteWrite)
|
||||||
if t.mcfg.Send {
|
if t.mcfg.Send {
|
||||||
t.metadataWatcher = NewMetadataWatcher(logger, sm, client.Name(), t, t.mcfg.SendInterval, flushDeadline)
|
t.metadataWatcher = NewMetadataWatcher(logger, sm, client.Name(), t, t.mcfg.SendInterval, flushDeadline)
|
||||||
}
|
}
|
||||||
|
@ -547,6 +557,7 @@ func (t *QueueManager) sendMetadataWithBackoff(ctx context.Context, metadata []p
|
||||||
)
|
)
|
||||||
|
|
||||||
begin := time.Now()
|
begin := time.Now()
|
||||||
|
//TODO: Should metadata be part of the marker?
|
||||||
err := t.storeClient.Store(ctx, req)
|
err := t.storeClient.Store(ctx, req)
|
||||||
t.metrics.sentBatchDuration.Observe(time.Since(begin).Seconds())
|
t.metrics.sentBatchDuration.Observe(time.Since(begin).Seconds())
|
||||||
|
|
||||||
|
@ -572,7 +583,7 @@ func (t *QueueManager) sendMetadataWithBackoff(ctx context.Context, metadata []p
|
||||||
|
|
||||||
// Append queues a sample to be sent to the remote storage. Blocks until all samples are
|
// Append queues a sample to be sent to the remote storage. Blocks until all samples are
|
||||||
// enqueued on their shards or a shutdown signal is received.
|
// enqueued on their shards or a shutdown signal is received.
|
||||||
func (t *QueueManager) Append(samples []record.RefSample) bool {
|
func (t *QueueManager) Append(samples []record.RefSample, segment int) bool {
|
||||||
outer:
|
outer:
|
||||||
for _, s := range samples {
|
for _, s := range samples {
|
||||||
t.seriesMtx.Lock()
|
t.seriesMtx.Lock()
|
||||||
|
@ -603,7 +614,9 @@ outer:
|
||||||
timestamp: s.T,
|
timestamp: s.T,
|
||||||
value: s.V,
|
value: s.V,
|
||||||
sType: tSample,
|
sType: tSample,
|
||||||
|
segment: segment,
|
||||||
}) {
|
}) {
|
||||||
|
t.markerHandler.UpdatePendingData(len(samples), segment)
|
||||||
continue outer
|
continue outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +633,7 @@ outer:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *QueueManager) AppendExemplars(exemplars []record.RefExemplar) bool {
|
func (t *QueueManager) AppendExemplars(exemplars []record.RefExemplar, segment int) bool {
|
||||||
if !t.sendExemplars {
|
if !t.sendExemplars {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -648,13 +661,16 @@ outer:
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
//TODO: Write a unit test where the enqueue is retried?
|
||||||
if t.shards.enqueue(e.Ref, timeSeries{
|
if t.shards.enqueue(e.Ref, timeSeries{
|
||||||
seriesLabels: lbls,
|
seriesLabels: lbls,
|
||||||
timestamp: e.T,
|
timestamp: e.T,
|
||||||
value: e.V,
|
value: e.V,
|
||||||
exemplarLabels: e.Labels,
|
exemplarLabels: e.Labels,
|
||||||
sType: tExemplar,
|
sType: tExemplar,
|
||||||
|
segment: segment,
|
||||||
}) {
|
}) {
|
||||||
|
t.markerHandler.UpdatePendingData(len(exemplars), segment)
|
||||||
continue outer
|
continue outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,7 +685,7 @@ outer:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *QueueManager) AppendHistograms(histograms []record.RefHistogramSample) bool {
|
func (t *QueueManager) AppendHistograms(histograms []record.RefHistogramSample, segment int) bool {
|
||||||
if !t.sendNativeHistograms {
|
if !t.sendNativeHistograms {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -701,7 +717,9 @@ outer:
|
||||||
timestamp: h.T,
|
timestamp: h.T,
|
||||||
histogram: h.H,
|
histogram: h.H,
|
||||||
sType: tHistogram,
|
sType: tHistogram,
|
||||||
|
segment: segment,
|
||||||
}) {
|
}) {
|
||||||
|
t.markerHandler.UpdatePendingData(len(histograms), segment)
|
||||||
continue outer
|
continue outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +734,7 @@ outer:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *QueueManager) AppendFloatHistograms(floatHistograms []record.RefFloatHistogramSample) bool {
|
func (t *QueueManager) AppendFloatHistograms(floatHistograms []record.RefFloatHistogramSample, segment int) bool {
|
||||||
if !t.sendNativeHistograms {
|
if !t.sendNativeHistograms {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -748,7 +766,9 @@ outer:
|
||||||
timestamp: h.T,
|
timestamp: h.T,
|
||||||
floatHistogram: h.FH,
|
floatHistogram: h.FH,
|
||||||
sType: tFloatHistogram,
|
sType: tFloatHistogram,
|
||||||
|
segment: segment,
|
||||||
}) {
|
}) {
|
||||||
|
t.markerHandler.UpdatePendingData(len(floatHistograms), segment)
|
||||||
continue outer
|
continue outer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,6 +821,7 @@ func (t *QueueManager) Stop() {
|
||||||
if t.mcfg.Send {
|
if t.mcfg.Send {
|
||||||
t.metadataWatcher.Stop()
|
t.metadataWatcher.Stop()
|
||||||
}
|
}
|
||||||
|
t.markerHandler.Stop()
|
||||||
|
|
||||||
// On shutdown, release the strings in the labels from the intern pool.
|
// On shutdown, release the strings in the labels from the intern pool.
|
||||||
t.seriesMtx.Lock()
|
t.seriesMtx.Lock()
|
||||||
|
@ -1211,6 +1232,8 @@ type timeSeries struct {
|
||||||
exemplarLabels labels.Labels
|
exemplarLabels labels.Labels
|
||||||
// The type of series: sample, exemplar, or histogram.
|
// The type of series: sample, exemplar, or histogram.
|
||||||
sType seriesType
|
sType seriesType
|
||||||
|
// WAL segment number this timeSeries came from
|
||||||
|
segment int
|
||||||
}
|
}
|
||||||
|
|
||||||
type seriesType int
|
type seriesType int
|
||||||
|
@ -1360,6 +1383,12 @@ func (s *shards) runShard(ctx context.Context, shardID int, queue *queue) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// batchSegmentCount tracks the count of data per segment in our batch.
|
||||||
|
// After sending the batch (regardless of success),
|
||||||
|
// QueueManager.processConsumedData is called to recalculate the total
|
||||||
|
// amount of unprocessed data.
|
||||||
|
batchSegmentCount := make(map[int]int)
|
||||||
|
|
||||||
timer := time.NewTimer(time.Duration(s.qm.cfg.BatchSendDeadline))
|
timer := time.NewTimer(time.Duration(s.qm.cfg.BatchSendDeadline))
|
||||||
stop := func() {
|
stop := func() {
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
|
@ -1394,10 +1423,10 @@ func (s *shards) runShard(ctx context.Context, shardID int, queue *queue) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nPendingSamples, nPendingExemplars, nPendingHistograms := s.populateTimeSeries(batch, pendingData)
|
nPendingSamples, nPendingExemplars, nPendingHistograms := s.populateTimeSeries(batch, pendingData, batchSegmentCount)
|
||||||
queue.ReturnForReuse(batch)
|
queue.ReturnForReuse(batch)
|
||||||
n := nPendingSamples + nPendingExemplars + nPendingHistograms
|
n := nPendingSamples + nPendingExemplars + nPendingHistograms
|
||||||
s.sendSamples(ctx, pendingData[:n], nPendingSamples, nPendingExemplars, nPendingHistograms, pBuf, &buf)
|
s.sendSamples(ctx, pendingData[:n], batchSegmentCount, nPendingSamples, nPendingExemplars, nPendingHistograms, pBuf, &buf)
|
||||||
|
|
||||||
stop()
|
stop()
|
||||||
timer.Reset(time.Duration(s.qm.cfg.BatchSendDeadline))
|
timer.Reset(time.Duration(s.qm.cfg.BatchSendDeadline))
|
||||||
|
@ -1405,11 +1434,11 @@ func (s *shards) runShard(ctx context.Context, shardID int, queue *queue) {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
batch := queue.Batch()
|
batch := queue.Batch()
|
||||||
if len(batch) > 0 {
|
if len(batch) > 0 {
|
||||||
nPendingSamples, nPendingExemplars, nPendingHistograms := s.populateTimeSeries(batch, pendingData)
|
nPendingSamples, nPendingExemplars, nPendingHistograms := s.populateTimeSeries(batch, pendingData, batchSegmentCount)
|
||||||
n := nPendingSamples + nPendingExemplars + nPendingHistograms
|
n := nPendingSamples + nPendingExemplars + nPendingHistograms
|
||||||
level.Debug(s.qm.logger).Log("msg", "runShard timer ticked, sending buffered data", "samples", nPendingSamples,
|
level.Debug(s.qm.logger).Log("msg", "runShard timer ticked, sending buffered data", "samples", nPendingSamples,
|
||||||
"exemplars", nPendingExemplars, "shard", shardNum, "histograms", nPendingHistograms)
|
"exemplars", nPendingExemplars, "shard", shardNum, "histograms", nPendingHistograms)
|
||||||
s.sendSamples(ctx, pendingData[:n], nPendingSamples, nPendingExemplars, nPendingHistograms, pBuf, &buf)
|
s.sendSamples(ctx, pendingData[:n], batchSegmentCount, nPendingSamples, nPendingExemplars, nPendingHistograms, pBuf, &buf)
|
||||||
}
|
}
|
||||||
queue.ReturnForReuse(batch)
|
queue.ReturnForReuse(batch)
|
||||||
timer.Reset(time.Duration(s.qm.cfg.BatchSendDeadline))
|
timer.Reset(time.Duration(s.qm.cfg.BatchSendDeadline))
|
||||||
|
@ -1417,7 +1446,7 @@ func (s *shards) runShard(ctx context.Context, shardID int, queue *queue) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *shards) populateTimeSeries(batch []timeSeries, pendingData []prompb.TimeSeries) (int, int, int) {
|
func (s *shards) populateTimeSeries(batch []timeSeries, pendingData []prompb.TimeSeries, batchSegmentCount map[int]int) (int, int, int) {
|
||||||
var nPendingSamples, nPendingExemplars, nPendingHistograms int
|
var nPendingSamples, nPendingExemplars, nPendingHistograms int
|
||||||
for nPending, d := range batch {
|
for nPending, d := range batch {
|
||||||
pendingData[nPending].Samples = pendingData[nPending].Samples[:0]
|
pendingData[nPending].Samples = pendingData[nPending].Samples[:0]
|
||||||
|
@ -1453,11 +1482,13 @@ func (s *shards) populateTimeSeries(batch []timeSeries, pendingData []prompb.Tim
|
||||||
pendingData[nPending].Histograms = append(pendingData[nPending].Histograms, FloatHistogramToHistogramProto(d.timestamp, d.floatHistogram))
|
pendingData[nPending].Histograms = append(pendingData[nPending].Histograms, FloatHistogramToHistogramProto(d.timestamp, d.floatHistogram))
|
||||||
nPendingHistograms++
|
nPendingHistograms++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
batchSegmentCount[d.segment]++
|
||||||
}
|
}
|
||||||
return nPendingSamples, nPendingExemplars, nPendingHistograms
|
return nPendingSamples, nPendingExemplars, nPendingHistograms
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *shards) sendSamples(ctx context.Context, samples []prompb.TimeSeries, sampleCount, exemplarCount, histogramCount int, pBuf *proto.Buffer, buf *[]byte) {
|
func (s *shards) sendSamples(ctx context.Context, samples []prompb.TimeSeries, batchSegmentCount map[int]int, sampleCount, exemplarCount, histogramCount int, pBuf *proto.Buffer, buf *[]byte) {
|
||||||
begin := time.Now()
|
begin := time.Now()
|
||||||
err := s.sendSamplesWithBackoff(ctx, samples, sampleCount, exemplarCount, histogramCount, pBuf, buf)
|
err := s.sendSamplesWithBackoff(ctx, samples, sampleCount, exemplarCount, histogramCount, pBuf, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1467,6 +1498,13 @@ func (s *shards) sendSamples(ctx context.Context, samples []prompb.TimeSeries, s
|
||||||
s.qm.metrics.failedHistogramsTotal.Add(float64(histogramCount))
|
s.qm.metrics.failedHistogramsTotal.Add(float64(histogramCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inform our queue manager about the data that got processed and clear out
|
||||||
|
// our map to prepare for the next batch.
|
||||||
|
s.qm.markerHandler.ProcessConsumedData(batchSegmentCount)
|
||||||
|
for segment := range batchSegmentCount {
|
||||||
|
delete(batchSegmentCount, segment)
|
||||||
|
}
|
||||||
|
|
||||||
// These counters are used to calculate the dynamic sharding, and as such
|
// These counters are used to calculate the dynamic sharding, and as such
|
||||||
// should be maintained irrespective of success or failure.
|
// should be maintained irrespective of success or failure.
|
||||||
s.qm.dataOut.incr(int64(len(samples)))
|
s.qm.dataOut.incr(int64(len(samples)))
|
||||||
|
|
|
@ -142,10 +142,10 @@ func TestSampleDelivery(t *testing.T) {
|
||||||
c.expectExemplars(exemplars[:len(exemplars)/2], series)
|
c.expectExemplars(exemplars[:len(exemplars)/2], series)
|
||||||
c.expectHistograms(histograms[:len(histograms)/2], series)
|
c.expectHistograms(histograms[:len(histograms)/2], series)
|
||||||
c.expectFloatHistograms(floatHistograms[:len(floatHistograms)/2], series)
|
c.expectFloatHistograms(floatHistograms[:len(floatHistograms)/2], series)
|
||||||
qm.Append(samples[:len(samples)/2])
|
qm.Append(samples[:len(samples)/2], 0)
|
||||||
qm.AppendExemplars(exemplars[:len(exemplars)/2])
|
qm.AppendExemplars(exemplars[:len(exemplars)/2], 0)
|
||||||
qm.AppendHistograms(histograms[:len(histograms)/2])
|
qm.AppendHistograms(histograms[:len(histograms)/2], 0)
|
||||||
qm.AppendFloatHistograms(floatHistograms[:len(floatHistograms)/2])
|
qm.AppendFloatHistograms(floatHistograms[:len(floatHistograms)/2], 0)
|
||||||
c.waitForExpectedData(t)
|
c.waitForExpectedData(t)
|
||||||
|
|
||||||
// Send second half of data.
|
// Send second half of data.
|
||||||
|
@ -153,10 +153,10 @@ func TestSampleDelivery(t *testing.T) {
|
||||||
c.expectExemplars(exemplars[len(exemplars)/2:], series)
|
c.expectExemplars(exemplars[len(exemplars)/2:], series)
|
||||||
c.expectHistograms(histograms[len(histograms)/2:], series)
|
c.expectHistograms(histograms[len(histograms)/2:], series)
|
||||||
c.expectFloatHistograms(floatHistograms[len(floatHistograms)/2:], series)
|
c.expectFloatHistograms(floatHistograms[len(floatHistograms)/2:], series)
|
||||||
qm.Append(samples[len(samples)/2:])
|
qm.Append(samples[len(samples)/2:], 0)
|
||||||
qm.AppendExemplars(exemplars[len(exemplars)/2:])
|
qm.AppendExemplars(exemplars[len(exemplars)/2:], 0)
|
||||||
qm.AppendHistograms(histograms[len(histograms)/2:])
|
qm.AppendHistograms(histograms[len(histograms)/2:], 0)
|
||||||
qm.AppendFloatHistograms(floatHistograms[len(floatHistograms)/2:])
|
qm.AppendFloatHistograms(floatHistograms[len(floatHistograms)/2:], 0)
|
||||||
c.waitForExpectedData(t)
|
c.waitForExpectedData(t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -217,11 +217,13 @@ func TestSampleDeliveryTimeout(t *testing.T) {
|
||||||
|
|
||||||
// Send the samples twice, waiting for the samples in the meantime.
|
// Send the samples twice, waiting for the samples in the meantime.
|
||||||
c.expectSamples(samples, series)
|
c.expectSamples(samples, series)
|
||||||
m.Append(samples)
|
m.Append(samples, 0)
|
||||||
|
//TODO: Check that the current marker is at -1?
|
||||||
c.waitForExpectedData(t)
|
c.waitForExpectedData(t)
|
||||||
|
|
||||||
c.expectSamples(samples, series)
|
c.expectSamples(samples, series)
|
||||||
m.Append(samples)
|
m.Append(samples, 1)
|
||||||
|
//TODO: Check that the current marker is at 0?
|
||||||
c.waitForExpectedData(t)
|
c.waitForExpectedData(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +260,7 @@ func TestSampleDeliveryOrder(t *testing.T) {
|
||||||
m.Start()
|
m.Start()
|
||||||
defer m.Stop()
|
defer m.Stop()
|
||||||
// These should be received by the client.
|
// These should be received by the client.
|
||||||
m.Append(samples)
|
m.Append(samples, 0)
|
||||||
c.waitForExpectedData(t)
|
c.waitForExpectedData(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +282,7 @@ func TestShutdown(t *testing.T) {
|
||||||
|
|
||||||
// Append blocks to guarantee delivery, so we do it in the background.
|
// Append blocks to guarantee delivery, so we do it in the background.
|
||||||
go func() {
|
go func() {
|
||||||
m.Append(samples)
|
m.Append(samples, 0)
|
||||||
}()
|
}()
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
@ -347,7 +349,7 @@ func TestReshard(t *testing.T) {
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for i := 0; i < len(samples); i += config.DefaultQueueConfig.Capacity {
|
for i := 0; i < len(samples); i += config.DefaultQueueConfig.Capacity {
|
||||||
sent := m.Append(samples[i : i+config.DefaultQueueConfig.Capacity])
|
sent := m.Append(samples[i:i+config.DefaultQueueConfig.Capacity], 0)
|
||||||
require.True(t, sent, "samples not sent")
|
require.True(t, sent, "samples not sent")
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
@ -418,7 +420,7 @@ func TestReshardPartialBatch(t *testing.T) {
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
m.Append(samples)
|
m.Append(samples, 0)
|
||||||
time.Sleep(batchSendDeadline)
|
time.Sleep(batchSendDeadline)
|
||||||
m.shards.stop()
|
m.shards.stop()
|
||||||
m.shards.start(1)
|
m.shards.start(1)
|
||||||
|
@ -464,7 +466,7 @@ func TestQueueFilledDeadlock(t *testing.T) {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(batchSendDeadline)
|
time.Sleep(batchSendDeadline)
|
||||||
m.Append(samples)
|
m.Append(samples, 0)
|
||||||
done <- struct{}{}
|
done <- struct{}{}
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
|
@ -914,7 +916,7 @@ func BenchmarkSampleSend(b *testing.B) {
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Append(samples)
|
m.Append(samples, 0)
|
||||||
m.UpdateSeriesSegment(series, i+1) // simulate what wlog.Watcher.garbageCollectSeries does
|
m.UpdateSeriesSegment(series, i+1) // simulate what wlog.Watcher.garbageCollectSeries does
|
||||||
m.SeriesReset(i + 1)
|
m.SeriesReset(i + 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,6 +175,18 @@ func (rws *WriteStorage) ApplyConfig(conf *config.Config) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger := rws.logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = log.NewNopLogger()
|
||||||
|
}
|
||||||
|
|
||||||
|
markerFileHandler, err := NewMarkerFileHandler(logger, rws.dir, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
markerHandler := NewMarkerHandler(markerFileHandler)
|
||||||
|
|
||||||
// Redacted to remove any passwords in the URL (that are
|
// Redacted to remove any passwords in the URL (that are
|
||||||
// technically accepted but not recommended) since this is
|
// technically accepted but not recommended) since this is
|
||||||
// only used for metric labels.
|
// only used for metric labels.
|
||||||
|
@ -183,7 +195,7 @@ func (rws *WriteStorage) ApplyConfig(conf *config.Config) error {
|
||||||
newQueueManagerMetrics(rws.reg, name, endpoint),
|
newQueueManagerMetrics(rws.reg, name, endpoint),
|
||||||
rws.watcherMetrics,
|
rws.watcherMetrics,
|
||||||
rws.liveReaderMetrics,
|
rws.liveReaderMetrics,
|
||||||
rws.logger,
|
logger,
|
||||||
rws.dir,
|
rws.dir,
|
||||||
rws.samplesIn,
|
rws.samplesIn,
|
||||||
rwConf.QueueConfig,
|
rwConf.QueueConfig,
|
||||||
|
@ -197,6 +209,7 @@ func (rws *WriteStorage) ApplyConfig(conf *config.Config) error {
|
||||||
rws.scraper,
|
rws.scraper,
|
||||||
rwConf.SendExemplars,
|
rwConf.SendExemplars,
|
||||||
rwConf.SendNativeHistograms,
|
rwConf.SendNativeHistograms,
|
||||||
|
markerHandler,
|
||||||
)
|
)
|
||||||
// Keep track of which queues are new so we know which to start.
|
// Keep track of which queues are new so we know which to start.
|
||||||
newHashes = append(newHashes, hash)
|
newHashes = append(newHashes, hash)
|
||||||
|
|
|
@ -51,18 +51,18 @@ type WriteTo interface {
|
||||||
// Append and AppendExemplar should block until the samples are fully accepted,
|
// Append and AppendExemplar should block until the samples are fully accepted,
|
||||||
// whether enqueued in memory or successfully written to it's final destination.
|
// whether enqueued in memory or successfully written to it's final destination.
|
||||||
// Once returned, the WAL Watcher will not attempt to pass that data again.
|
// Once returned, the WAL Watcher will not attempt to pass that data again.
|
||||||
Append([]record.RefSample) bool
|
Append(samples []record.RefSample, segment int) bool
|
||||||
AppendExemplars([]record.RefExemplar) bool
|
AppendExemplars(exemplars []record.RefExemplar, segment int) bool
|
||||||
AppendHistograms([]record.RefHistogramSample) bool
|
AppendHistograms(histogramSamples []record.RefHistogramSample, segment int) bool
|
||||||
AppendFloatHistograms([]record.RefFloatHistogramSample) bool
|
AppendFloatHistograms(floatHistogramSamples []record.RefFloatHistogramSample, segment int) bool
|
||||||
StoreSeries([]record.RefSeries, int)
|
StoreSeries(series []record.RefSeries, segment int)
|
||||||
|
|
||||||
// Next two methods are intended for garbage-collection: first we call
|
// Next two methods are intended for garbage-collection: first we call
|
||||||
// UpdateSeriesSegment on all current series
|
// UpdateSeriesSegment on all current series
|
||||||
UpdateSeriesSegment([]record.RefSeries, int)
|
UpdateSeriesSegment(series []record.RefSeries, segment int)
|
||||||
// Then SeriesReset is called to allow the deletion
|
// Then SeriesReset is called to allow the deletion
|
||||||
// of all series created in a segment lower than the argument.
|
// of all series created in a segment lower than the argument.
|
||||||
SeriesReset(int)
|
SeriesReset(segment int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to notifier the watcher that data has been written so that it can read.
|
// Used to notifier the watcher that data has been written so that it can read.
|
||||||
|
@ -70,6 +70,19 @@ type WriteNotified interface {
|
||||||
Notify()
|
Notify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marker allows the Watcher to start from a specific segment in the WAL.
|
||||||
|
// Implementers can use this interface to save and restore save points.
|
||||||
|
type Marker interface {
|
||||||
|
// LastMarkedSegment should return the last segment stored in the marker.
|
||||||
|
// Must return nil if there is no mark.
|
||||||
|
//
|
||||||
|
// The Watcher will start reading the first segment whose value is greater
|
||||||
|
// than the return value.
|
||||||
|
LastMarkedSegment() int
|
||||||
|
//TODO: Can it not just return -1? Is the *int some Go thing? Also, if it's -1 maybe the Watcher will just read from 0?
|
||||||
|
//TODO: Also, we anyway have to check for negative integers because this is not unsigned?
|
||||||
|
}
|
||||||
|
|
||||||
type WatcherMetrics struct {
|
type WatcherMetrics struct {
|
||||||
recordsRead *prometheus.CounterVec
|
recordsRead *prometheus.CounterVec
|
||||||
recordDecodeFails *prometheus.CounterVec
|
recordDecodeFails *prometheus.CounterVec
|
||||||
|
@ -82,6 +95,7 @@ type WatcherMetrics struct {
|
||||||
type Watcher struct {
|
type Watcher struct {
|
||||||
name string
|
name string
|
||||||
writer WriteTo
|
writer WriteTo
|
||||||
|
marker Marker
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
walDir string
|
walDir string
|
||||||
lastCheckpoint string
|
lastCheckpoint string
|
||||||
|
@ -92,6 +106,7 @@ type Watcher struct {
|
||||||
|
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
startTimestamp int64 // the start time as a Prometheus timestamp
|
startTimestamp int64 // the start time as a Prometheus timestamp
|
||||||
|
savedSegment int // Last tailed marker. Overrides startTimestamp.
|
||||||
sendSamples bool
|
sendSamples bool
|
||||||
|
|
||||||
recordsReadMetric *prometheus.CounterVec
|
recordsReadMetric *prometheus.CounterVec
|
||||||
|
@ -169,13 +184,15 @@ func NewWatcherMetrics(reg prometheus.Registerer) *WatcherMetrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWatcher creates a new WAL watcher for a given WriteTo.
|
// NewWatcher creates a new WAL watcher for a given WriteTo.
|
||||||
func NewWatcher(metrics *WatcherMetrics, readerMetrics *LiveReaderMetrics, logger log.Logger, name string, writer WriteTo, dir string, sendExemplars, sendHistograms bool) *Watcher {
|
// Reading will start at the segment returned by marker if marker is non-nil.
|
||||||
|
func NewWatcher(metrics *WatcherMetrics, readerMetrics *LiveReaderMetrics, logger log.Logger, name string, writer WriteTo, marker Marker, dir string, sendExemplars, sendHistograms bool) *Watcher {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.NewNopLogger()
|
logger = log.NewNopLogger()
|
||||||
}
|
}
|
||||||
return &Watcher{
|
return &Watcher{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
writer: writer,
|
writer: writer,
|
||||||
|
marker: marker,
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
readerMetrics: readerMetrics,
|
readerMetrics: readerMetrics,
|
||||||
walDir: filepath.Join(dir, "wal"),
|
walDir: filepath.Join(dir, "wal"),
|
||||||
|
@ -189,6 +206,7 @@ func NewWatcher(metrics *WatcherMetrics, readerMetrics *LiveReaderMetrics, logge
|
||||||
|
|
||||||
MaxSegment: -1,
|
MaxSegment: -1,
|
||||||
}
|
}
|
||||||
|
//TODO: Set savedSegment to -1?
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Watcher) Notify() {
|
func (w *Watcher) Notify() {
|
||||||
|
@ -246,7 +264,14 @@ func (w *Watcher) loop() {
|
||||||
|
|
||||||
// We may encounter failures processing the WAL; we should wait and retry.
|
// We may encounter failures processing the WAL; we should wait and retry.
|
||||||
for !isClosed(w.quit) {
|
for !isClosed(w.quit) {
|
||||||
|
if w.marker != nil {
|
||||||
|
w.savedSegment = w.marker.LastMarkedSegment()
|
||||||
|
//TODO: Does this print the int or the address of the int?
|
||||||
|
level.Debug(w.logger).Log("msg", "last saved segment", "segment", w.savedSegment)
|
||||||
|
}
|
||||||
|
|
||||||
w.SetStartTime(time.Now())
|
w.SetStartTime(time.Now())
|
||||||
|
|
||||||
if err := w.Run(); err != nil {
|
if err := w.Run(); err != nil {
|
||||||
level.Error(w.logger).Log("msg", "error tailing WAL", "err", err)
|
level.Error(w.logger).Log("msg", "error tailing WAL", "err", err)
|
||||||
}
|
}
|
||||||
|
@ -568,17 +593,17 @@ func (w *Watcher) readSegment(r *LiveReader, segmentNum int, tail bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, s := range samples {
|
for _, s := range samples {
|
||||||
if s.T > w.startTimestamp {
|
if w.canSendSamples(segmentNum, s.T) {
|
||||||
if !w.sendSamples {
|
if !w.sendSamples {
|
||||||
w.sendSamples = true
|
w.sendSamples = true
|
||||||
duration := time.Since(w.startTime)
|
duration := time.Since(w.startTime)
|
||||||
level.Info(w.logger).Log("msg", "Done replaying WAL", "duration", duration)
|
level.Info(w.logger).Log("msg", "Done replaying WAL", "duration", duration, "segment", segmentNum)
|
||||||
}
|
}
|
||||||
samplesToSend = append(samplesToSend, s)
|
samplesToSend = append(samplesToSend, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(samplesToSend) > 0 {
|
if len(samplesToSend) > 0 {
|
||||||
w.writer.Append(samplesToSend)
|
w.writer.Append(samplesToSend, segmentNum)
|
||||||
samplesToSend = samplesToSend[:0]
|
samplesToSend = samplesToSend[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +622,7 @@ func (w *Watcher) readSegment(r *LiveReader, segmentNum int, tail bool) error {
|
||||||
w.recordDecodeFailsMetric.Inc()
|
w.recordDecodeFailsMetric.Inc()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.writer.AppendExemplars(exemplars)
|
w.writer.AppendExemplars(exemplars, segmentNum)
|
||||||
|
|
||||||
case record.HistogramSamples:
|
case record.HistogramSamples:
|
||||||
// Skip if experimental "histograms over remote write" is not enabled.
|
// Skip if experimental "histograms over remote write" is not enabled.
|
||||||
|
@ -623,7 +648,7 @@ func (w *Watcher) readSegment(r *LiveReader, segmentNum int, tail bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(histogramsToSend) > 0 {
|
if len(histogramsToSend) > 0 {
|
||||||
w.writer.AppendHistograms(histogramsToSend)
|
w.writer.AppendHistograms(histogramsToSend, segmentNum)
|
||||||
histogramsToSend = histogramsToSend[:0]
|
histogramsToSend = histogramsToSend[:0]
|
||||||
}
|
}
|
||||||
case record.FloatHistogramSamples:
|
case record.FloatHistogramSamples:
|
||||||
|
@ -650,7 +675,7 @@ func (w *Watcher) readSegment(r *LiveReader, segmentNum int, tail bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(floatHistogramsToSend) > 0 {
|
if len(floatHistogramsToSend) > 0 {
|
||||||
w.writer.AppendFloatHistograms(floatHistogramsToSend)
|
w.writer.AppendFloatHistograms(floatHistogramsToSend, segmentNum)
|
||||||
floatHistogramsToSend = floatHistogramsToSend[:0]
|
floatHistogramsToSend = floatHistogramsToSend[:0]
|
||||||
}
|
}
|
||||||
case record.Tombstones:
|
case record.Tombstones:
|
||||||
|
@ -663,6 +688,16 @@ func (w *Watcher) readSegment(r *LiveReader, segmentNum int, tail bool) error {
|
||||||
return errors.Wrapf(r.Err(), "segment %d: %v", segmentNum, r.Err())
|
return errors.Wrapf(r.Err(), "segment %d: %v", segmentNum, r.Err())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add a function description
|
||||||
|
func (w *Watcher) canSendSamples(segmentNum int, ts int64) bool {
|
||||||
|
//TODO: What if we have already replayed the WAL? Then segmentNum > *w.savedSegment
|
||||||
|
// will always return true, and we will never get to ts > w.startTimestamp?
|
||||||
|
if w.savedSegment >= 0 && segmentNum > w.savedSegment {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return ts > w.startTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
// Go through all series in a segment updating the segmentNum, so we can delete older series.
|
// Go through all series in a segment updating the segmentNum, so we can delete older series.
|
||||||
// Used with readCheckpoint - implements segmentReadFn.
|
// Used with readCheckpoint - implements segmentReadFn.
|
||||||
func (w *Watcher) readSegmentForGC(r *LiveReader, segmentNum int, _ bool) error {
|
func (w *Watcher) readSegmentForGC(r *LiveReader, segmentNum int, _ bool) error {
|
||||||
|
|
|
@ -60,22 +60,22 @@ type writeToMock struct {
|
||||||
seriesSegmentIndexes map[chunks.HeadSeriesRef]int
|
seriesSegmentIndexes map[chunks.HeadSeriesRef]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wtm *writeToMock) Append(s []record.RefSample) bool {
|
func (wtm *writeToMock) Append(s []record.RefSample, segment int) bool {
|
||||||
wtm.samplesAppended += len(s)
|
wtm.samplesAppended += len(s)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wtm *writeToMock) AppendExemplars(e []record.RefExemplar) bool {
|
func (wtm *writeToMock) AppendExemplars(e []record.RefExemplar, segment int) bool {
|
||||||
wtm.exemplarsAppended += len(e)
|
wtm.exemplarsAppended += len(e)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wtm *writeToMock) AppendHistograms(h []record.RefHistogramSample) bool {
|
func (wtm *writeToMock) AppendHistograms(h []record.RefHistogramSample, segment int) bool {
|
||||||
wtm.histogramsAppended += len(h)
|
wtm.histogramsAppended += len(h)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wtm *writeToMock) AppendFloatHistograms(fh []record.RefFloatHistogramSample) bool {
|
func (wtm *writeToMock) AppendFloatHistograms(fh []record.RefFloatHistogramSample, segment int) bool {
|
||||||
wtm.floatHistogramsAppended += len(fh)
|
wtm.floatHistogramsAppended += len(fh)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ func TestTailSamples(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wt := newWriteToMock()
|
wt := newWriteToMock()
|
||||||
watcher := NewWatcher(wMetrics, nil, nil, "", wt, dir, true, true)
|
watcher := NewWatcher(wMetrics, nil, nil, "", wt, nil, dir, true, true)
|
||||||
watcher.SetStartTime(now)
|
watcher.SetStartTime(now)
|
||||||
|
|
||||||
// Set the Watcher's metrics so they're not nil pointers.
|
// Set the Watcher's metrics so they're not nil pointers.
|
||||||
|
@ -295,7 +295,7 @@ func TestReadToEndNoCheckpoint(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wt := newWriteToMock()
|
wt := newWriteToMock()
|
||||||
watcher := NewWatcher(wMetrics, nil, nil, "", wt, dir, false, false)
|
watcher := NewWatcher(wMetrics, nil, nil, "", wt, nil, dir, false, false)
|
||||||
go watcher.Start()
|
go watcher.Start()
|
||||||
|
|
||||||
expected := seriesCount
|
expected := seriesCount
|
||||||
|
@ -384,7 +384,7 @@ func TestReadToEndWithCheckpoint(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
readTimeout = time.Second
|
readTimeout = time.Second
|
||||||
wt := newWriteToMock()
|
wt := newWriteToMock()
|
||||||
watcher := NewWatcher(wMetrics, nil, nil, "", wt, dir, false, false)
|
watcher := NewWatcher(wMetrics, nil, nil, "", wt, nil, dir, false, false)
|
||||||
go watcher.Start()
|
go watcher.Start()
|
||||||
|
|
||||||
expected := seriesCount * 2
|
expected := seriesCount * 2
|
||||||
|
@ -455,7 +455,7 @@ func TestReadCheckpoint(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wt := newWriteToMock()
|
wt := newWriteToMock()
|
||||||
watcher := NewWatcher(wMetrics, nil, nil, "", wt, dir, false, false)
|
watcher := NewWatcher(wMetrics, nil, nil, "", wt, nil, dir, false, false)
|
||||||
go watcher.Start()
|
go watcher.Start()
|
||||||
|
|
||||||
expectedSeries := seriesCount
|
expectedSeries := seriesCount
|
||||||
|
@ -524,7 +524,7 @@ func TestReadCheckpointMultipleSegments(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wt := newWriteToMock()
|
wt := newWriteToMock()
|
||||||
watcher := NewWatcher(wMetrics, nil, nil, "", wt, dir, false, false)
|
watcher := NewWatcher(wMetrics, nil, nil, "", wt, nil, dir, false, false)
|
||||||
watcher.MaxSegment = -1
|
watcher.MaxSegment = -1
|
||||||
|
|
||||||
// Set the Watcher's metrics so they're not nil pointers.
|
// Set the Watcher's metrics so they're not nil pointers.
|
||||||
|
@ -597,7 +597,7 @@ func TestCheckpointSeriesReset(t *testing.T) {
|
||||||
|
|
||||||
readTimeout = time.Second
|
readTimeout = time.Second
|
||||||
wt := newWriteToMock()
|
wt := newWriteToMock()
|
||||||
watcher := NewWatcher(wMetrics, nil, nil, "", wt, dir, false, false)
|
watcher := NewWatcher(wMetrics, nil, nil, "", wt, nil, dir, false, false)
|
||||||
watcher.MaxSegment = -1
|
watcher.MaxSegment = -1
|
||||||
go watcher.Start()
|
go watcher.Start()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue