Skip to content

Instantly share code, notes, and snippets.

@robskillington
Created October 18, 2017 17:15
Show Gist options
  • Save robskillington/6e5d71cb1330383cd4992dd1e30c4899 to your computer and use it in GitHub Desktop.
Save robskillington/6e5d71cb1330383cd4992dd1e30c4899 to your computer and use it in GitHub Desktop.
Metrics Vector Prototype
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())
}
@robskillington
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment