mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-11 13:57:36 -08:00
Initial directory re-arrangement for storage.
This commit is contained in:
parent
8af1458b39
commit
2bbdaa5790
|
@ -1,61 +0,0 @@
|
||||||
// {
|
|
||||||
// set evaluation_interval = "30s";
|
|
||||||
// target "http://www.example.com:80/metrics"
|
|
||||||
|
|
||||||
// rule archived {name="instance:requests_total:sum"} = sum by (instance) {name="requests"};
|
|
||||||
// rule archived {name="instance:requests-by_result_code_total:sum"} =
|
|
||||||
// sum by (instance,result_code) {name="requests"};
|
|
||||||
// rule archived {name="instance:requests-by_result_code:sum"} =
|
|
||||||
// {name="instances:requests-by_result_code"}
|
|
||||||
// / by (instance)
|
|
||||||
// {name="instances:requests_total:sum"};
|
|
||||||
// }
|
|
||||||
|
|
||||||
{
|
|
||||||
set evaluation_interval = “2m”;
|
|
||||||
|
|
||||||
permanent {
|
|
||||||
rule {
|
|
||||||
labels {
|
|
||||||
set name = “process:request_rate_qps-allowed:sum”;
|
|
||||||
set job = “frontend”;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Values may be a literal or an expression.
|
|
||||||
set value = 500;
|
|
||||||
|
|
||||||
// I wonder: Is it practical to express labels similar to above in a better DSL?
|
|
||||||
// set value = EXPRESSION … WITH LABELS {foo=”bar”};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rule {
|
|
||||||
// This provides a way of overriding existing labels, unsetting them, or
|
|
||||||
// appending new ones.
|
|
||||||
labels {
|
|
||||||
// “name” is obligatory.
|
|
||||||
set name = “process:requests_total:sum”;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we are extracting a metric with the name label value of “requests” from
|
|
||||||
// job “frontend” and merely accumulating this into a named variable. It is
|
|
||||||
// similar to standing. Each sum is keyed to the UNIX process and job from which
|
|
||||||
// it came.
|
|
||||||
set value = SUM({name=”requests”, job=”frontend”}) BY (process, job);
|
|
||||||
}
|
|
||||||
|
|
||||||
rule {
|
|
||||||
// This provides a way of overriding existing labels, unsetting them, or
|
|
||||||
// appending new ones.
|
|
||||||
labels {
|
|
||||||
// “name” is obligatory.
|
|
||||||
set name = “process:request_qps:rate5m”;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we are extracting a metric with the name label value of “requests” from
|
|
||||||
// job “frontend” and merely accumulating this into a named variable. It is
|
|
||||||
// similar to standing.
|
|
||||||
set value = RATE({name=”process:requests_total:sum”, job=”frontend”} OVER “5m”);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,506 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type itemType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
itemError itemType = iota
|
|
||||||
itemEof
|
|
||||||
itemRuleContextOpen
|
|
||||||
itemRuleContextClose
|
|
||||||
itemSetKeyword
|
|
||||||
itemKey
|
|
||||||
itemEqual
|
|
||||||
itemValue
|
|
||||||
itemSemicolon
|
|
||||||
itemQuote
|
|
||||||
itemRulesKeyword
|
|
||||||
itemRuleKeyword
|
|
||||||
itemRuleName
|
|
||||||
itemRuleValue
|
|
||||||
itemOpenBracket
|
|
||||||
itemCloseBracket
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
literalCloseBracket = "}"
|
|
||||||
literalEof = -1
|
|
||||||
literalOpenBracket = "{"
|
|
||||||
literalRule = "rule"
|
|
||||||
literalRules = "rules"
|
|
||||||
literalSet = "set"
|
|
||||||
literalEqual = "="
|
|
||||||
literalQuote = `"`
|
|
||||||
literalSemicolon = ";"
|
|
||||||
)
|
|
||||||
|
|
||||||
type element struct {
|
|
||||||
itemType itemType
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e element) String() string {
|
|
||||||
switch e.itemType {
|
|
||||||
case itemError:
|
|
||||||
return e.value
|
|
||||||
case itemEof:
|
|
||||||
return "EOF"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s %q", e.itemType, e.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
type lexer struct {
|
|
||||||
input string
|
|
||||||
elements chan element
|
|
||||||
start int
|
|
||||||
position int
|
|
||||||
runeWidth int
|
|
||||||
}
|
|
||||||
|
|
||||||
func lex(name, input string) (*lexer, chan element) {
|
|
||||||
l := &lexer{
|
|
||||||
input: input,
|
|
||||||
elements: make(chan element),
|
|
||||||
}
|
|
||||||
go l.run()
|
|
||||||
return l, l.elements
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) run() {
|
|
||||||
for state := lexBody; state != nil; {
|
|
||||||
state = state(l)
|
|
||||||
}
|
|
||||||
close(l.elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) next() (rune rune) {
|
|
||||||
if l.position >= len(l.input) {
|
|
||||||
l.runeWidth = 0
|
|
||||||
return literalEof
|
|
||||||
}
|
|
||||||
|
|
||||||
rune, l.runeWidth = utf8.DecodeRuneInString(l.input[l.position:])
|
|
||||||
l.position += l.runeWidth
|
|
||||||
return rune
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexBody(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalOpenBracket) {
|
|
||||||
return lexRulesetOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case rune == literalEof:
|
|
||||||
l.emit(itemEof)
|
|
||||||
return nil
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("illegal input")
|
|
||||||
}
|
|
||||||
return lexBody
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetOpen(l *lexer) lexFunction {
|
|
||||||
l.position += len(literalOpenBracket)
|
|
||||||
l.emit(itemRuleContextOpen)
|
|
||||||
|
|
||||||
return lexRulesetInside
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetInside(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalCloseBracket) {
|
|
||||||
return lexRulesetClose
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalSet) {
|
|
||||||
return lexRuleSetKeyword
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalRules) {
|
|
||||||
return lexRulesetRules
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case rune == literalEof:
|
|
||||||
return l.errorf("unterminated ruleset")
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
case rune == ';':
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
return lexRulesetInside
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRules(l *lexer) lexFunction {
|
|
||||||
l.position += len(literalRules)
|
|
||||||
l.emit(itemRulesKeyword)
|
|
||||||
|
|
||||||
return lexRulesetRulesBlockOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRulesBlockOpen(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalOpenBracket) {
|
|
||||||
l.position += len(literalOpenBracket)
|
|
||||||
l.emit(itemOpenBracket)
|
|
||||||
return lexRulesetRulesBlockInside
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexRulesetRulesBlockOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRulesBlockInside(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalRule) {
|
|
||||||
return lexRulesetRuleBegin
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalCloseBracket) {
|
|
||||||
return lexRulesetRulesBlockClose
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexRulesetRulesBlockInside
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRulesBlockClose(l *lexer) lexFunction {
|
|
||||||
l.position += len(literalCloseBracket)
|
|
||||||
l.emit(itemCloseBracket)
|
|
||||||
|
|
||||||
return lexRulesetInside
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRuleBegin(l *lexer) lexFunction {
|
|
||||||
l.position += len(literalRule)
|
|
||||||
l.emit(itemRuleKeyword)
|
|
||||||
|
|
||||||
return lexRulesetRuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRuleName(l *lexer) lexFunction {
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
case isIdentifierOpen(rune):
|
|
||||||
for {
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isMetricIdentifier(rune):
|
|
||||||
case rune == '=':
|
|
||||||
l.backup()
|
|
||||||
l.emit(itemRuleName)
|
|
||||||
return lexRulesetRuleEqual
|
|
||||||
default:
|
|
||||||
return l.errorf("bad rule name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexRulesetRuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRuleEqual(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalEqual) {
|
|
||||||
l.position += len(literalEqual)
|
|
||||||
l.emit(itemEqual)
|
|
||||||
return lexRulesetRuleDefinitionBegin
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexRulesetRuleEqual
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetRuleDefinitionBegin(l *lexer) lexFunction {
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
case isIdentifierOpen(rune):
|
|
||||||
for {
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isMetricIdentifier(rune):
|
|
||||||
case rune == ';':
|
|
||||||
l.emit(itemRuleValue)
|
|
||||||
return lexRulesetRulesBlockInside
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexRulesetRuleDefinitionBegin
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetKeyword(l *lexer) lexFunction {
|
|
||||||
l.position += len(literalSet)
|
|
||||||
|
|
||||||
l.emit(itemSetKeyword)
|
|
||||||
|
|
||||||
return lexRuleSetInside
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) backup() {
|
|
||||||
l.position -= l.runeWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentifierOpen(rune rune) bool {
|
|
||||||
switch rune := rune; {
|
|
||||||
case unicode.IsLetter(rune):
|
|
||||||
return true
|
|
||||||
case rune == '_':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetInside(l *lexer) lexFunction {
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case rune == literalEof:
|
|
||||||
return l.errorf("unterminated set statement")
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
case rune == ';':
|
|
||||||
return l.errorf("unexpected ;")
|
|
||||||
case rune == '=':
|
|
||||||
return l.errorf("unexpected =")
|
|
||||||
case isIdentifierOpen(rune):
|
|
||||||
l.backup()
|
|
||||||
return lexRuleSetKey
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized input")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexRuleSetInside
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentifier(rune rune) bool {
|
|
||||||
switch rune := rune; {
|
|
||||||
case isIdentifierOpen(rune):
|
|
||||||
return true
|
|
||||||
case unicode.IsDigit(rune):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isMetricIdentifier(rune rune) bool {
|
|
||||||
switch rune := rune; {
|
|
||||||
case isIdentifier(rune):
|
|
||||||
return true
|
|
||||||
case rune == ':':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) peek() rune {
|
|
||||||
rune := l.next()
|
|
||||||
l.backup()
|
|
||||||
return rune
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) atTerminator() bool {
|
|
||||||
switch rune := l.peek(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
return true
|
|
||||||
case rune == ';':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetKey(l *lexer) lexFunction {
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case rune == literalEof:
|
|
||||||
return l.errorf("incomplete set statement")
|
|
||||||
case isIdentifier(rune):
|
|
||||||
default:
|
|
||||||
l.backup()
|
|
||||||
if !l.atTerminator() {
|
|
||||||
return l.errorf("unexpected character %+U %q", rune, rune)
|
|
||||||
}
|
|
||||||
l.emit(itemKey)
|
|
||||||
return lexRuleSetEqual
|
|
||||||
}
|
|
||||||
return lexRuleSetKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetEqual(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalEqual) {
|
|
||||||
l.position += len(literalEqual)
|
|
||||||
l.emit(itemEqual)
|
|
||||||
return lexRuleSetValueOpenQuote
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case rune == literalEof:
|
|
||||||
return l.errorf("incomplete set statement")
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unexpected character %+U %q", rune, rune)
|
|
||||||
}
|
|
||||||
return lexRuleSetEqual
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetValueOpenQuote(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalQuote) {
|
|
||||||
l.position += len(literalQuote)
|
|
||||||
l.emit(itemQuote)
|
|
||||||
|
|
||||||
return lexRuleSetValue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case rune == literalEof:
|
|
||||||
return l.errorf("incomplete set statement")
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unexpected character %+U %q", rune, rune)
|
|
||||||
}
|
|
||||||
return lexRuleSetValueOpenQuote
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetValue(l *lexer) lexFunction {
|
|
||||||
var lastRuneEscapes bool = false
|
|
||||||
|
|
||||||
for {
|
|
||||||
rune := l.next()
|
|
||||||
{
|
|
||||||
if rune == '"' && !lastRuneEscapes {
|
|
||||||
l.backup()
|
|
||||||
l.emit(itemValue)
|
|
||||||
return lexRuleSetValueCloseQuote
|
|
||||||
}
|
|
||||||
|
|
||||||
if !lastRuneEscapes && rune == '\\' {
|
|
||||||
lastRuneEscapes = true
|
|
||||||
} else {
|
|
||||||
lastRuneEscapes = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetValueCloseQuote(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalQuote) {
|
|
||||||
l.position += len(literalQuote)
|
|
||||||
l.emit(itemQuote)
|
|
||||||
|
|
||||||
return lexRuleSetSemicolon
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unexpected character %+U %q", rune, rune)
|
|
||||||
|
|
||||||
}
|
|
||||||
return lexRuleSetValueCloseQuote
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRuleSetSemicolon(l *lexer) lexFunction {
|
|
||||||
if strings.HasPrefix(l.input[l.position:], literalSemicolon) {
|
|
||||||
l.position += len(literalSemicolon)
|
|
||||||
l.emit(itemSemicolon)
|
|
||||||
return lexRulesetInside
|
|
||||||
}
|
|
||||||
|
|
||||||
switch rune := l.next(); {
|
|
||||||
case isSpace(rune):
|
|
||||||
l.ignore()
|
|
||||||
default:
|
|
||||||
return l.errorf("unexpected character %+U %q", rune, rune)
|
|
||||||
}
|
|
||||||
return lexRuleSetSemicolon
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) ignore() {
|
|
||||||
l.start = l.position
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) errorf(format string, args ...interface{}) lexFunction {
|
|
||||||
l.elements <- element{itemError, fmt.Sprintf(format, args...)}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSpace(rune rune) bool {
|
|
||||||
switch rune {
|
|
||||||
case ' ', '\t', '\n', '\r':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexRulesetClose(l *lexer) lexFunction {
|
|
||||||
l.position += len(literalCloseBracket)
|
|
||||||
l.emit(itemCloseBracket)
|
|
||||||
|
|
||||||
return lexBody
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) emit(i itemType) {
|
|
||||||
l.elements <- element{i, l.input[l.start:l.position]}
|
|
||||||
l.start = l.position
|
|
||||||
}
|
|
||||||
|
|
||||||
type lexFunction func(*lexer) lexFunction
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
in := `{
|
|
||||||
set evaluation_interval = "10m";
|
|
||||||
|
|
||||||
rules {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
set name = "your mom";
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
{
|
|
||||||
set evaluation_interval = "30m";
|
|
||||||
}`
|
|
||||||
fmt.Println(in)
|
|
||||||
_, v := lex("", in)
|
|
||||||
for value := range v {
|
|
||||||
fmt.Println(value)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,192 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type lexTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
elements []element
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tEof = element{itemEof, ""}
|
|
||||||
tOpenRuleset = element{itemOpenBracket, "{"}
|
|
||||||
tCloseBracket = element{itemCloseBracket, "}"}
|
|
||||||
tIllegal = element{itemError, "illegal input"}
|
|
||||||
tSet = element{itemSetKeyword, "set"}
|
|
||||||
tEqual = element{itemEqual, "="}
|
|
||||||
tQuote = element{itemQuote, `"`}
|
|
||||||
tSemicolon = element{itemSemicolon, ";"}
|
|
||||||
tRules = element{itemRulesKeyword, "rules"}
|
|
||||||
)
|
|
||||||
|
|
||||||
var lexTests = []lexTest{
|
|
||||||
{
|
|
||||||
"empty",
|
|
||||||
"",
|
|
||||||
[]element{
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"empty with new line",
|
|
||||||
"\n\n",
|
|
||||||
[]element{
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"one empty ruleset",
|
|
||||||
"{}",
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"one empty ruleset distributed over new line",
|
|
||||||
"{\n}",
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two empty rulesets",
|
|
||||||
"{} {}",
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tCloseBracket,
|
|
||||||
tOpenRuleset,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two empty rulesets distributed over new line",
|
|
||||||
"{}\n{}",
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tCloseBracket,
|
|
||||||
tOpenRuleset,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"garbage",
|
|
||||||
"garbage",
|
|
||||||
[]element{tIllegal},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"one set",
|
|
||||||
`{ set foo = "bar"; }`,
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tSet,
|
|
||||||
element{itemKey, "foo"},
|
|
||||||
tEqual,
|
|
||||||
tQuote,
|
|
||||||
element{itemValue, "bar"},
|
|
||||||
tQuote,
|
|
||||||
tSemicolon,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"one set over multiple lines",
|
|
||||||
`{
|
|
||||||
set
|
|
||||||
foo
|
|
||||||
=
|
|
||||||
"bar"
|
|
||||||
;
|
|
||||||
}`,
|
|
||||||
[]element{tOpenRuleset, tSet, element{itemKey, "foo"}, tEqual, tQuote, element{itemValue, "bar"}, tQuote, tSemicolon, tCloseBracket, tEof},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two sets",
|
|
||||||
`{ set foo = "bar";set baz = "qux"; }`,
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tSet,
|
|
||||||
element{itemKey, "foo"},
|
|
||||||
tEqual,
|
|
||||||
tQuote,
|
|
||||||
element{itemValue, "bar"},
|
|
||||||
tQuote,
|
|
||||||
tSemicolon,
|
|
||||||
tSet,
|
|
||||||
element{itemKey, "baz"},
|
|
||||||
tEqual,
|
|
||||||
tQuote,
|
|
||||||
element{itemValue, "qux"},
|
|
||||||
tQuote,
|
|
||||||
tSemicolon,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two over multiple lines",
|
|
||||||
`{ set foo = "bar";
|
|
||||||
set
|
|
||||||
baz
|
|
||||||
=
|
|
||||||
"qux"
|
|
||||||
;
|
|
||||||
}`,
|
|
||||||
[]element{
|
|
||||||
tOpenRuleset,
|
|
||||||
tSet,
|
|
||||||
element{itemKey, "foo"},
|
|
||||||
tEqual,
|
|
||||||
tQuote,
|
|
||||||
element{itemValue, "bar"},
|
|
||||||
tQuote,
|
|
||||||
tSemicolon,
|
|
||||||
tSet,
|
|
||||||
element{itemKey, "baz"},
|
|
||||||
tEqual,
|
|
||||||
tQuote,
|
|
||||||
element{itemValue, "qux"},
|
|
||||||
tQuote,
|
|
||||||
tSemicolon,
|
|
||||||
tCloseBracket,
|
|
||||||
tEof,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func collect(l *lexTest) []element {
|
|
||||||
_, v := lex("", l.input)
|
|
||||||
|
|
||||||
emission := make([]element, 0)
|
|
||||||
|
|
||||||
for i := range v {
|
|
||||||
emission = append(emission, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return emission
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFoo(t *testing.T) {
|
|
||||||
for _, test := range lexTests {
|
|
||||||
e := collect(&test)
|
|
||||||
|
|
||||||
if len(e) != len(test.elements) {
|
|
||||||
t.Errorf("%s: got\n\n\t%v\nexpected\n\n\t%v", test.name, e, test.elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, _ := range test.elements {
|
|
||||||
if test.elements[i] != e[i] {
|
|
||||||
t.Errorf("%s[%d]: got\n\n\t%v\nexpected\n\n\t%v", test.name, i, e[i], test.elements[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
3
main.go
3
main.go
|
@ -2,11 +2,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/gorest"
|
"code.google.com/p/gorest"
|
||||||
|
"github.com/matttproud/prometheus/storage/metric/leveldb"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
m, _ := NewLevigoMetricPersistence("/tmp/metrics")
|
m, _ := leveldb.NewLevigoMetricPersistence("/tmp/metrics")
|
||||||
s := &MetricsService{
|
s := &MetricsService{
|
||||||
persistence: m,
|
persistence: m,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
type MetricPersistence interface {
|
|
||||||
Close() error
|
|
||||||
AppendSample(sample *Sample) error
|
|
||||||
|
|
||||||
GetLabelNames() ([]string, error)
|
|
||||||
GetLabelPairs() ([]LabelPairs, error)
|
|
||||||
GetMetrics() ([]LabelPairs, error)
|
|
||||||
|
|
||||||
GetMetricFingerprintsForLabelPairs(labelSets []*LabelPairs) ([]*Fingerprint, error)
|
|
||||||
RecordLabelNameFingerprint(sample *Sample) error
|
|
||||||
RecordFingerprintWatermark(sample *Sample) error
|
|
||||||
GetFingerprintLabelPairs(fingerprint Fingerprint) (LabelPairs, error)
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
16
service.go
16
service.go
|
@ -2,18 +2,20 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/gorest"
|
"code.google.com/p/gorest"
|
||||||
|
"github.com/matttproud/prometheus/model"
|
||||||
|
"github.com/matttproud/prometheus/storage/metric/leveldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MetricsService struct {
|
type MetricsService struct {
|
||||||
gorest.RestService `root:"/" consumes:"application/json" produces:"application/json"`
|
gorest.RestService `root:"/" consumes:"application/json" produces:"application/json"`
|
||||||
|
|
||||||
persistence *LevigoMetricPersistence
|
persistence *leveldb.LevigoMetricPersistence
|
||||||
|
|
||||||
listLabels gorest.EndPoint `method:"GET" path:"/labels/" output:"[]string"`
|
listLabels gorest.EndPoint `method:"GET" path:"/labels/" output:"[]string"`
|
||||||
listLabelPairs gorest.EndPoint `method:"GET" path:"/label-pairs/" output:"[]LabelPairs"`
|
listLabelPairs gorest.EndPoint `method:"GET" path:"/label-pairs/" output:"[]model.LabelPairs"`
|
||||||
listMetrics gorest.EndPoint `method:"GET" path:"/metrics/" output:"[]LabelPairs"`
|
listMetrics gorest.EndPoint `method:"GET" path:"/metrics/" output:"[]model.LabelPairs"`
|
||||||
|
|
||||||
appendSample gorest.EndPoint `method:"POST" path:"/metrics/" postdata:"Sample"`
|
appendSample gorest.EndPoint `method:"POST" path:"/metrics/" postdata:"model.Sample"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MetricsService) ListLabels() []string {
|
func (m MetricsService) ListLabels() []string {
|
||||||
|
@ -26,7 +28,7 @@ func (m MetricsService) ListLabels() []string {
|
||||||
return labels
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MetricsService) ListLabelPairs() []LabelPairs {
|
func (m MetricsService) ListLabelPairs() []model.LabelPairs {
|
||||||
labelPairs, labelPairsError := m.persistence.GetLabelPairs()
|
labelPairs, labelPairsError := m.persistence.GetLabelPairs()
|
||||||
|
|
||||||
if labelPairsError != nil {
|
if labelPairsError != nil {
|
||||||
|
@ -36,7 +38,7 @@ func (m MetricsService) ListLabelPairs() []LabelPairs {
|
||||||
return labelPairs
|
return labelPairs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MetricsService) ListMetrics() []LabelPairs {
|
func (m MetricsService) ListMetrics() []model.LabelPairs {
|
||||||
metrics, metricsError := m.persistence.GetMetrics()
|
metrics, metricsError := m.persistence.GetMetrics()
|
||||||
|
|
||||||
if metricsError != nil {
|
if metricsError != nil {
|
||||||
|
@ -46,7 +48,7 @@ func (m MetricsService) ListMetrics() []LabelPairs {
|
||||||
return metrics
|
return metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MetricsService) AppendSample(s Sample) {
|
func (m MetricsService) AppendSample(s model.Sample) {
|
||||||
responseBuilder := m.ResponseBuilder()
|
responseBuilder := m.ResponseBuilder()
|
||||||
if appendError := m.persistence.AppendSample(&s); appendError == nil {
|
if appendError := m.persistence.AppendSample(&s); appendError == nil {
|
||||||
responseBuilder.SetResponseCode(200)
|
responseBuilder.SetResponseCode(200)
|
||||||
|
|
19
storage/metric/interface.go
Normal file
19
storage/metric/interface.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/matttproud/prometheus/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MetricPersistence interface {
|
||||||
|
Close() error
|
||||||
|
AppendSample(sample *model.Sample) error
|
||||||
|
|
||||||
|
GetLabelNames() ([]string, error)
|
||||||
|
GetLabelPairs() ([]model.LabelPairs, error)
|
||||||
|
GetMetrics() ([]model.LabelPairs, error)
|
||||||
|
|
||||||
|
GetMetricFingerprintsForLabelPairs(labelSets []*model.LabelPairs) ([]*model.Fingerprint, error)
|
||||||
|
RecordLabelNameFingerprint(sample *model.Sample) error
|
||||||
|
RecordFingerprintWatermark(sample *model.Sample) error
|
||||||
|
GetFingerprintLabelPairs(fingerprint model.Fingerprint) (model.LabelPairs, error)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package leveldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/goprotobuf/proto"
|
"code.google.com/p/goprotobuf/proto"
|
||||||
|
@ -6,30 +6,34 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matttproud/prometheus/coding"
|
"github.com/matttproud/prometheus/coding"
|
||||||
"github.com/matttproud/prometheus/coding/indexable"
|
"github.com/matttproud/prometheus/coding/indexable"
|
||||||
|
"github.com/matttproud/prometheus/model"
|
||||||
data "github.com/matttproud/prometheus/model/generated"
|
data "github.com/matttproud/prometheus/model/generated"
|
||||||
|
index "github.com/matttproud/prometheus/storage/raw/index/leveldb"
|
||||||
|
storage "github.com/matttproud/prometheus/storage/raw/leveldb"
|
||||||
"github.com/matttproud/prometheus/utility"
|
"github.com/matttproud/prometheus/utility"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pendingArchival map[int64]float64
|
|
||||||
|
|
||||||
type LevigoMetricPersistence struct {
|
type LevigoMetricPersistence struct {
|
||||||
fingerprintHighWaterMarks *LevigoPersistence
|
fingerprintHighWaterMarks *storage.LevigoPersistence
|
||||||
fingerprintLabelPairs *LevigoPersistence
|
fingerprintLabelPairs *storage.LevigoPersistence
|
||||||
fingerprintLowWaterMarks *LevigoPersistence
|
fingerprintLowWaterMarks *storage.LevigoPersistence
|
||||||
fingerprintSamples *LevigoPersistence
|
fingerprintSamples *storage.LevigoPersistence
|
||||||
labelNameFingerprints *LevigoPersistence
|
labelNameFingerprints *storage.LevigoPersistence
|
||||||
labelPairFingerprints *LevigoPersistence
|
labelPairFingerprints *storage.LevigoPersistence
|
||||||
metricMembershipIndex *LevigoMembershipIndex
|
metricMembershipIndex *index.LevigoMembershipIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type levigoOpener func()
|
||||||
|
|
||||||
func (l *LevigoMetricPersistence) Close() error {
|
func (l *LevigoMetricPersistence) Close() error {
|
||||||
log.Printf("Closing LevigoPersistence storage containers...")
|
log.Printf("Closing LevigoPersistence storage containers...")
|
||||||
|
|
||||||
var persistences = []struct {
|
var persistences = []struct {
|
||||||
name string
|
name string
|
||||||
closer LevigoCloser
|
closer io.Closer
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"Fingerprint High-Water Marks",
|
"Fingerprint High-Water Marks",
|
||||||
|
@ -94,8 +98,6 @@ func (l *LevigoMetricPersistence) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type levigoOpener func()
|
|
||||||
|
|
||||||
func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence, error) {
|
func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence, error) {
|
||||||
log.Printf("Opening LevigoPersistence storage containers...")
|
log.Printf("Opening LevigoPersistence storage containers...")
|
||||||
|
|
||||||
|
@ -111,7 +113,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"High-Water Marks by Fingerprint",
|
"High-Water Marks by Fingerprint",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.fingerprintHighWaterMarks, anomaly = NewLevigoPersistence(baseDirectory+"/high_water_marks_by_fingerprint", 1000000, 10)
|
emission.fingerprintHighWaterMarks, anomaly = storage.NewLevigoPersistence(baseDirectory+"/high_water_marks_by_fingerprint", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -119,7 +121,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"Label Names and Value Pairs by Fingerprint",
|
"Label Names and Value Pairs by Fingerprint",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.fingerprintLabelPairs, anomaly = NewLevigoPersistence(baseDirectory+"/label_name_and_value_pairs_by_fingerprint", 1000000, 10)
|
emission.fingerprintLabelPairs, anomaly = storage.NewLevigoPersistence(baseDirectory+"/label_name_and_value_pairs_by_fingerprint", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -127,7 +129,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"Low-Water Marks by Fingerprint",
|
"Low-Water Marks by Fingerprint",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.fingerprintLowWaterMarks, anomaly = NewLevigoPersistence(baseDirectory+"/low_water_marks_by_fingerprint", 1000000, 10)
|
emission.fingerprintLowWaterMarks, anomaly = storage.NewLevigoPersistence(baseDirectory+"/low_water_marks_by_fingerprint", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -135,7 +137,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"Samples by Fingerprint",
|
"Samples by Fingerprint",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.fingerprintSamples, anomaly = NewLevigoPersistence(baseDirectory+"/samples_by_fingerprint", 1000000, 10)
|
emission.fingerprintSamples, anomaly = storage.NewLevigoPersistence(baseDirectory+"/samples_by_fingerprint", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -143,7 +145,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"Fingerprints by Label Name",
|
"Fingerprints by Label Name",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.labelNameFingerprints, anomaly = NewLevigoPersistence(baseDirectory+"/fingerprints_by_label_name", 1000000, 10)
|
emission.labelNameFingerprints, anomaly = storage.NewLevigoPersistence(baseDirectory+"/fingerprints_by_label_name", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -151,7 +153,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"Fingerprints by Label Name and Value Pair",
|
"Fingerprints by Label Name and Value Pair",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.labelPairFingerprints, anomaly = NewLevigoPersistence(baseDirectory+"/fingerprints_by_label_name_and_value_pair", 1000000, 10)
|
emission.labelPairFingerprints, anomaly = storage.NewLevigoPersistence(baseDirectory+"/fingerprints_by_label_name_and_value_pair", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -159,7 +161,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
"Metric Membership Index",
|
"Metric Membership Index",
|
||||||
func() {
|
func() {
|
||||||
var anomaly error
|
var anomaly error
|
||||||
emission.metricMembershipIndex, anomaly = NewLevigoMembershipIndex(baseDirectory+"/metric_membership_index", 1000000, 10)
|
emission.metricMembershipIndex, anomaly = index.NewLevigoMembershipIndex(baseDirectory+"/metric_membership_index", 1000000, 10)
|
||||||
errorChannel <- anomaly
|
errorChannel <- anomaly
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -190,7 +192,7 @@ func NewLevigoMetricPersistence(baseDirectory string) (*LevigoMetricPersistence,
|
||||||
return emission, nil
|
return emission, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ddoFromSample(sample *Sample) *data.MetricDDO {
|
func ddoFromSample(sample *model.Sample) *data.MetricDDO {
|
||||||
labelNames := make([]string, 0, len(sample.Labels))
|
labelNames := make([]string, 0, len(sample.Labels))
|
||||||
|
|
||||||
for labelName, _ := range sample.Labels {
|
for labelName, _ := range sample.Labels {
|
||||||
|
@ -218,7 +220,7 @@ func ddoFromSample(sample *Sample) *data.MetricDDO {
|
||||||
return metricDDO
|
return metricDDO
|
||||||
}
|
}
|
||||||
|
|
||||||
func ddoFromMetric(metric Metric) *data.MetricDDO {
|
func ddoFromMetric(metric model.Metric) *data.MetricDDO {
|
||||||
labelNames := make([]string, 0, len(metric))
|
labelNames := make([]string, 0, len(metric))
|
||||||
|
|
||||||
for labelName, _ := range metric {
|
for labelName, _ := range metric {
|
||||||
|
@ -266,7 +268,7 @@ func (l *LevigoMetricPersistence) indexMetric(ddo *data.MetricDDO) error {
|
||||||
|
|
||||||
func fingerprintDDOForMessage(message proto.Message) (*data.FingerprintDDO, error) {
|
func fingerprintDDOForMessage(message proto.Message) (*data.FingerprintDDO, error) {
|
||||||
if messageByteArray, marshalError := proto.Marshal(message); marshalError == nil {
|
if messageByteArray, marshalError := proto.Marshal(message); marshalError == nil {
|
||||||
fingerprint := FingerprintFromByteArray(messageByteArray)
|
fingerprint := model.FingerprintFromByteArray(messageByteArray)
|
||||||
return &data.FingerprintDDO{
|
return &data.FingerprintDDO{
|
||||||
Signature: proto.String(string(fingerprint)),
|
Signature: proto.String(string(fingerprint)),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -431,7 +433,7 @@ func (l *LevigoMetricPersistence) appendFingerprints(ddo *data.MetricDDO) error
|
||||||
return errors.New("Unknown error in appending label pairs to fingerprint.")
|
return errors.New("Unknown error in appending label pairs to fingerprint.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoMetricPersistence) AppendSample(sample *Sample) error {
|
func (l *LevigoMetricPersistence) AppendSample(sample *model.Sample) error {
|
||||||
fmt.Printf("Sample: %q\n", sample)
|
fmt.Printf("Sample: %q\n", sample)
|
||||||
|
|
||||||
metricDDO := ddoFromSample(sample)
|
metricDDO := ddoFromSample(sample)
|
||||||
|
@ -501,14 +503,14 @@ func (l *LevigoMetricPersistence) GetLabelNames() ([]string, error) {
|
||||||
return nil, errors.New("Unknown error encountered when querying label names.")
|
return nil, errors.New("Unknown error encountered when querying label names.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoMetricPersistence) GetLabelPairs() ([]LabelPairs, error) {
|
func (l *LevigoMetricPersistence) GetLabelPairs() ([]model.LabelPairs, error) {
|
||||||
if getAll, getAllError := l.labelPairFingerprints.GetAll(); getAllError == nil {
|
if getAll, getAllError := l.labelPairFingerprints.GetAll(); getAllError == nil {
|
||||||
result := make([]LabelPairs, 0, len(getAll))
|
result := make([]model.LabelPairs, 0, len(getAll))
|
||||||
labelPairDDO := &data.LabelPairDDO{}
|
labelPairDDO := &data.LabelPairDDO{}
|
||||||
|
|
||||||
for _, pair := range getAll {
|
for _, pair := range getAll {
|
||||||
if unmarshalError := proto.Unmarshal(pair.Left, labelPairDDO); unmarshalError == nil {
|
if unmarshalError := proto.Unmarshal(pair.Left, labelPairDDO); unmarshalError == nil {
|
||||||
item := LabelPairs{
|
item := model.LabelPairs{
|
||||||
*labelPairDDO.Name: *labelPairDDO.Value,
|
*labelPairDDO.Name: *labelPairDDO.Value,
|
||||||
}
|
}
|
||||||
result = append(result, item)
|
result = append(result, item)
|
||||||
|
@ -526,12 +528,12 @@ func (l *LevigoMetricPersistence) GetLabelPairs() ([]LabelPairs, error) {
|
||||||
return nil, errors.New("Unknown error encountered when querying label pairs.")
|
return nil, errors.New("Unknown error encountered when querying label pairs.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoMetricPersistence) GetMetrics() ([]LabelPairs, error) {
|
func (l *LevigoMetricPersistence) GetMetrics() ([]model.LabelPairs, error) {
|
||||||
log.Printf("GetMetrics()\n")
|
log.Printf("GetMetrics()\n")
|
||||||
|
|
||||||
if getAll, getAllError := l.labelPairFingerprints.GetAll(); getAllError == nil {
|
if getAll, getAllError := l.labelPairFingerprints.GetAll(); getAllError == nil {
|
||||||
log.Printf("getAll: %q\n", getAll)
|
log.Printf("getAll: %q\n", getAll)
|
||||||
result := make([]LabelPairs, 0)
|
result := make([]model.LabelPairs, 0)
|
||||||
fingerprintCollection := &data.FingerprintCollectionDDO{}
|
fingerprintCollection := &data.FingerprintCollectionDDO{}
|
||||||
|
|
||||||
fingerprints := make(utility.Set)
|
fingerprints := make(utility.Set)
|
||||||
|
@ -552,7 +554,7 @@ func (l *LevigoMetricPersistence) GetMetrics() ([]LabelPairs, error) {
|
||||||
labelPairCollectionDDO := &data.LabelPairCollectionDDO{}
|
labelPairCollectionDDO := &data.LabelPairCollectionDDO{}
|
||||||
|
|
||||||
if labelPairCollectionDDOMarshalError := proto.Unmarshal(labelPairCollectionRaw, labelPairCollectionDDO); labelPairCollectionDDOMarshalError == nil {
|
if labelPairCollectionDDOMarshalError := proto.Unmarshal(labelPairCollectionRaw, labelPairCollectionDDO); labelPairCollectionDDOMarshalError == nil {
|
||||||
intermediate := make(LabelPairs, 0)
|
intermediate := make(model.LabelPairs, 0)
|
||||||
|
|
||||||
for _, member := range labelPairCollectionDDO.Member {
|
for _, member := range labelPairCollectionDDO.Member {
|
||||||
intermediate[*member.Name] = *member.Value
|
intermediate[*member.Name] = *member.Value
|
||||||
|
@ -581,7 +583,7 @@ func (l *LevigoMetricPersistence) GetMetrics() ([]LabelPairs, error) {
|
||||||
return nil, errors.New("Unknown error encountered when querying metrics.")
|
return nil, errors.New("Unknown error encountered when querying metrics.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoMetricPersistence) GetWatermarksForMetric(metric Metric) (*Interval, int, error) {
|
func (l *LevigoMetricPersistence) GetWatermarksForMetric(metric model.Metric) (*model.Interval, int, error) {
|
||||||
metricDDO := ddoFromMetric(metric)
|
metricDDO := ddoFromMetric(metric)
|
||||||
|
|
||||||
if fingerprintDDO, fingerprintDDOErr := fingerprintDDOForMessage(metricDDO); fingerprintDDOErr == nil {
|
if fingerprintDDO, fingerprintDDOErr := fingerprintDDOForMessage(metricDDO); fingerprintDDOErr == nil {
|
||||||
|
@ -602,7 +604,7 @@ func (l *LevigoMetricPersistence) GetWatermarksForMetric(metric Metric) (*Interv
|
||||||
var foundEntries int = 0
|
var foundEntries int = 0
|
||||||
|
|
||||||
if *fingerprintDDO.Signature == *found.Fingerprint.Signature {
|
if *fingerprintDDO.Signature == *found.Fingerprint.Signature {
|
||||||
emission := &Interval{
|
emission := &model.Interval{
|
||||||
OldestInclusive: indexable.DecodeTime(found.Timestamp),
|
OldestInclusive: indexable.DecodeTime(found.Timestamp),
|
||||||
NewestInclusive: indexable.DecodeTime(found.Timestamp),
|
NewestInclusive: indexable.DecodeTime(found.Timestamp),
|
||||||
}
|
}
|
||||||
|
@ -622,7 +624,7 @@ func (l *LevigoMetricPersistence) GetWatermarksForMetric(metric Metric) (*Interv
|
||||||
}
|
}
|
||||||
return emission, foundEntries, nil
|
return emission, foundEntries, nil
|
||||||
} else {
|
} else {
|
||||||
return &Interval{}, -6, nil
|
return &model.Interval{}, -6, nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Could not de-serialize start key: %q\n", unmarshalErr)
|
log.Printf("Could not de-serialize start key: %q\n", unmarshalErr)
|
||||||
|
@ -655,7 +657,7 @@ func (l *LevigoMetricPersistence) GetWatermarksForMetric(metric Metric) (*Interv
|
||||||
|
|
||||||
// TODO(mtp): Holes in the data!
|
// TODO(mtp): Holes in the data!
|
||||||
|
|
||||||
func (l *LevigoMetricPersistence) GetSamplesForMetric(metric Metric, interval Interval) ([]Samples, error) {
|
func (l *LevigoMetricPersistence) GetSamplesForMetric(metric model.Metric, interval model.Interval) ([]model.Samples, error) {
|
||||||
metricDDO := ddoFromMetric(metric)
|
metricDDO := ddoFromMetric(metric)
|
||||||
|
|
||||||
if fingerprintDDO, fingerprintDDOErr := fingerprintDDOForMessage(metricDDO); fingerprintDDOErr == nil {
|
if fingerprintDDO, fingerprintDDOErr := fingerprintDDOForMessage(metricDDO); fingerprintDDOErr == nil {
|
||||||
|
@ -667,7 +669,7 @@ func (l *LevigoMetricPersistence) GetSamplesForMetric(metric Metric, interval In
|
||||||
Timestamp: indexable.EncodeTime(interval.OldestInclusive),
|
Timestamp: indexable.EncodeTime(interval.OldestInclusive),
|
||||||
}
|
}
|
||||||
|
|
||||||
emission := make([]Samples, 0)
|
emission := make([]model.Samples, 0)
|
||||||
|
|
||||||
if encode, encodeErr := coding.NewProtocolBufferEncoder(start).Encode(); encodeErr == nil {
|
if encode, encodeErr := coding.NewProtocolBufferEncoder(start).Encode(); encodeErr == nil {
|
||||||
iterator.Seek(encode)
|
iterator.Seek(encode)
|
||||||
|
@ -680,8 +682,8 @@ func (l *LevigoMetricPersistence) GetSamplesForMetric(metric Metric, interval In
|
||||||
if *fingerprintDDO.Signature == *key.Fingerprint.Signature {
|
if *fingerprintDDO.Signature == *key.Fingerprint.Signature {
|
||||||
// Wart
|
// Wart
|
||||||
if indexable.DecodeTime(key.Timestamp).Unix() <= interval.NewestInclusive.Unix() {
|
if indexable.DecodeTime(key.Timestamp).Unix() <= interval.NewestInclusive.Unix() {
|
||||||
emission = append(emission, Samples{
|
emission = append(emission, model.Samples{
|
||||||
Value: SampleValue(*value.Value),
|
Value: model.SampleValue(*value.Value),
|
||||||
Timestamp: indexable.DecodeTime(key.Timestamp),
|
Timestamp: indexable.DecodeTime(key.Timestamp),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
|
@ -1,8 +1,9 @@
|
||||||
package main
|
package leveldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/goprotobuf/proto"
|
"code.google.com/p/goprotobuf/proto"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/matttproud/prometheus/model"
|
||||||
data "github.com/matttproud/prometheus/model/generated"
|
data "github.com/matttproud/prometheus/model/generated"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
|
@ -168,10 +169,10 @@ func TestAppendSampleAsPureSparseAppend(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
appendSample := func(x int) bool {
|
appendSample := func(x int) bool {
|
||||||
sample := &Sample{
|
sample := &model.Sample{
|
||||||
Value: SampleValue(float32(x)),
|
Value: model.SampleValue(float32(x)),
|
||||||
Timestamp: time.Unix(int64(x), int64(x)),
|
Timestamp: time.Unix(int64(x), int64(x)),
|
||||||
Labels: LabelPairs{string(x): string(x)},
|
Labels: model.LabelPairs{string(x): string(x)},
|
||||||
}
|
}
|
||||||
|
|
||||||
appendErr := persistence.AppendSample(sample)
|
appendErr := persistence.AppendSample(sample)
|
||||||
|
@ -200,10 +201,10 @@ func TestAppendSampleAsSparseAppendWithReads(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
appendSample := func(x int) bool {
|
appendSample := func(x int) bool {
|
||||||
sample := &Sample{
|
sample := &model.Sample{
|
||||||
Value: SampleValue(float32(x)),
|
Value: model.SampleValue(float32(x)),
|
||||||
Timestamp: time.Unix(int64(x), int64(x)),
|
Timestamp: time.Unix(int64(x), int64(x)),
|
||||||
Labels: LabelPairs{string(x): string(x)},
|
Labels: model.LabelPairs{string(x): string(x)},
|
||||||
}
|
}
|
||||||
|
|
||||||
appendErr := persistence.AppendSample(sample)
|
appendErr := persistence.AppendSample(sample)
|
||||||
|
@ -293,10 +294,10 @@ func TestAppendSampleAsPureSingleEntityAppend(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
appendSample := func(x int) bool {
|
appendSample := func(x int) bool {
|
||||||
sample := &Sample{
|
sample := &model.Sample{
|
||||||
Value: SampleValue(float32(x)),
|
Value: model.SampleValue(float32(x)),
|
||||||
Timestamp: time.Unix(int64(x), 0),
|
Timestamp: time.Unix(int64(x), 0),
|
||||||
Labels: LabelPairs{"name": "my_metric"},
|
Labels: model.LabelPairs{"name": "my_metric"},
|
||||||
}
|
}
|
||||||
|
|
||||||
appendErr := persistence.AppendSample(sample)
|
appendErr := persistence.AppendSample(sample)
|
||||||
|
@ -338,8 +339,8 @@ func TestStochastic(t *testing.T) {
|
||||||
metricNewestSample := make(map[int]int64)
|
metricNewestSample := make(map[int]int64)
|
||||||
|
|
||||||
for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ {
|
for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ {
|
||||||
sample := &Sample{
|
sample := &model.Sample{
|
||||||
Labels: LabelPairs{},
|
Labels: model.LabelPairs{},
|
||||||
}
|
}
|
||||||
|
|
||||||
sample.Labels["name"] = fmt.Sprintf("metric_index_%d", metricIndex)
|
sample.Labels["name"] = fmt.Sprintf("metric_index_%d", metricIndex)
|
||||||
|
@ -381,7 +382,7 @@ func TestStochastic(t *testing.T) {
|
||||||
|
|
||||||
for sampleIndex := 0; sampleIndex < numberOfSamples; sampleIndex++ {
|
for sampleIndex := 0; sampleIndex < numberOfSamples; sampleIndex++ {
|
||||||
sample.Timestamp = time.Unix(nextTimestamp(), 0)
|
sample.Timestamp = time.Unix(nextTimestamp(), 0)
|
||||||
sample.Value = SampleValue(sampleIndex)
|
sample.Value = model.SampleValue(sampleIndex)
|
||||||
|
|
||||||
appendErr := persistence.AppendSample(sample)
|
appendErr := persistence.AppendSample(sample)
|
||||||
|
|
||||||
|
@ -504,7 +505,7 @@ func TestStochastic(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metric := make(Metric)
|
metric := make(model.Metric)
|
||||||
|
|
||||||
metric["name"] = fmt.Sprintf("metric_index_%d", metricIndex)
|
metric["name"] = fmt.Sprintf("metric_index_%d", metricIndex)
|
||||||
|
|
||||||
|
@ -565,7 +566,7 @@ func TestStochastic(t *testing.T) {
|
||||||
end = second
|
end = second
|
||||||
}
|
}
|
||||||
|
|
||||||
interval := Interval{
|
interval := model.Interval{
|
||||||
OldestInclusive: time.Unix(begin, 0),
|
OldestInclusive: time.Unix(begin, 0),
|
||||||
NewestInclusive: time.Unix(end, 0),
|
NewestInclusive: time.Unix(end, 0),
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/matttproud/prometheus/coding"
|
"github.com/matttproud/prometheus/coding"
|
|
@ -1,8 +1,9 @@
|
||||||
package main
|
package leveldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/matttproud/prometheus/coding"
|
"github.com/matttproud/prometheus/coding"
|
||||||
data "github.com/matttproud/prometheus/model/generated"
|
data "github.com/matttproud/prometheus/model/generated"
|
||||||
|
"github.com/matttproud/prometheus/storage/raw/leveldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -10,7 +11,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type LevigoMembershipIndex struct {
|
type LevigoMembershipIndex struct {
|
||||||
persistence *LevigoPersistence
|
persistence *leveldb.LevigoPersistence
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoMembershipIndex) Close() error {
|
func (l *LevigoMembershipIndex) Close() error {
|
||||||
|
@ -30,10 +31,10 @@ func (l *LevigoMembershipIndex) Put(key coding.Encoder) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLevigoMembershipIndex(storageRoot string, cacheCapacity, bitsPerBloomFilterEncoded int) (*LevigoMembershipIndex, error) {
|
func NewLevigoMembershipIndex(storageRoot string, cacheCapacity, bitsPerBloomFilterEncoded int) (*LevigoMembershipIndex, error) {
|
||||||
var levigoPersistence *LevigoPersistence
|
var levigoPersistence *leveldb.LevigoPersistence
|
||||||
var levigoPersistenceError error
|
var levigoPersistenceError error
|
||||||
|
|
||||||
if levigoPersistence, levigoPersistenceError = NewLevigoPersistence(storageRoot, cacheCapacity, bitsPerBloomFilterEncoded); levigoPersistenceError == nil {
|
if levigoPersistence, levigoPersistenceError = leveldb.NewLevigoPersistence(storageRoot, cacheCapacity, bitsPerBloomFilterEncoded); levigoPersistenceError == nil {
|
||||||
levigoMembershipIndex := &LevigoMembershipIndex{
|
levigoMembershipIndex := &LevigoMembershipIndex{
|
||||||
persistence: levigoPersistence,
|
persistence: levigoPersistence,
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package raw
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/matttproud/prometheus/coding"
|
"github.com/matttproud/prometheus/coding"
|
|
@ -1,15 +1,12 @@
|
||||||
package main
|
package leveldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jmhodges/levigo"
|
"github.com/jmhodges/levigo"
|
||||||
"github.com/matttproud/prometheus/coding"
|
"github.com/matttproud/prometheus/coding"
|
||||||
|
"github.com/matttproud/prometheus/storage/raw"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LevigoCloser interface {
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type LevigoPersistence struct {
|
type LevigoPersistence struct {
|
||||||
cache *levigo.Cache
|
cache *levigo.Cache
|
||||||
filterPolicy *levigo.FilterPolicy
|
filterPolicy *levigo.FilterPolicy
|
||||||
|
@ -19,6 +16,13 @@ type LevigoPersistence struct {
|
||||||
writeOptions *levigo.WriteOptions
|
writeOptions *levigo.WriteOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type iteratorCloser struct {
|
||||||
|
iterator *levigo.Iterator
|
||||||
|
readOptions *levigo.ReadOptions
|
||||||
|
snapshot *levigo.Snapshot
|
||||||
|
storage *levigo.DB
|
||||||
|
}
|
||||||
|
|
||||||
func NewLevigoPersistence(storageRoot string, cacheCapacity, bitsPerBloomFilterEncoded int) (*LevigoPersistence, error) {
|
func NewLevigoPersistence(storageRoot string, cacheCapacity, bitsPerBloomFilterEncoded int) (*LevigoPersistence, error) {
|
||||||
options := levigo.NewOptions()
|
options := levigo.NewOptions()
|
||||||
options.SetCreateIfMissing(true)
|
options.SetCreateIfMissing(true)
|
||||||
|
@ -87,14 +91,13 @@ func (l *LevigoPersistence) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoPersistence) Get(value coding.Encoder) ([]byte, error) {
|
func (l *LevigoPersistence) Get(value coding.Encoder) ([]byte, error) {
|
||||||
var key []byte
|
if key, keyError := value.Encode(); keyError == nil {
|
||||||
var keyError error
|
|
||||||
|
|
||||||
if key, keyError = value.Encode(); keyError == nil {
|
|
||||||
return l.storage.Get(l.readOptions, key)
|
return l.storage.Get(l.readOptions, key)
|
||||||
|
} else {
|
||||||
|
return nil, keyError
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, keyError
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoPersistence) Has(value coding.Encoder) (bool, error) {
|
func (l *LevigoPersistence) Has(value coding.Encoder) (bool, error) {
|
||||||
|
@ -108,26 +111,20 @@ func (l *LevigoPersistence) Has(value coding.Encoder) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoPersistence) Drop(value coding.Encoder) error {
|
func (l *LevigoPersistence) Drop(value coding.Encoder) error {
|
||||||
var key []byte
|
if key, keyError := value.Encode(); keyError == nil {
|
||||||
var keyError error
|
|
||||||
|
|
||||||
if key, keyError = value.Encode(); keyError == nil {
|
|
||||||
|
|
||||||
if deleteError := l.storage.Delete(l.writeOptions, key); deleteError != nil {
|
if deleteError := l.storage.Delete(l.writeOptions, key); deleteError != nil {
|
||||||
return deleteError
|
return deleteError
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return nil
|
return keyError
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyError
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoPersistence) Put(key, value coding.Encoder) error {
|
func (l *LevigoPersistence) Put(key, value coding.Encoder) error {
|
||||||
var keyEncoded []byte
|
if keyEncoded, keyError := key.Encode(); keyError == nil {
|
||||||
var keyError error
|
|
||||||
|
|
||||||
if keyEncoded, keyError = key.Encode(); keyError == nil {
|
|
||||||
if valueEncoded, valueError := value.Encode(); valueError == nil {
|
if valueEncoded, valueError := value.Encode(); valueError == nil {
|
||||||
|
|
||||||
if putError := l.storage.Put(l.writeOptions, keyEncoded, valueEncoded); putError != nil {
|
if putError := l.storage.Put(l.writeOptions, keyEncoded, valueEncoded); putError != nil {
|
||||||
|
@ -136,14 +133,14 @@ func (l *LevigoPersistence) Put(key, value coding.Encoder) error {
|
||||||
} else {
|
} else {
|
||||||
return valueError
|
return valueError
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return nil
|
return keyError
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyError
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LevigoPersistence) GetAll() ([]Pair, error) {
|
func (l *LevigoPersistence) GetAll() ([]raw.Pair, error) {
|
||||||
snapshot := l.storage.NewSnapshot()
|
snapshot := l.storage.NewSnapshot()
|
||||||
defer l.storage.ReleaseSnapshot(snapshot)
|
defer l.storage.ReleaseSnapshot(snapshot)
|
||||||
readOptions := levigo.NewReadOptions()
|
readOptions := levigo.NewReadOptions()
|
||||||
|
@ -154,10 +151,10 @@ func (l *LevigoPersistence) GetAll() ([]Pair, error) {
|
||||||
defer iterator.Close()
|
defer iterator.Close()
|
||||||
iterator.SeekToFirst()
|
iterator.SeekToFirst()
|
||||||
|
|
||||||
result := make([]Pair, 0)
|
result := make([]raw.Pair, 0)
|
||||||
|
|
||||||
for iterator := iterator; iterator.Valid(); iterator.Next() {
|
for iterator := iterator; iterator.Valid(); iterator.Next() {
|
||||||
result = append(result, Pair{Left: iterator.Key(), Right: iterator.Value()})
|
result = append(result, raw.Pair{Left: iterator.Key(), Right: iterator.Value()})
|
||||||
}
|
}
|
||||||
|
|
||||||
iteratorError := iterator.GetError()
|
iteratorError := iterator.GetError()
|
||||||
|
@ -169,13 +166,6 @@ func (l *LevigoPersistence) GetAll() ([]Pair, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type iteratorCloser struct {
|
|
||||||
iterator *levigo.Iterator
|
|
||||||
readOptions *levigo.ReadOptions
|
|
||||||
snapshot *levigo.Snapshot
|
|
||||||
storage *levigo.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *iteratorCloser) Close() error {
|
func (i *iteratorCloser) Close() error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if i.storage != nil {
|
if i.storage != nil {
|
Loading…
Reference in a new issue