2023-06-28 04:32:05 -07:00
|
|
|
package remote
|
|
|
|
|
|
|
|
import (
|
|
|
|
"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{}
|
2023-07-12 16:46:26 -07:00
|
|
|
dir string
|
2023-06-28 04:32:05 -07:00
|
|
|
|
|
|
|
logger log.Logger
|
|
|
|
|
|
|
|
lastMarkedSegmentFilePath string
|
|
|
|
}
|
|
|
|
|
2023-07-12 16:46:26 -07:00
|
|
|
func NewMarkerFileHandler(logger log.Logger, walDir, markerId string) MarkerFileHandler {
|
2023-06-28 04:32:05 -07:00
|
|
|
dir := filepath.Join(walDir, "remote", markerId)
|
|
|
|
|
|
|
|
mfh := &markerFileHandler{
|
|
|
|
segmentToMark: make(chan int, 1),
|
|
|
|
quit: make(chan struct{}),
|
|
|
|
logger: logger,
|
2023-07-12 16:46:26 -07:00
|
|
|
dir: dir,
|
|
|
|
lastMarkedSegmentFilePath: filepath.Join(dir, "segment"),
|
2023-06-28 04:32:05 -07:00
|
|
|
}
|
|
|
|
|
2023-06-28 05:57:21 -07:00
|
|
|
//TODO: Should this be in a separate Start() function?
|
2023-06-28 04:32:05 -07:00
|
|
|
go mfh.markSegmentAsync()
|
|
|
|
|
2023-07-12 16:46:26 -07:00
|
|
|
return mfh
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mfh *markerFileHandler) Start() {
|
|
|
|
go mfh.markSegmentAsync()
|
2023-06-28 04:32:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// LastMarkedSegment implements wlog.Marker.
|
|
|
|
func (mfh *markerFileHandler) LastMarkedSegment() int {
|
2023-07-12 16:46:26 -07:00
|
|
|
bb, err := os.ReadFile(mfh.lastMarkedSegmentFilePath)
|
2023-06-28 04:32:05 -07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|