2013-02-08 17:03:26 +00: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"
"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 {
firstFingerprint model . Fingerprint
firstSupertime time . Time
lastFingerprint model . Fingerprint
lastSupertime time . Time
}
func ( f * diskFrontier ) String ( ) string {
return fmt . Sprintf ( "diskFrontier from %s at %s to %s at %s" , f . firstFingerprint . ToRowKey ( ) , f . firstSupertime , f . lastFingerprint . ToRowKey ( ) , f . lastSupertime )
}
2013-03-11 21:59:28 +00:00
func ( f * diskFrontier ) ContainsFingerprint ( fingerprint model . Fingerprint ) bool {
2013-03-15 01:29:42 +00:00
return ! ( fingerprint . Less ( f . firstFingerprint ) || f . lastFingerprint . Less ( fingerprint ) )
2013-03-11 21:59:28 +00:00
}
2013-02-08 17:03:26 +00:00
func newDiskFrontier ( i iterator ) ( d * diskFrontier , err error ) {
i . SeekToLast ( )
2013-03-16 08:19:21 +00:00
if ! i . Valid ( ) || i . Key ( ) == nil {
2013-02-08 17:03:26 +00:00
return
}
lastKey , err := extractSampleKey ( i )
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
i . SeekToFirst ( )
firstKey , err := extractSampleKey ( i )
if i . Key ( ) == nil {
return
}
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
d = & diskFrontier { }
d . firstFingerprint = model . NewFingerprintFromRowKey ( * firstKey . Fingerprint . Signature )
d . firstSupertime = indexable . DecodeTime ( firstKey . Timestamp )
d . lastFingerprint = model . NewFingerprintFromRowKey ( * lastKey . Fingerprint . Signature )
d . lastSupertime = indexable . DecodeTime ( lastKey . Timestamp )
return
}
// 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
// fingerprint. A nil diskFrontier will be returned if the series cannot
// be found in the store.
func newSeriesFrontier ( f model . Fingerprint , d diskFrontier , i iterator ) ( s * seriesFrontier , err error ) {
var (
lowerSeek = firstSupertime
upperSeek = lastSupertime
)
// 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 )
}
key := & dto . SampleKey {
Fingerprint : f . ToDTO ( ) ,
Timestamp : upperSeek ,
}
raw , err := coding . NewProtocolBufferEncoder ( key ) . Encode ( )
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
i . Seek ( raw )
if i . Key ( ) == nil {
return
}
retrievedKey , err := extractSampleKey ( i )
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
retrievedFingerprint := model . NewFingerprintFromRowKey ( * retrievedKey . Fingerprint . Signature )
// 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 ) {
i . Prev ( )
retrievedKey , err = extractSampleKey ( i )
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
retrievedFingerprint := model . NewFingerprintFromRowKey ( * retrievedKey . Fingerprint . Signature )
// If the previous key does not match, we know that the requested
// fingerprint does not live in the database.
if ! retrievedFingerprint . Equal ( f ) {
return
}
}
s = & seriesFrontier {
lastSupertime : indexable . DecodeTime ( retrievedKey . Timestamp ) ,
lastTime : time . Unix ( * retrievedKey . LastTimestamp , 0 ) ,
}
key . Timestamp = lowerSeek
raw , err = coding . NewProtocolBufferEncoder ( key ) . Encode ( )
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
i . Seek ( raw )
retrievedKey , err = extractSampleKey ( i )
if err != nil {
2013-03-01 17:51:36 +00:00
panic ( err )
2013-02-08 17:03:26 +00:00
}
retrievedFingerprint = model . NewFingerprintFromRowKey ( * retrievedKey . Fingerprint . Signature )
s . firstSupertime = indexable . DecodeTime ( retrievedKey . Timestamp )
return
}