mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
Add storage method to delete time series
This commit is contained in:
parent
0de6edbdfc
commit
5c6c0e2faa
|
@ -47,6 +47,9 @@ type Storage interface {
|
||||||
// The iterator will never return samples older than retention time,
|
// The iterator will never return samples older than retention time,
|
||||||
// relative to the time NewIterator was called.
|
// relative to the time NewIterator was called.
|
||||||
NewIterator(clientmodel.Fingerprint) SeriesIterator
|
NewIterator(clientmodel.Fingerprint) SeriesIterator
|
||||||
|
// Drop all time series associated with the given fingerprints. This operation
|
||||||
|
// will not show up in the series operations metrics.
|
||||||
|
DropMetricsForFingerprints(...clientmodel.Fingerprint)
|
||||||
// Run the various maintenance loops in goroutines. Returns when the
|
// Run the various maintenance loops in goroutines. Returns when the
|
||||||
// storage is ready to use. Keeps everything running in the background
|
// storage is ready to use. Keeps everything running in the background
|
||||||
// until Stop is called.
|
// until Stop is called.
|
||||||
|
|
|
@ -430,6 +430,26 @@ func (s *memorySeriesStorage) MetricForFingerprint(fp clientmodel.Fingerprint) c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DropMetric implements Storage.
|
||||||
|
func (s *memorySeriesStorage) DropMetricsForFingerprints(fps ...clientmodel.Fingerprint) {
|
||||||
|
for _, fp := range fps {
|
||||||
|
s.fpLocker.Lock(fp)
|
||||||
|
|
||||||
|
if series, ok := s.fpToSeries.get(fp); ok {
|
||||||
|
s.fpToSeries.del(fp)
|
||||||
|
s.numSeries.Dec()
|
||||||
|
s.persistence.unindexMetric(fp, series.metric)
|
||||||
|
if _, err := s.persistence.deleteSeriesFile(fp); err != nil {
|
||||||
|
log.Errorf("Error deleting series file for %v: %v", fp, err)
|
||||||
|
}
|
||||||
|
} else if err := s.persistence.purgeArchivedMetric(fp); err != nil {
|
||||||
|
log.Errorf("Error purging metric with fingerprint %v: %v", fp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.fpLocker.Unlock(fp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Append implements Storage.
|
// Append implements Storage.
|
||||||
func (s *memorySeriesStorage) Append(sample *clientmodel.Sample) {
|
func (s *memorySeriesStorage) Append(sample *clientmodel.Sample) {
|
||||||
if s.getNumChunksToPersist() >= s.maxChunksToPersist {
|
if s.getNumChunksToPersist() >= s.maxChunksToPersist {
|
||||||
|
@ -694,7 +714,7 @@ func (s *memorySeriesStorage) cycleThroughArchivedFingerprints() chan clientmode
|
||||||
|
|
||||||
for {
|
for {
|
||||||
archivedFPs, err := s.persistence.fingerprintsModifiedBefore(
|
archivedFPs, err := s.persistence.fingerprintsModifiedBefore(
|
||||||
clientmodel.TimestampFromTime(time.Now()).Add(-s.dropAfter),
|
clientmodel.Now().Add(-s.dropAfter),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to lookup archived fingerprint ranges: ", err)
|
log.Error("Failed to lookup archived fingerprint ranges: ", err)
|
||||||
|
@ -750,7 +770,7 @@ loop:
|
||||||
dirtySeriesCount = 0
|
dirtySeriesCount = 0
|
||||||
checkpointTimer.Reset(s.checkpointInterval)
|
checkpointTimer.Reset(s.checkpointInterval)
|
||||||
case fp := <-memoryFingerprints:
|
case fp := <-memoryFingerprints:
|
||||||
if s.maintainMemorySeries(fp, clientmodel.TimestampFromTime(time.Now()).Add(-s.dropAfter)) {
|
if s.maintainMemorySeries(fp, clientmodel.Now().Add(-s.dropAfter)) {
|
||||||
dirtySeriesCount++
|
dirtySeriesCount++
|
||||||
// Check if we have enough "dirty" series so that we need an early checkpoint.
|
// Check if we have enough "dirty" series so that we need an early checkpoint.
|
||||||
// However, if we are already behind persisting chunks, creating a checkpoint
|
// However, if we are already behind persisting chunks, creating a checkpoint
|
||||||
|
@ -764,7 +784,7 @@ loop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case fp := <-archivedFingerprints:
|
case fp := <-archivedFingerprints:
|
||||||
s.maintainArchivedSeries(fp, clientmodel.TimestampFromTime(time.Now()).Add(-s.dropAfter))
|
s.maintainArchivedSeries(fp, clientmodel.Now().Add(-s.dropAfter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wait until both channels are closed.
|
// Wait until both channels are closed.
|
||||||
|
|
|
@ -152,8 +152,7 @@ func TestRetentionCutoff(t *testing.T) {
|
||||||
|
|
||||||
s.dropAfter = 1 * time.Hour
|
s.dropAfter = 1 * time.Hour
|
||||||
|
|
||||||
samples := make(clientmodel.Samples, 120)
|
for i := 0; i < 120; i++ {
|
||||||
for i := range samples {
|
|
||||||
smpl := &clientmodel.Sample{
|
smpl := &clientmodel.Sample{
|
||||||
Metric: clientmodel.Metric{"job": "test"},
|
Metric: clientmodel.Metric{"job": "test"},
|
||||||
Timestamp: insertStart.Add(time.Duration(i) * time.Minute), // 1 minute intervals.
|
Timestamp: insertStart.Add(time.Duration(i) * time.Minute), // 1 minute intervals.
|
||||||
|
@ -204,6 +203,85 @@ func TestRetentionCutoff(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDropMetrics(t *testing.T) {
|
||||||
|
now := clientmodel.Now()
|
||||||
|
insertStart := now.Add(-2 * time.Hour)
|
||||||
|
|
||||||
|
s, closer := NewTestStorage(t, 1)
|
||||||
|
defer closer.Close()
|
||||||
|
|
||||||
|
m1 := clientmodel.Metric{clientmodel.MetricNameLabel: "test", "n1": "v1"}
|
||||||
|
m2 := clientmodel.Metric{clientmodel.MetricNameLabel: "test", "n1": "v2"}
|
||||||
|
|
||||||
|
N := 120000
|
||||||
|
|
||||||
|
for j, m := range []clientmodel.Metric{m1, m2} {
|
||||||
|
for i := 0; i < N; i++ {
|
||||||
|
smpl := &clientmodel.Sample{
|
||||||
|
Metric: m,
|
||||||
|
Timestamp: insertStart.Add(time.Duration(i) * time.Millisecond), // 1 minute intervals.
|
||||||
|
Value: clientmodel.SampleValue(j),
|
||||||
|
}
|
||||||
|
s.Append(smpl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.WaitForIndexing()
|
||||||
|
|
||||||
|
matcher := metric.LabelMatchers{{
|
||||||
|
Type: metric.Equal,
|
||||||
|
Name: clientmodel.MetricNameLabel,
|
||||||
|
Value: "test",
|
||||||
|
}}
|
||||||
|
|
||||||
|
fps := s.FingerprintsForLabelMatchers(matcher)
|
||||||
|
if len(fps) != 2 {
|
||||||
|
t.Fatalf("unexpected number of fingerprints: %d", len(fps))
|
||||||
|
}
|
||||||
|
|
||||||
|
it := s.NewIterator(fps[0])
|
||||||
|
if vals := it.RangeValues(metric.Interval{insertStart, now}); len(vals) != N {
|
||||||
|
t.Fatalf("unexpected number of samples: %d", len(vals))
|
||||||
|
}
|
||||||
|
it = s.NewIterator(fps[1])
|
||||||
|
if vals := it.RangeValues(metric.Interval{insertStart, now}); len(vals) != N {
|
||||||
|
t.Fatalf("unexpected number of samples: %d", len(vals))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.DropMetricsForFingerprints(fps[0])
|
||||||
|
s.WaitForIndexing()
|
||||||
|
|
||||||
|
fps2 := s.FingerprintsForLabelMatchers(matcher)
|
||||||
|
if len(fps2) != 1 {
|
||||||
|
t.Fatalf("unexpected number of fingerprints: %d", len(fps2))
|
||||||
|
}
|
||||||
|
|
||||||
|
it = s.NewIterator(fps[0])
|
||||||
|
if vals := it.RangeValues(metric.Interval{insertStart, now}); len(vals) != 0 {
|
||||||
|
t.Fatalf("unexpected number of samples: %d", len(vals))
|
||||||
|
}
|
||||||
|
it = s.NewIterator(fps[1])
|
||||||
|
if vals := it.RangeValues(metric.Interval{insertStart, now}); len(vals) != N {
|
||||||
|
t.Fatalf("unexpected number of samples: %d", len(vals))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.DropMetricsForFingerprints(fps...)
|
||||||
|
s.WaitForIndexing()
|
||||||
|
|
||||||
|
fps3 := s.FingerprintsForLabelMatchers(matcher)
|
||||||
|
if len(fps3) != 0 {
|
||||||
|
t.Fatalf("unexpected number of fingerprints: %d", len(fps3))
|
||||||
|
}
|
||||||
|
|
||||||
|
it = s.NewIterator(fps[0])
|
||||||
|
if vals := it.RangeValues(metric.Interval{insertStart, now}); len(vals) != 0 {
|
||||||
|
t.Fatalf("unexpected number of samples: %d", len(vals))
|
||||||
|
}
|
||||||
|
it = s.NewIterator(fps[1])
|
||||||
|
if vals := it.RangeValues(metric.Interval{insertStart, now}); len(vals) != 0 {
|
||||||
|
t.Fatalf("unexpected number of samples: %d", len(vals))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestLoop is just a smoke test for the loop method, if we can switch it on and
|
// TestLoop is just a smoke test for the loop method, if we can switch it on and
|
||||||
// off without disaster.
|
// off without disaster.
|
||||||
func TestLoop(t *testing.T) {
|
func TestLoop(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue