Adding upstream version 2.5.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
c71cb8b61d
commit
982828099e
783 changed files with 150650 additions and 0 deletions
46
index/upsidedown/store/metrics/batch.go
Normal file
46
index/upsidedown/store/metrics/batch.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import store "github.com/blevesearch/upsidedown_store_api"
|
||||
|
||||
type Batch struct {
|
||||
s *Store
|
||||
o store.KVBatch
|
||||
}
|
||||
|
||||
func (b *Batch) Set(key, val []byte) {
|
||||
b.o.Set(key, val)
|
||||
}
|
||||
|
||||
func (b *Batch) Delete(key []byte) {
|
||||
b.o.Delete(key)
|
||||
}
|
||||
|
||||
func (b *Batch) Merge(key, val []byte) {
|
||||
b.s.timerBatchMerge.Time(func() {
|
||||
b.o.Merge(key, val)
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Batch) Reset() {
|
||||
b.o.Reset()
|
||||
}
|
||||
|
||||
func (b *Batch) Close() error {
|
||||
err := b.o.Close()
|
||||
b.o = nil
|
||||
return err
|
||||
}
|
58
index/upsidedown/store/metrics/iterator.go
Normal file
58
index/upsidedown/store/metrics/iterator.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import store "github.com/blevesearch/upsidedown_store_api"
|
||||
|
||||
type Iterator struct {
|
||||
s *Store
|
||||
o store.KVIterator
|
||||
}
|
||||
|
||||
func (i *Iterator) Seek(x []byte) {
|
||||
i.s.timerIteratorSeek.Time(func() {
|
||||
i.o.Seek(x)
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Iterator) Next() {
|
||||
i.s.timerIteratorNext.Time(func() {
|
||||
i.o.Next()
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Iterator) Current() ([]byte, []byte, bool) {
|
||||
return i.o.Current()
|
||||
}
|
||||
|
||||
func (i *Iterator) Key() []byte {
|
||||
return i.o.Key()
|
||||
}
|
||||
|
||||
func (i *Iterator) Value() []byte {
|
||||
return i.o.Value()
|
||||
}
|
||||
|
||||
func (i *Iterator) Valid() bool {
|
||||
return i.o.Valid()
|
||||
}
|
||||
|
||||
func (i *Iterator) Close() error {
|
||||
err := i.o.Close()
|
||||
if err != nil {
|
||||
i.s.AddError("Iterator.Close", err, nil)
|
||||
}
|
||||
return err
|
||||
}
|
141
index/upsidedown/store/metrics/metrics_test.go
Normal file
141
index/upsidedown/store/metrics/metrics_test.go
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/v2/index/upsidedown/store/gtreap"
|
||||
)
|
||||
|
||||
func TestMetricsStore(t *testing.T) {
|
||||
_, err := New(nil, map[string]interface{}{})
|
||||
if err == nil {
|
||||
t.Errorf("expected err when bad config")
|
||||
}
|
||||
|
||||
_, err = New(nil, map[string]interface{}{
|
||||
"kvStoreName_actual": "some-invalid-kvstore-name",
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("expected err when unknown kvStoreName_actual")
|
||||
}
|
||||
|
||||
s, err := New(nil, map[string]interface{}{
|
||||
"kvStoreName_actual": gtreap.Name,
|
||||
"path": "",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
err = s.(*Store).WriteJSON(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b.Len() <= 0 {
|
||||
t.Errorf("expected some output from WriteJSON")
|
||||
}
|
||||
var m map[string]interface{}
|
||||
err = json.Unmarshal(b.Bytes(), &m)
|
||||
if err != nil {
|
||||
t.Errorf("expected WriteJSON to be unmarshallable")
|
||||
}
|
||||
if len(m) == 0 {
|
||||
t.Errorf("expected some entries")
|
||||
}
|
||||
|
||||
b = bytes.NewBuffer(nil)
|
||||
s.(*Store).WriteCSVHeader(b)
|
||||
if b.Len() <= 0 {
|
||||
t.Errorf("expected some output from WriteCSVHeader")
|
||||
}
|
||||
|
||||
b = bytes.NewBuffer(nil)
|
||||
s.(*Store).WriteCSV(b)
|
||||
if b.Len() <= 0 {
|
||||
t.Errorf("expected some output from WriteCSV")
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
s, err := New(nil, map[string]interface{}{
|
||||
"kvStoreName_actual": gtreap.Name,
|
||||
"path": "",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
x, ok := s.(*Store)
|
||||
if !ok {
|
||||
t.Errorf("expecting a Store")
|
||||
}
|
||||
|
||||
x.AddError("foo", fmt.Errorf("Foo"), []byte("fooKey"))
|
||||
x.AddError("bar", fmt.Errorf("Bar"), nil)
|
||||
x.AddError("baz", fmt.Errorf("Baz"), []byte("bazKey"))
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
err = x.WriteJSON(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
err = json.Unmarshal(b.Bytes(), &m)
|
||||
if err != nil {
|
||||
t.Errorf("expected unmarshallable writeJSON, err: %v, b: %s",
|
||||
err, b.Bytes())
|
||||
}
|
||||
|
||||
errorsi, ok := m["Errors"]
|
||||
if !ok || errorsi == nil {
|
||||
t.Errorf("expected errorsi")
|
||||
}
|
||||
errors, ok := errorsi.([]interface{})
|
||||
if !ok || errors == nil {
|
||||
t.Errorf("expected errorsi is array")
|
||||
}
|
||||
if len(errors) != 3 {
|
||||
t.Errorf("expected errors len 3")
|
||||
}
|
||||
|
||||
e := errors[0].(map[string]interface{})
|
||||
if e["Op"].(string) != "foo" ||
|
||||
e["Err"].(string) != "Foo" ||
|
||||
len(e["Time"].(string)) < 10 ||
|
||||
e["Key"].(string) != "fooKey" {
|
||||
t.Errorf("expected foo, %#v", e)
|
||||
}
|
||||
e = errors[1].(map[string]interface{})
|
||||
if e["Op"].(string) != "bar" ||
|
||||
e["Err"].(string) != "Bar" ||
|
||||
len(e["Time"].(string)) < 10 ||
|
||||
e["Key"].(string) != "" {
|
||||
t.Errorf("expected bar, %#v", e)
|
||||
}
|
||||
e = errors[2].(map[string]interface{})
|
||||
if e["Op"].(string) != "baz" ||
|
||||
e["Err"].(string) != "Baz" ||
|
||||
len(e["Time"].(string)) < 10 ||
|
||||
e["Key"].(string) != "bazKey" {
|
||||
t.Errorf("expected baz, %#v", e)
|
||||
}
|
||||
}
|
64
index/upsidedown/store/metrics/reader.go
Normal file
64
index/upsidedown/store/metrics/reader.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import store "github.com/blevesearch/upsidedown_store_api"
|
||||
|
||||
type Reader struct {
|
||||
s *Store
|
||||
o store.KVReader
|
||||
}
|
||||
|
||||
func (r *Reader) Get(key []byte) (v []byte, err error) {
|
||||
r.s.timerReaderGet.Time(func() {
|
||||
v, err = r.o.Get(key)
|
||||
if err != nil {
|
||||
r.s.AddError("Reader.Get", err, key)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) MultiGet(keys [][]byte) (vals [][]byte, err error) {
|
||||
r.s.timerReaderMultiGet.Time(func() {
|
||||
vals, err = r.o.MultiGet(keys)
|
||||
if err != nil {
|
||||
r.s.AddError("Reader.MultiGet", err, nil)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) PrefixIterator(prefix []byte) (i store.KVIterator) {
|
||||
r.s.timerReaderPrefixIterator.Time(func() {
|
||||
i = &Iterator{s: r.s, o: r.o.PrefixIterator(prefix)}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) RangeIterator(start, end []byte) (i store.KVIterator) {
|
||||
r.s.timerReaderRangeIterator.Time(func() {
|
||||
i = &Iterator{s: r.s, o: r.o.RangeIterator(start, end)}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) Close() error {
|
||||
err := r.o.Close()
|
||||
if err != nil {
|
||||
r.s.AddError("Reader.Close", err, nil)
|
||||
}
|
||||
return err
|
||||
}
|
50
index/upsidedown/store/metrics/stats.go
Normal file
50
index/upsidedown/store/metrics/stats.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2014 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/v2/util"
|
||||
store "github.com/blevesearch/upsidedown_store_api"
|
||||
)
|
||||
|
||||
type stats struct {
|
||||
s *Store
|
||||
}
|
||||
|
||||
func (s *stats) statsMap() map[string]interface{} {
|
||||
ms := map[string]interface{}{}
|
||||
|
||||
ms["metrics"] = map[string]interface{}{
|
||||
"reader_get": TimerMap(s.s.timerReaderGet),
|
||||
"reader_multi_get": TimerMap(s.s.timerReaderMultiGet),
|
||||
"reader_prefix_iterator": TimerMap(s.s.timerReaderPrefixIterator),
|
||||
"reader_range_iterator": TimerMap(s.s.timerReaderRangeIterator),
|
||||
"writer_execute_batch": TimerMap(s.s.timerWriterExecuteBatch),
|
||||
"iterator_seek": TimerMap(s.s.timerIteratorSeek),
|
||||
"iterator_next": TimerMap(s.s.timerIteratorNext),
|
||||
"batch_merge": TimerMap(s.s.timerBatchMerge),
|
||||
}
|
||||
|
||||
if o, ok := s.s.o.(store.KVStoreStats); ok {
|
||||
ms["kv"] = o.StatsMap()
|
||||
}
|
||||
|
||||
return ms
|
||||
}
|
||||
|
||||
func (s *stats) MarshalJSON() ([]byte, error) {
|
||||
m := s.statsMap()
|
||||
return util.MarshalJSON(m)
|
||||
}
|
277
index/upsidedown/store/metrics/store.go
Normal file
277
index/upsidedown/store/metrics/store.go
Normal file
|
@ -0,0 +1,277 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics provides a bleve.store.KVStore implementation that
|
||||
// wraps another, real KVStore implementation, and uses go-metrics to
|
||||
// track runtime performance metrics.
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve/v2/registry"
|
||||
"github.com/blevesearch/bleve/v2/util"
|
||||
"github.com/blevesearch/go-metrics"
|
||||
store "github.com/blevesearch/upsidedown_store_api"
|
||||
)
|
||||
|
||||
const Name = "metrics"
|
||||
|
||||
type Store struct {
|
||||
o store.KVStore
|
||||
|
||||
timerReaderGet metrics.Timer
|
||||
timerReaderMultiGet metrics.Timer
|
||||
timerReaderPrefixIterator metrics.Timer
|
||||
timerReaderRangeIterator metrics.Timer
|
||||
timerWriterExecuteBatch metrics.Timer
|
||||
timerIteratorSeek metrics.Timer
|
||||
timerIteratorNext metrics.Timer
|
||||
timerBatchMerge metrics.Timer
|
||||
|
||||
m sync.Mutex // Protects the fields that follow.
|
||||
errors *list.List // Capped list of StoreError's.
|
||||
|
||||
s *stats
|
||||
}
|
||||
|
||||
func New(mo store.MergeOperator, config map[string]interface{}) (store.KVStore, error) {
|
||||
|
||||
name, ok := config["kvStoreName_actual"].(string)
|
||||
if !ok || name == "" {
|
||||
return nil, fmt.Errorf("metrics: missing kvStoreName_actual,"+
|
||||
" config: %#v", config)
|
||||
}
|
||||
|
||||
if name == Name {
|
||||
return nil, fmt.Errorf("metrics: circular kvStoreName_actual")
|
||||
}
|
||||
|
||||
ctr := registry.KVStoreConstructorByName(name)
|
||||
if ctr == nil {
|
||||
return nil, fmt.Errorf("metrics: no kv store constructor,"+
|
||||
" kvStoreName_actual: %s", name)
|
||||
}
|
||||
|
||||
kvs, err := ctr(mo, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rv := &Store{
|
||||
o: kvs,
|
||||
|
||||
timerReaderGet: metrics.NewTimer(),
|
||||
timerReaderMultiGet: metrics.NewTimer(),
|
||||
timerReaderPrefixIterator: metrics.NewTimer(),
|
||||
timerReaderRangeIterator: metrics.NewTimer(),
|
||||
timerWriterExecuteBatch: metrics.NewTimer(),
|
||||
timerIteratorSeek: metrics.NewTimer(),
|
||||
timerIteratorNext: metrics.NewTimer(),
|
||||
timerBatchMerge: metrics.NewTimer(),
|
||||
|
||||
errors: list.New(),
|
||||
}
|
||||
|
||||
rv.s = &stats{s: rv}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
err := registry.RegisterKVStore(Name, New)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) Close() error {
|
||||
return s.o.Close()
|
||||
}
|
||||
|
||||
func (s *Store) Reader() (store.KVReader, error) {
|
||||
o, err := s.o.Reader()
|
||||
if err != nil {
|
||||
s.AddError("Reader", err, nil)
|
||||
return nil, err
|
||||
}
|
||||
return &Reader{s: s, o: o}, nil
|
||||
}
|
||||
|
||||
func (s *Store) Writer() (store.KVWriter, error) {
|
||||
o, err := s.o.Writer()
|
||||
if err != nil {
|
||||
s.AddError("Writer", err, nil)
|
||||
return nil, err
|
||||
}
|
||||
return &Writer{s: s, o: o}, nil
|
||||
}
|
||||
|
||||
// Metric specific code below:
|
||||
|
||||
const MaxErrors = 100
|
||||
|
||||
type StoreError struct {
|
||||
Time string
|
||||
Op string
|
||||
Err string
|
||||
Key string
|
||||
}
|
||||
|
||||
func (s *Store) AddError(op string, err error, key []byte) {
|
||||
e := &StoreError{
|
||||
Time: time.Now().Format(time.RFC3339Nano),
|
||||
Op: op,
|
||||
Err: fmt.Sprintf("%v", err),
|
||||
Key: string(key),
|
||||
}
|
||||
|
||||
s.m.Lock()
|
||||
for s.errors.Len() >= MaxErrors {
|
||||
s.errors.Remove(s.errors.Front())
|
||||
}
|
||||
s.errors.PushBack(e)
|
||||
s.m.Unlock()
|
||||
}
|
||||
|
||||
func (s *Store) WriteJSON(w io.Writer) (err error) {
|
||||
_, err = w.Write([]byte(`{"TimerReaderGet":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerReaderGet)
|
||||
_, err = w.Write([]byte(`,"TimerReaderMultiGet":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerReaderMultiGet)
|
||||
_, err = w.Write([]byte(`,"TimerReaderPrefixIterator":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerReaderPrefixIterator)
|
||||
_, err = w.Write([]byte(`,"TimerReaderRangeIterator":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerReaderRangeIterator)
|
||||
_, err = w.Write([]byte(`,"TimerWriterExecuteBatch":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerWriterExecuteBatch)
|
||||
_, err = w.Write([]byte(`,"TimerIteratorSeek":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerIteratorSeek)
|
||||
_, err = w.Write([]byte(`,"TimerIteratorNext":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerIteratorNext)
|
||||
_, err = w.Write([]byte(`,"TimerBatchMerge":`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
WriteTimerJSON(w, s.timerBatchMerge)
|
||||
|
||||
_, err = w.Write([]byte(`,"Errors":[`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
e := s.errors.Front()
|
||||
i := 0
|
||||
for e != nil {
|
||||
se, ok := e.Value.(*StoreError)
|
||||
if ok && se != nil {
|
||||
if i > 0 {
|
||||
_, err = w.Write([]byte(","))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
var buf []byte
|
||||
buf, err = util.MarshalJSON(se)
|
||||
if err == nil {
|
||||
_, err = w.Write(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
e = e.Next()
|
||||
i = i + 1
|
||||
}
|
||||
_, err = w.Write([]byte(`]`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// see if the underlying implementation has its own stats
|
||||
if o, ok := s.o.(store.KVStoreStats); ok {
|
||||
storeStats := o.Stats()
|
||||
var storeBytes []byte
|
||||
storeBytes, err = util.MarshalJSON(storeStats)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = fmt.Fprintf(w, `, "store": %s`, string(storeBytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(`}`))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Store) WriteCSVHeader(w io.Writer) {
|
||||
WriteTimerCSVHeader(w, "TimerReaderGet")
|
||||
WriteTimerCSVHeader(w, "TimerReaderPrefixIterator")
|
||||
WriteTimerCSVHeader(w, "TimerReaderRangeIterator")
|
||||
WriteTimerCSVHeader(w, "TimerWtierExecuteBatch")
|
||||
WriteTimerCSVHeader(w, "TimerIteratorSeek")
|
||||
WriteTimerCSVHeader(w, "TimerIteratorNext")
|
||||
WriteTimerCSVHeader(w, "TimerBatchMerge")
|
||||
}
|
||||
|
||||
func (s *Store) WriteCSV(w io.Writer) {
|
||||
WriteTimerCSV(w, s.timerReaderGet)
|
||||
WriteTimerCSV(w, s.timerReaderPrefixIterator)
|
||||
WriteTimerCSV(w, s.timerReaderRangeIterator)
|
||||
WriteTimerCSV(w, s.timerWriterExecuteBatch)
|
||||
WriteTimerCSV(w, s.timerIteratorSeek)
|
||||
WriteTimerCSV(w, s.timerIteratorNext)
|
||||
WriteTimerCSV(w, s.timerBatchMerge)
|
||||
}
|
||||
|
||||
func (s *Store) Stats() json.Marshaler {
|
||||
return s.s
|
||||
}
|
||||
|
||||
func (s *Store) StatsMap() map[string]interface{} {
|
||||
return s.s.statsMap()
|
||||
}
|
95
index/upsidedown/store/metrics/store_test.go
Normal file
95
index/upsidedown/store/metrics/store_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/v2/index/upsidedown/store/gtreap"
|
||||
store "github.com/blevesearch/upsidedown_store_api"
|
||||
"github.com/blevesearch/upsidedown_store_api/test"
|
||||
)
|
||||
|
||||
func open(t *testing.T, mo store.MergeOperator) store.KVStore {
|
||||
rv, err := New(mo, map[string]interface{}{
|
||||
"kvStoreName_actual": gtreap.Name,
|
||||
"path": "",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func cleanup(t *testing.T, s store.KVStore) {
|
||||
err := s.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetricsKVCrud(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestKVCrud(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsReaderIsolation(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestReaderIsolation(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsReaderOwnsGetBytes(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestReaderOwnsGetBytes(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsWriterOwnsBytes(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestWriterOwnsBytes(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsPrefixIterator(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestPrefixIterator(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsPrefixIteratorSeek(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestPrefixIteratorSeek(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsRangeIterator(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestRangeIterator(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsRangeIteratorSeek(t *testing.T) {
|
||||
s := open(t, nil)
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestRangeIteratorSeek(t, s)
|
||||
}
|
||||
|
||||
func TestMetricsMerge(t *testing.T) {
|
||||
s := open(t, &test.TestMergeCounter{})
|
||||
defer cleanup(t, s)
|
||||
test.CommonTestMerge(t, s)
|
||||
}
|
135
index/upsidedown/store/metrics/util.go
Normal file
135
index/upsidedown/store/metrics/util.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/blevesearch/go-metrics"
|
||||
)
|
||||
|
||||
// NOTE: This is copy & pasted from cbft as otherwise there
|
||||
// would be an import cycle.
|
||||
|
||||
var timerPercentiles = []float64{0.5, 0.75, 0.95, 0.99, 0.999}
|
||||
|
||||
func TimerMap(timer metrics.Timer) map[string]interface{} {
|
||||
|
||||
rv := make(map[string]interface{})
|
||||
t := timer.Snapshot()
|
||||
p := t.Percentiles(timerPercentiles)
|
||||
|
||||
percentileKeys := []string{"median", "75%", "95%", "99%", "99.9%"}
|
||||
percentiles := make(map[string]interface{})
|
||||
for i, pi := range p {
|
||||
if !isNanOrInf(pi) {
|
||||
percentileKey := percentileKeys[i]
|
||||
percentiles[percentileKey] = pi
|
||||
}
|
||||
}
|
||||
|
||||
rateKeys := []string{"1-min", "5-min", "15-min", "mean"}
|
||||
rates := make(map[string]interface{})
|
||||
for i, ri := range []float64{t.Rate1(), t.Rate5(), t.Rate15(), t.RateMean()} {
|
||||
if !isNanOrInf(ri) {
|
||||
rateKey := rateKeys[i]
|
||||
rates[rateKey] = ri
|
||||
}
|
||||
}
|
||||
|
||||
rv["count"] = t.Count()
|
||||
rv["min"] = t.Min()
|
||||
rv["max"] = t.Max()
|
||||
mean := t.Mean()
|
||||
if !isNanOrInf(mean) {
|
||||
rv["mean"] = mean
|
||||
}
|
||||
stddev := t.StdDev()
|
||||
if !isNanOrInf(stddev) {
|
||||
rv["stddev"] = stddev
|
||||
}
|
||||
rv["percentiles"] = percentiles
|
||||
rv["rates"] = rates
|
||||
|
||||
return rv
|
||||
}
|
||||
|
||||
func isNanOrInf(v float64) bool {
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func WriteTimerJSON(w io.Writer, timer metrics.Timer) {
|
||||
t := timer.Snapshot()
|
||||
p := t.Percentiles(timerPercentiles)
|
||||
|
||||
fmt.Fprintf(w, `{"count":%9d,`, t.Count())
|
||||
fmt.Fprintf(w, `"min":%9d,`, t.Min())
|
||||
fmt.Fprintf(w, `"max":%9d,`, t.Max())
|
||||
fmt.Fprintf(w, `"mean":%12.2f,`, t.Mean())
|
||||
fmt.Fprintf(w, `"stddev":%12.2f,`, t.StdDev())
|
||||
fmt.Fprintf(w, `"percentiles":{`)
|
||||
fmt.Fprintf(w, `"median":%12.2f,`, p[0])
|
||||
fmt.Fprintf(w, `"75%%":%12.2f,`, p[1])
|
||||
fmt.Fprintf(w, `"95%%":%12.2f,`, p[2])
|
||||
fmt.Fprintf(w, `"99%%":%12.2f,`, p[3])
|
||||
fmt.Fprintf(w, `"99.9%%":%12.2f},`, p[4])
|
||||
fmt.Fprintf(w, `"rates":{`)
|
||||
fmt.Fprintf(w, `"1-min":%12.2f,`, t.Rate1())
|
||||
fmt.Fprintf(w, `"5-min":%12.2f,`, t.Rate5())
|
||||
fmt.Fprintf(w, `"15-min":%12.2f,`, t.Rate15())
|
||||
fmt.Fprintf(w, `"mean":%12.2f}}`, t.RateMean())
|
||||
}
|
||||
|
||||
func WriteTimerCSVHeader(w io.Writer, prefix string) {
|
||||
fmt.Fprintf(w, "%s-count,", prefix)
|
||||
fmt.Fprintf(w, "%s-min,", prefix)
|
||||
fmt.Fprintf(w, "%s-max,", prefix)
|
||||
fmt.Fprintf(w, "%s-mean,", prefix)
|
||||
fmt.Fprintf(w, "%s-stddev,", prefix)
|
||||
fmt.Fprintf(w, "%s-percentile-50%%,", prefix)
|
||||
fmt.Fprintf(w, "%s-percentile-75%%,", prefix)
|
||||
fmt.Fprintf(w, "%s-percentile-95%%,", prefix)
|
||||
fmt.Fprintf(w, "%s-percentile-99%%,", prefix)
|
||||
fmt.Fprintf(w, "%s-percentile-99.9%%,", prefix)
|
||||
fmt.Fprintf(w, "%s-rate-1-min,", prefix)
|
||||
fmt.Fprintf(w, "%s-rate-5-min,", prefix)
|
||||
fmt.Fprintf(w, "%s-rate-15-min,", prefix)
|
||||
fmt.Fprintf(w, "%s-rate-mean", prefix)
|
||||
}
|
||||
|
||||
func WriteTimerCSV(w io.Writer, timer metrics.Timer) {
|
||||
t := timer.Snapshot()
|
||||
p := t.Percentiles(timerPercentiles)
|
||||
|
||||
fmt.Fprintf(w, `%d,`, t.Count())
|
||||
fmt.Fprintf(w, `%d,`, t.Min())
|
||||
fmt.Fprintf(w, `%d,`, t.Max())
|
||||
fmt.Fprintf(w, `%f,`, t.Mean())
|
||||
fmt.Fprintf(w, `%f,`, t.StdDev())
|
||||
fmt.Fprintf(w, `%f,`, p[0])
|
||||
fmt.Fprintf(w, `%f,`, p[1])
|
||||
fmt.Fprintf(w, `%f,`, p[2])
|
||||
fmt.Fprintf(w, `%f,`, p[3])
|
||||
fmt.Fprintf(w, `%f,`, p[4])
|
||||
fmt.Fprintf(w, `%f,`, t.Rate1())
|
||||
fmt.Fprintf(w, `%f,`, t.Rate5())
|
||||
fmt.Fprintf(w, `%f,`, t.Rate15())
|
||||
fmt.Fprintf(w, `%f`, t.RateMean())
|
||||
}
|
60
index/upsidedown/store/metrics/writer.go
Normal file
60
index/upsidedown/store/metrics/writer.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
//
|
||||
// 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 metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
store "github.com/blevesearch/upsidedown_store_api"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
s *Store
|
||||
o store.KVWriter
|
||||
}
|
||||
|
||||
func (w *Writer) Close() error {
|
||||
err := w.o.Close()
|
||||
if err != nil {
|
||||
w.s.AddError("Writer.Close", err, nil)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Writer) NewBatch() store.KVBatch {
|
||||
return &Batch{s: w.s, o: w.o.NewBatch()}
|
||||
}
|
||||
|
||||
func (w *Writer) NewBatchEx(options store.KVBatchOptions) ([]byte, store.KVBatch, error) {
|
||||
buf, b, err := w.o.NewBatchEx(options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return buf, &Batch{s: w.s, o: b}, nil
|
||||
}
|
||||
|
||||
func (w *Writer) ExecuteBatch(b store.KVBatch) (err error) {
|
||||
batch, ok := b.(*Batch)
|
||||
if !ok {
|
||||
return fmt.Errorf("wrong type of batch")
|
||||
}
|
||||
w.s.timerWriterExecuteBatch.Time(func() {
|
||||
err = w.o.ExecuteBatch(batch.o)
|
||||
if err != nil {
|
||||
w.s.AddError("Writer.ExecuteBatch", err, nil)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue