2013-02-08 09:03:26 -08:00
// Copyright 2013 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric
import (
"fmt"
"github.com/prometheus/prometheus/coding"
"github.com/prometheus/prometheus/coding/indexable"
"github.com/prometheus/prometheus/model"
dto "github.com/prometheus/prometheus/model/generated"
2013-03-25 02:24:59 -07:00
"github.com/prometheus/prometheus/storage/raw/leveldb"
2013-02-08 09:03:26 -08:00
"time"
)
// diskFrontier describes an on-disk store of series to provide a
// representation of the known keyspace and time series values available.
//
// This is used to reduce the burden associated with LevelDB iterator
// management.
type diskFrontier struct {
2013-05-17 03:58:15 -07:00
firstFingerprint * model . Fingerprint
2013-02-08 09:03:26 -08:00
firstSupertime time . Time
2013-05-17 03:58:15 -07:00
lastFingerprint * model . Fingerprint
2013-02-08 09:03:26 -08:00
lastSupertime time . Time
}
2013-04-01 04:22:38 -07:00
func ( f diskFrontier ) String ( ) string {
2013-02-08 09:03:26 -08:00
return fmt . Sprintf ( "diskFrontier from %s at %s to %s at %s" , f . firstFingerprint . ToRowKey ( ) , f . firstSupertime , f . lastFingerprint . ToRowKey ( ) , f . lastSupertime )
}
2013-05-17 03:58:15 -07:00
func ( f diskFrontier ) ContainsFingerprint ( fingerprint * model . Fingerprint ) bool {
2013-03-14 18:29:42 -07:00
return ! ( fingerprint . Less ( f . firstFingerprint ) || f . lastFingerprint . Less ( fingerprint ) )
2013-03-11 14:59:28 -07:00
}
2013-05-22 10:06:06 -07:00
func newDiskFrontier ( i leveldb . Iterator ) ( d * diskFrontier , present bool , err error ) {
2013-03-25 02:24:59 -07:00
if ! i . SeekToLast ( ) || i . Key ( ) == nil {
2013-05-22 10:06:06 -07:00
return nil , false , nil
2013-02-08 09:03:26 -08:00
}
lastKey , err := extractSampleKey ( i )
if err != nil {
2013-05-22 10:06:06 -07:00
return nil , false , err
2013-02-08 09:03:26 -08:00
}
2013-04-01 04:22:38 -07:00
if ! i . SeekToFirst ( ) || i . Key ( ) == nil {
2013-05-22 10:06:06 -07:00
return nil , false , nil
2013-04-01 04:22:38 -07:00
}
2013-02-08 09:03:26 -08:00
firstKey , err := extractSampleKey ( i )
if err != nil {
2013-05-22 10:06:06 -07:00
return nil , false , err
2013-02-08 09:03:26 -08:00
}
d = & diskFrontier { }
2013-04-21 10:16:15 -07:00
d . firstFingerprint = firstKey . Fingerprint
d . firstSupertime = firstKey . FirstTimestamp
d . lastFingerprint = lastKey . Fingerprint
d . lastSupertime = lastKey . FirstTimestamp
2013-02-08 09:03:26 -08:00
2013-05-22 10:06:06 -07:00
return d , true , nil
2013-02-08 09:03:26 -08:00
}
// seriesFrontier represents the valid seek frontier for a given series.
type seriesFrontier struct {
firstSupertime time . Time
lastSupertime time . Time
lastTime time . Time
}
func ( f seriesFrontier ) String ( ) string {
return fmt . Sprintf ( "seriesFrontier from %s to %s at %s" , f . firstSupertime , f . lastSupertime , f . lastTime )
}
// newSeriesFrontier furnishes a populated diskFrontier for a given
2013-05-22 10:06:06 -07:00
// fingerprint. If the series is absent, present will be false.
func newSeriesFrontier ( f * model . Fingerprint , d * diskFrontier , i leveldb . Iterator ) ( s * seriesFrontier , present bool , err error ) {
2013-04-05 09:03:45 -07:00
lowerSeek := firstSupertime
upperSeek := lastSupertime
2013-02-08 09:03:26 -08:00
2013-04-01 04:22:38 -07:00
// If the diskFrontier for this iterator says that the candidate fingerprint
// is outside of its seeking domain, there is no way that a seriesFrontier
// could be materialized. Simply bail.
if ! d . ContainsFingerprint ( f ) {
2013-05-22 10:06:06 -07:00
return nil , false , nil
2013-04-01 04:22:38 -07:00
}
2013-02-08 09:03:26 -08:00
// If we are either the first or the last key in the database, we need to use
// pessimistic boundary frontiers.
if f . Equal ( d . firstFingerprint ) {
lowerSeek = indexable . EncodeTime ( d . firstSupertime )
}
if f . Equal ( d . lastFingerprint ) {
upperSeek = indexable . EncodeTime ( d . lastSupertime )
}
2013-04-21 10:16:15 -07:00
// TODO: Convert this to SampleKey.ToPartialDTO.
2013-02-08 09:03:26 -08:00
key := & dto . SampleKey {
Fingerprint : f . ToDTO ( ) ,
Timestamp : upperSeek ,
}
2013-05-15 22:38:31 -07:00
raw := coding . NewPBEncoder ( key ) . MustEncode ( )
2013-02-08 09:03:26 -08:00
i . Seek ( raw )
if i . Key ( ) == nil {
2013-05-22 10:06:06 -07:00
return nil , false , fmt . Errorf ( "illegal condition: empty key" )
2013-02-08 09:03:26 -08:00
}
retrievedKey , err := extractSampleKey ( i )
if err != nil {
2013-03-01 09:51:36 -08:00
panic ( err )
2013-02-08 09:03:26 -08:00
}
2013-04-21 10:16:15 -07:00
retrievedFingerprint := retrievedKey . Fingerprint
2013-02-08 09:03:26 -08:00
// The returned fingerprint may not match if the original seek key lives
// outside of a metric's frontier. This is probable, for we are seeking to
// to the maximum allowed time, which could advance us to the next
// fingerprint.
//
//
if ! retrievedFingerprint . Equal ( f ) {
2013-03-25 02:24:59 -07:00
i . Previous ( )
2013-02-08 09:03:26 -08:00
retrievedKey , err = extractSampleKey ( i )
if err != nil {
2013-03-01 09:51:36 -08:00
panic ( err )
2013-02-08 09:03:26 -08:00
}
2013-04-21 10:16:15 -07:00
retrievedFingerprint := retrievedKey . Fingerprint
2013-02-08 09:03:26 -08:00
// If the previous key does not match, we know that the requested
// fingerprint does not live in the database.
if ! retrievedFingerprint . Equal ( f ) {
2013-05-22 10:06:06 -07:00
return nil , false , nil
2013-02-08 09:03:26 -08:00
}
}
s = & seriesFrontier {
2013-04-21 10:16:15 -07:00
lastSupertime : retrievedKey . FirstTimestamp ,
lastTime : retrievedKey . LastTimestamp ,
2013-02-08 09:03:26 -08:00
}
key . Timestamp = lowerSeek
2013-05-15 22:38:31 -07:00
raw = coding . NewPBEncoder ( key ) . MustEncode ( )
2013-02-08 09:03:26 -08:00
i . Seek ( raw )
retrievedKey , err = extractSampleKey ( i )
if err != nil {
2013-03-01 09:51:36 -08:00
panic ( err )
2013-02-08 09:03:26 -08:00
}
2013-04-21 10:16:15 -07:00
retrievedFingerprint = retrievedKey . Fingerprint
2013-02-08 09:03:26 -08:00
2013-04-21 10:16:15 -07:00
s . firstSupertime = retrievedKey . FirstTimestamp
2013-02-08 09:03:26 -08:00
2013-05-22 10:06:06 -07:00
return s , true , nil
2013-02-08 09:03:26 -08:00
}
2013-04-05 09:03:45 -07:00
// Contains indicates whether a given time value is within the recorded
// interval.
func ( s seriesFrontier ) Contains ( t time . Time ) bool {
return ! ( t . Before ( s . firstSupertime ) || t . After ( s . lastTime ) )
}
// InSafeSeekRange indicates whether the time is within the recorded time range
// and is safely seekable such that a seek does not result in an iterator point
// after the last value of the series or outside of the entire store.
func ( s seriesFrontier ) InSafeSeekRange ( t time . Time ) ( safe bool ) {
if ! s . Contains ( t ) {
return
}
if s . lastSupertime . Before ( t ) {
return
}
return true
}
func ( s seriesFrontier ) After ( t time . Time ) bool {
return s . firstSupertime . After ( t )
}
// optimalStartTime indicates what the best start time for a curation operation
// should be given the curation remark.
func ( s seriesFrontier ) optimalStartTime ( remark * model . CurationRemark ) ( t time . Time ) {
switch {
case remark == nil :
t = s . firstSupertime
case s . After ( remark . LastCompletionTimestamp ) :
t = s . firstSupertime
case ! s . InSafeSeekRange ( remark . LastCompletionTimestamp ) :
t = s . lastSupertime
default :
t = remark . LastCompletionTimestamp
}
return
}