mirror of
https://github.com/prometheus/prometheus
synced 2026-04-20 22:41:05 +08:00
tsdb: use float64 for retention percentage
The retention.percentage config field was typed as uint, which silently truncated fractional values. Setting percentage: 1.5 in prometheus.yml resulted in a retention of 1%, with no warning or error. Remove the redundant MaxPercentage > 100 clamp in main.go; the config UnmarshalYAML already returns an error for out-of-range values before this code is reached. Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
@@ -797,16 +797,12 @@ func main() {
|
||||
logger.Warn("Time retention value is too high. Limiting to: " + y.String())
|
||||
}
|
||||
|
||||
if cfg.tsdb.MaxPercentage > 100 {
|
||||
cfg.tsdb.MaxPercentage = 100
|
||||
logger.Warn("Percentage retention value is too high. Limiting to: 100%")
|
||||
}
|
||||
if cfg.tsdb.MaxPercentage > 0 {
|
||||
if cfg.tsdb.MaxBytes > 0 {
|
||||
logger.Warn("storage.tsdb.retention.size is ignored, because storage.tsdb.retention.percentage is specified")
|
||||
}
|
||||
if prom_runtime.FsSize(localStoragePath) == 0 {
|
||||
fmt.Fprintln(os.Stderr, fmt.Errorf("unable to detect total capacity of metric storage at %s, please disable retention percentage (%d%%)", localStoragePath, cfg.tsdb.MaxPercentage))
|
||||
fmt.Fprintln(os.Stderr, fmt.Errorf("unable to detect total capacity of metric storage at %s, please disable retention percentage (%g%%)", localStoragePath, cfg.tsdb.MaxPercentage))
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
@@ -2014,7 +2010,7 @@ type tsdbOptions struct {
|
||||
MaxBlockChunkSegmentSize units.Base2Bytes
|
||||
RetentionDuration model.Duration
|
||||
MaxBytes units.Base2Bytes
|
||||
MaxPercentage uint
|
||||
MaxPercentage float64
|
||||
NoLockfile bool
|
||||
WALCompressionType compression.Type
|
||||
HeadChunksWriteQueueSize int
|
||||
|
||||
@@ -1111,7 +1111,7 @@ type TSDBRetentionConfig struct {
|
||||
Size units.Base2Bytes `yaml:"size,omitempty"`
|
||||
|
||||
// Maximum percentage of disk used for TSDB storage.
|
||||
Percentage uint `yaml:"percentage,omitempty"`
|
||||
Percentage float64 `yaml:"percentage,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
@@ -1124,7 +1124,7 @@ func (t *TSDBRetentionConfig) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
if t.Size < 0 {
|
||||
return fmt.Errorf("'storage.tsdb.retention.size' must be greater than or equal to 0, got %v", t.Size)
|
||||
}
|
||||
if t.Percentage > 100 {
|
||||
if t.Percentage < 0 || t.Percentage > 100 {
|
||||
return fmt.Errorf("'storage.tsdb.retention.percentage' must be in the range [0, 100], got %v", t.Percentage)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -2640,7 +2640,7 @@ var expectedErrors = []struct {
|
||||
},
|
||||
{
|
||||
filename: "tsdb_retention_percentage_negative.bad.yml",
|
||||
errMsg: "cannot unmarshal !!int `-1` into uint",
|
||||
errMsg: "'storage.tsdb.retention.percentage' must be in the range [0, 100]",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2652,6 +2652,12 @@ func TestBadConfigs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTSDBRetentionPercentageFloat(t *testing.T) {
|
||||
c, err := LoadFile("testdata/tsdb_retention_percentage_float.good.yml", false, promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0.5, c.StorageConfig.TSDBConfig.Retention.Percentage)
|
||||
}
|
||||
|
||||
func TestBadStaticConfigsYML(t *testing.T) {
|
||||
content, err := os.ReadFile("testdata/static_config.bad.yml")
|
||||
require.NoError(t, err)
|
||||
|
||||
4
config/testdata/tsdb_retention_percentage_float.good.yml
vendored
Normal file
4
config/testdata/tsdb_retention_percentage_float.good.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
storage:
|
||||
tsdb:
|
||||
retention:
|
||||
percentage: 0.5
|
||||
10
tsdb/db.go
10
tsdb/db.go
@@ -130,7 +130,7 @@ type Options struct {
|
||||
// Maximum % of disk space to use for blocks to be retained.
|
||||
// 0 or less means disabled.
|
||||
// If both MaxBytes and MaxPercentage are set, percentage prevails.
|
||||
MaxPercentage uint
|
||||
MaxPercentage float64
|
||||
|
||||
// NoLockfile disables creation and consideration of a lock file.
|
||||
NoLockfile bool
|
||||
@@ -1105,7 +1105,7 @@ func open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, rn
|
||||
db.metrics = newDBMetrics(db, r)
|
||||
maxBytes := max(opts.MaxBytes, 0)
|
||||
db.metrics.maxBytes.Set(float64(maxBytes))
|
||||
db.metrics.maxPercentage.Set(float64(max(opts.MaxPercentage, 0)))
|
||||
db.metrics.maxPercentage.Set(max(opts.MaxPercentage, 0))
|
||||
db.metrics.retentionDuration.Set((time.Duration(opts.RetentionDuration) * time.Millisecond).Seconds())
|
||||
|
||||
// Calling db.reload() calls db.reloadBlocks() which requires cmtx to be locked.
|
||||
@@ -1295,7 +1295,7 @@ func (db *DB) ApplyConfig(conf *config.Config) error {
|
||||
db.opts.MaxBytes = int64(conf.StorageConfig.TSDBConfig.Retention.Size)
|
||||
db.metrics.maxBytes.Set(float64(db.opts.MaxBytes))
|
||||
db.opts.MaxPercentage = conf.StorageConfig.TSDBConfig.Retention.Percentage
|
||||
db.metrics.maxPercentage.Set(float64(db.opts.MaxPercentage))
|
||||
db.metrics.maxPercentage.Set(db.opts.MaxPercentage)
|
||||
db.retentionMtx.Unlock()
|
||||
}
|
||||
} else {
|
||||
@@ -1342,7 +1342,7 @@ func (db *DB) getRetentionDuration() int64 {
|
||||
}
|
||||
|
||||
// getRetentionSettings returns max bytes and max percentage settings in a thread-safe manner.
|
||||
func (db *DB) getRetentionSettings() (int64, uint) {
|
||||
func (db *DB) getRetentionSettings() (int64, float64) {
|
||||
db.retentionMtx.RLock()
|
||||
defer db.retentionMtx.RUnlock()
|
||||
return db.opts.MaxBytes, db.opts.MaxPercentage
|
||||
@@ -2018,7 +2018,7 @@ func BeyondSizeRetention(db *DB, blocks []*Block) (deletable map[ulid.ULID]struc
|
||||
if diskSize <= 0 {
|
||||
db.logger.Warn("Unable to retrieve filesystem size of database directory, skip percentage limitation and default to fixed size limitation", "dir", db.dir)
|
||||
} else {
|
||||
maxBytes = int64(uint64(maxPercentage) * diskSize / 100)
|
||||
maxBytes = int64(float64(diskSize) * maxPercentage / 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9663,8 +9663,8 @@ func TestBeyondSizeRetentionWithPercentage(t *testing.T) {
|
||||
db := newTestDB(t, withOpts(opts))
|
||||
require.Zero(t, db.Head().Size())
|
||||
|
||||
blocks := make([]*Block, 0, opts.MaxPercentage+1)
|
||||
for range opts.MaxPercentage {
|
||||
blocks := make([]*Block, 0, int(opts.MaxPercentage)+1)
|
||||
for range int(opts.MaxPercentage) {
|
||||
blocks = append(blocks, &Block{
|
||||
numBytesChunks: numBytesChunks,
|
||||
meta: BlockMeta{ULID: ulid.Make()},
|
||||
|
||||
@@ -268,7 +268,7 @@ type Options struct {
|
||||
TSDBRetentionDuration model.Duration
|
||||
TSDBDir string
|
||||
TSDBMaxBytes units.Base2Bytes
|
||||
TSDBMaxPercentage uint
|
||||
TSDBMaxPercentage float64
|
||||
LocalStorage LocalStorage
|
||||
Storage storage.Storage
|
||||
ExemplarStorage storage.ExemplarQueryable
|
||||
@@ -889,7 +889,7 @@ func (h *Handler) runtimeInfo() (api_v1.RuntimeInfo, error) {
|
||||
if status.StorageRetention != "" {
|
||||
status.StorageRetention += " or "
|
||||
}
|
||||
status.StorageRetention = status.StorageRetention + strconv.FormatUint(uint64(tsdbMaxPercentage), 10) + "%"
|
||||
status.StorageRetention = status.StorageRetention + strconv.FormatFloat(tsdbMaxPercentage, 'g', -1, 64) + "%"
|
||||
}
|
||||
|
||||
metrics, err := h.gatherer.Gather()
|
||||
|
||||
Reference in New Issue
Block a user