Repository: prometheus/tsdb
Pull request #667 (2019-07-22 13:43:13)

add metric for tsdb size retention bytes
Signed-off-by: YaoZengzeng <yaozengzeng@zju.edu.cn>

fixes: prometheus/prometheus#5790
<!--
    Don't forget!
    
    - Most PRs would require a CHANGELOG entry.
    
    - If the PR adds or changes a behaviour or fixes a bug of an exported API it would need a unit/e2e test.
    
    - Where possible use only exported APIs for tests to simplify the review and make it as close as possible to an actual library usage.
    
    - No tests are needed for internal implementation changes.
    
    - Performance improvements would need a benchmark test to prove it.
    
    - All exposed objects should have a comment.
    
    - All comments should start with a capital letter and end with a full stop.
 -->

YaoZengzeng: @brian-brazil PTAL
krasi-georgiev: Why do you need this?
YaoZengzeng: @krasi-georgiev Just fix issue prometheus/prometheus#5790
YaoZengzeng: @brian-brazil Updated, naming is always hard for me 😄 

But as @krasi-georgiev said, I'm also wondering if this metric is really necessary.
brian-brazil: It's generally useful to have metrics like this for flags that set limits.
krasi-georgiev: Yes I understand the use case, but seems a bit strange to add metrics that are constants like - version numbers, config flags etc.
Anyway if @brian-brazil 	is ok with this feel free to merge.
brian-brazil: Config flags like this which are limits are useful for alerting (though this specific one isn't quite, as retention handling is automatic).
krasi-georgiev: Can you please add a test for this metric as well to ensure that the value is the same as the set value when opening a db.

look at the db size tests for some ideas.
YaoZengzeng: @brian-brazil @krasi-georgiev PTAL
brian-brazil: :+1: 

Patch:
diff --git a/chunks/chunks.go b/chunks/chunks.go
index bd7b9e7..8c6945c 100644
--- a/chunks/chunks.go
+++ b/chunks/chunks.go
@@ -295,7 +295,7 @@ func (w *Writer) WriteChunks(chks ...Meta) error {
 	}
 	newsz := w.n + maxLen
 
-	if w.wbuf == nil || w.n > w.segmentSize || newsz > w.segmentSize && maxLen <= w.segmentSize {
+	if w.wbuf == nil || newsz > w.segmentSize && maxLen <= w.segmentSize {
 		if err := w.cut(); err != nil {
 			return err
 		}
diff --git a/db.go b/db.go
index aa9ec17..ec83002 100644
--- a/db.go
+++ b/db.go
@@ -157,6 +157,7 @@ type dbMetrics struct {
 	startTime            prometheus.GaugeFunc
 	tombCleanTimer       prometheus.Histogram
 	blocksBytes          prometheus.Gauge
+	maxBytes             prometheus.Gauge
 	sizeRetentionCount   prometheus.Counter
 }
 
@@ -227,6 +228,10 @@ func newDBMetrics(db *DB, r prometheus.Registerer) *dbMetrics {
 		Name: "prometheus_tsdb_storage_blocks_bytes",
 		Help: "The number of bytes that are currently used for local storage by all blocks.",
 	})
+	m.maxBytes = prometheus.NewGauge(prometheus.GaugeOpts{
+		Name: "prometheus_tsdb_retention_limit_bytes",
+		Help: "Max number of bytes to be retained in the tsdb blocks, configured 0 means disabled",
+	})
 	m.sizeRetentionCount = prometheus.NewCounter(prometheus.CounterOpts{
 		Name: "prometheus_tsdb_size_retentions_total",
 		Help: "The number of times that blocks were deleted because the maximum number of bytes was exceeded.",
@@ -244,6 +249,7 @@ func newDBMetrics(db *DB, r prometheus.Registerer) *dbMetrics {
 			m.startTime,
 			m.tombCleanTimer,
 			m.blocksBytes,
+			m.maxBytes,
 			m.sizeRetentionCount,
 		)
 	}
@@ -454,6 +460,12 @@ func Open(dir string, l log.Logger, r prometheus.Registerer, opts *Options) (db
 	}
 	db.metrics = newDBMetrics(db, r)
 
+	maxBytes := opts.MaxBytes
+	if maxBytes < 0 {
+		maxBytes = 0
+	}
+	db.metrics.maxBytes.Set(float64(maxBytes))
+
 	if !opts.NoLockfile {
 		absdir, err := filepath.Abs(dir)
 		if err != nil {


Test patch:
diff --git a/db_test.go b/db_test.go
index b06e05c..25fb8a7 100644
--- a/db_test.go
+++ b/db_test.go
@@ -1138,6 +1138,30 @@ func TestSizeRetention(t *testing.T) {
 
 }
 
+func TestSizeRetentionMetric(t *testing.T) {
+	cases := []struct {
+		maxBytes    int64
+		expMaxBytes int64
+	}{
+		{maxBytes: 1000, expMaxBytes: 1000},
+		{maxBytes: 0, expMaxBytes: 0},
+		{maxBytes: -1000, expMaxBytes: 0},
+	}
+
+	for _, c := range cases {
+		db, delete := openTestDB(t, &Options{
+			BlockRanges: []int64{100},
+			MaxBytes:    c.maxBytes,
+		})
+
+		actMaxBytes := int64(prom_testutil.ToFloat64(db.metrics.maxBytes))
+		testutil.Equals(t, actMaxBytes, c.expMaxBytes, "metric retention limit bytes mismatch")
+
+		testutil.Ok(t, db.Close())
+		delete()
+	}
+}
+
 func TestNotMatcherSelectsLabelsUnsetSeries(t *testing.T) {
 	db, delete := openTestDB(t, nil)
 	defer func() {