Created
October 18, 2017 17:15
-
-
Save robskillington/6e5d71cb1330383cd4992dd1e30c4899 to your computer and use it in GitHub Desktop.
Metrics Vector Prototype
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"sync" | |
"sync/atomic" | |
) | |
const ( | |
offset64 = 14695981039346656037 | |
prime64 = 1099511628211 | |
) | |
func hashNew() uint64 { | |
return offset64 | |
} | |
func hashAdd(h uint64, s string) uint64 { | |
for i := 0; i < len(s); i++ { | |
h ^= uint64(s[i]) | |
h *= prime64 | |
} | |
return h | |
} | |
func hashAddByte(h uint64, b byte) uint64 { | |
h ^= uint64(b) | |
h *= prime64 | |
return h | |
} | |
type CounterVector struct { | |
sync.RWMutex | |
tags []string | |
help string | |
instances map[uint64]*resolvedCounterVector | |
} | |
type resolvedCounterVector struct { | |
v int64 | |
} | |
type VectorOptions struct { | |
Help string | |
} | |
func NewCounterVector(opts VectorOptions, tags ...string) *CounterVector { | |
return &CounterVector{ | |
help: opts.Help, | |
tags: tags, | |
instances: make(map[uint64]*resolvedCounterVector), | |
} | |
} | |
func (v *CounterVector) With(tagName, tagValue string) CounterVectorQuery { | |
hash := hashNew() | |
valid := false | |
if v.tags[0] == tagName { | |
hash = hashAdd(hash, tagName) | |
hash = hashAddByte(hash, byte(255)) | |
valid = true | |
} | |
return CounterVectorQuery{ | |
v: v, | |
hash: hash, | |
valid: valid, | |
idxNext: 1, | |
} | |
} | |
func (v *CounterVector) value(hash uint64) int64 { | |
v.RLock() | |
r, ok := v.instances[hash] | |
v.RUnlock() | |
if !ok { | |
return 0 | |
} | |
return atomic.LoadInt64(&r.v) | |
} | |
func (v *CounterVector) inc(hash uint64, delta int64) { | |
v.RLock() | |
r, ok := v.instances[hash] | |
v.RUnlock() | |
if !ok { | |
v.Lock() | |
r, ok = v.instances[hash] | |
if !ok { | |
r = new(resolvedCounterVector) | |
v.instances[hash] = r | |
} | |
v.Unlock() | |
} | |
atomic.AddInt64(&r.v, delta) | |
} | |
type CounterVectorQuery struct { | |
v *CounterVector | |
hash uint64 | |
valid bool | |
idxNext int | |
} | |
func (q CounterVectorQuery) Valid() bool { | |
return q.valid | |
} | |
func (q CounterVectorQuery) With(tagName, tagValue string) CounterVectorQuery { | |
if !q.valid { | |
return q | |
} | |
if q.idxNext >= len(q.v.tags) { | |
r := q | |
r.valid = false | |
return q | |
} | |
if q.v.tags[q.idxNext] != tagName { | |
r := q | |
r.valid = false | |
return q | |
} | |
r := q | |
r.idxNext++ | |
r.hash = hashAdd(r.hash, tagValue) | |
r.hash = hashAddByte(r.hash, byte(255)) | |
return r | |
} | |
func (q CounterVectorQuery) Resolved() bool { | |
return q.idxNext == len(q.v.tags) | |
} | |
func (q CounterVectorQuery) Value() int64 { | |
if !q.Resolved() { | |
return 0 | |
} | |
return q.v.value(q.hash) | |
} | |
func (q CounterVectorQuery) Inc(delta int64) { | |
if !q.Resolved() { | |
return | |
} | |
q.v.inc(q.hash, delta) | |
} | |
func main() { | |
metric := NewCounterVector(VectorOptions{Help: "RPC calls executed"}, "name", "caller", "callee") | |
v := metric.With("name", "calls"). | |
With("caller", "dispatch"). | |
With("callee", "api") | |
v.Inc(42) | |
v.Inc(3) | |
fmt.Printf("value: %v\n", v.Value()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://play.golang.org/p/NGPtpy4GRD