Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ObserverEffect and Nil* metrics. #17

Merged
merged 2 commits into from
Oct 8, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,32 @@ type Counter interface {
Inc(int64)
}

// Create a new Counter.
func NewCounter() Counter {
if UseNilMetrics {
return NilCounter{}
}
return &StandardCounter{0}
}

// No-op Counter.
type NilCounter struct{}

// Force the compiler to check that NilCounter implements Counter.
var _ Counter = NilCounter{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cool trick, but unnecessary here since NewCounter will enforce the same thing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually was gonna say something about ObserverEffect but then I
remembered getFletcherFromDayCare and decided I had no platform whence to
speak.

On Fri, Sep 13, 2013 at 11:51 AM, Wade Simmons notifications@github.comwrote:

In counter.go:

@@ -13,6 +13,32 @@ type Counter interface {
Inc(int64)
}

+// Create a new Counter.
+func NewCounter() Counter {

  • if UseNilMetrics {
  •   return NilCounter{}
    
  • }
  • return &StandardCounter{0}
    +}

+// No-op Counter.
+type NilCounter struct{}
+
+// Force the compiler to check that NilCounter implements Counter.
+var _ Counter = NilCounter{}

This is cool trick, but unnecessary here since NewCounter will enforce the
same thing.


Reply to this email directly or view it on GitHubhttps://github.com//pull/17/files#r6355966
.


// No-op.
func (c NilCounter) Clear() {}

// No-op.
func (c NilCounter) Count() int64 { return 0 }

// No-op.
func (c NilCounter) Dec(i int64) {}

// No-op.
func (c NilCounter) Inc(i int64) {}

// The standard implementation of a Counter uses the sync/atomic package
// to manage a single int64 value.
type StandardCounter struct {
Expand All @@ -22,11 +48,6 @@ type StandardCounter struct {
// Force the compiler to check that StandardCounter implements Counter.
var _ Counter = &StandardCounter{}

// Create a new counter.
func NewCounter() *StandardCounter {
return &StandardCounter{0}
}

// Clear the counter: set it to zero.
func (c *StandardCounter) Clear() {
atomic.StoreInt64(&c.count, 0)
Expand Down
54 changes: 36 additions & 18 deletions ewma.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,58 @@ type EWMA interface {
Update(int64)
}

// The standard implementation of an EWMA tracks the number of uncounted
// events and processes them on each tick. It uses the sync/atomic package
// to manage uncounted events.
type StandardEWMA struct {
alpha float64
init bool
mutex sync.Mutex
rate float64
uncounted int64
}

// Force the compiler to check that StandardEWMA implements EWMA.
var _ EWMA = &StandardEWMA{}

// Create a new EWMA with the given alpha.
func NewEWMA(alpha float64) *StandardEWMA {
func NewEWMA(alpha float64) EWMA {
if UseNilMetrics {
return NilEWMA{}
}
return &StandardEWMA{alpha: alpha}
}

// Create a new EWMA with alpha set for a one-minute moving average.
func NewEWMA1() *StandardEWMA {
func NewEWMA1() EWMA {
return NewEWMA(1 - math.Exp(-5.0/60.0/1))
}

// Create a new EWMA with alpha set for a five-minute moving average.
func NewEWMA5() *StandardEWMA {
func NewEWMA5() EWMA {
return NewEWMA(1 - math.Exp(-5.0/60.0/5))
}

// Create a new EWMA with alpha set for a fifteen-minute moving average.
func NewEWMA15() *StandardEWMA {
func NewEWMA15() EWMA {
return NewEWMA(1 - math.Exp(-5.0/60.0/15))
}

// No-op EWMA.
type NilEWMA struct{}

// Force the compiler to check that NilEWMA implements EWMA.
var _ EWMA = NilEWMA{}

// No-op.
func (a NilEWMA) Rate() float64 { return 0.0 }

// No-op.
func (a NilEWMA) Tick() {}

// No-op.
func (a NilEWMA) Update(n int64) {}

// The standard implementation of an EWMA tracks the number of uncounted
// events and processes them on each tick. It uses the sync/atomic package
// to manage uncounted events.
type StandardEWMA struct {
alpha float64
init bool
mutex sync.Mutex
rate float64
uncounted int64
}

// Force the compiler to check that StandardEWMA implements EWMA.
var _ EWMA = &StandardEWMA{}

// Return the moving average rate of events per second.
func (a *StandardEWMA) Rate() float64 {
a.mutex.Lock()
Expand Down
25 changes: 20 additions & 5 deletions gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ type Gauge interface {
Value() int64
}

// Create a new Gauge.
func NewGauge() Gauge {
if UseNilMetrics {
return NilGauge{}
}
return &StandardGauge{0}
}

// No-op Gauge.
type NilGauge struct{}

// Force the compiler to check that NilGauge implements Gauge.
var _ Gauge = NilGauge{}

// No-op.
func (g NilGauge) Update(v int64) {}

// No-op.
func (g NilGauge) Value() int64 { return 0 }

// The standard implementation of a Gauge uses the sync/atomic package
// to manage a single int64 value.
type StandardGauge struct {
Expand All @@ -20,11 +40,6 @@ type StandardGauge struct {
// Force the compiler to check that StandardGauge implements Gauge.
var _ Gauge = &StandardGauge{}

// Create a new gauge.
func NewGauge() *StandardGauge {
return &StandardGauge{0}
}

// Update the gauge's value.
func (g *StandardGauge) Update(v int64) {
atomic.StoreInt64(&g.value, v)
Expand Down
33 changes: 27 additions & 6 deletions healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,33 @@ type Healthcheck interface {
Unhealthy(error)
}

// Create a new Healthcheck, which will use the given function to update
// its status.
func NewHealthcheck(f func(Healthcheck)) Healthcheck {
if UseNilMetrics {
return NilHealthcheck{}
}
return &StandardHealthcheck{nil, f}
}

// No-op Healthcheck.
type NilHealthcheck struct{}

// Force the compiler to check that NilHealthcheck implements Healthcheck.
var _ Healthcheck = NilHealthcheck{}

// No-op.
func (h NilHealthcheck) Check() {}

// No-op.
func (h NilHealthcheck) Error() error { return nil }

// No-op.
func (h NilHealthcheck) Healthy() {}

// No-op.
func (h NilHealthcheck) Unhealthy(err error) {}

// The standard implementation of a Healthcheck stores the status and a
// function to call to update the status.
type StandardHealthcheck struct {
Expand All @@ -21,12 +48,6 @@ type StandardHealthcheck struct {
// Force the compiler to check that StandardHealthcheck implements Healthcheck.
var _ Healthcheck = &StandardHealthcheck{}

// Create a new healthcheck, which will use the given function to update
// its status.
func NewHealthcheck(f func(Healthcheck)) *StandardHealthcheck {
return &StandardHealthcheck{nil, f}
}

// Update the healthcheck's status.
func (h *StandardHealthcheck) Check() {
h.f(h)
Expand Down
65 changes: 53 additions & 12 deletions histogram.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,59 @@ type Histogram interface {
Variance() float64
}

// Create a new Histogram with the given Sample. The initial values compare
// so that the first value will be both min and max and the variance is flagged
// for special treatment on its first iteration.
func NewHistogram(s Sample) Histogram {
if UseNilMetrics {
return NilHistogram{}
}
return &StandardHistogram{
max: math.MinInt64,
min: math.MaxInt64,
s: s,
variance: [2]float64{-1.0, 0.0},
}
}

// No-op Histogram.
type NilHistogram struct{}

// Force the compiler to check that NilHistogram implements Histogram.
var _ Histogram = NilHistogram{}

// No-op.
func (h NilHistogram) Clear() {}

// No-op.
func (h NilHistogram) Count() int64 { return 0 }

// No-op.
func (h NilHistogram) Max() int64 { return 0 }

// No-op.
func (h NilHistogram) Mean() float64 { return 0.0 }

// No-op.
func (h NilHistogram) Min() int64 { return 0 }

// No-op.
func (h NilHistogram) Percentile(p float64) float64 { return 0.0 }

// No-op.
func (h NilHistogram) Percentiles(ps []float64) []float64 {
return make([]float64, len(ps))
}

// No-op.
func (h NilHistogram) StdDev() float64 { return 0.0 }

// No-op.
func (h NilHistogram) Update(v int64) {}

// No-op.
func (h NilHistogram) Variance() float64 { return 0.0 }

// The standard implementation of a Histogram uses a Sample and a goroutine
// to synchronize its calculations.
type StandardHistogram struct {
Expand All @@ -36,18 +89,6 @@ type StandardHistogram struct {
// Force the compiler to check that StandardHistogram implements Histogram.
var _ Histogram = &StandardHistogram{}

// Create a new histogram with the given Sample. The initial values compare
// so that the first value will be both min and max and the variance is flagged
// for special treatment on its first iteration.
func NewHistogram(s Sample) *StandardHistogram {
return &StandardHistogram{
max: math.MinInt64,
min: math.MaxInt64,
s: s,
variance: [2]float64{-1.0, 0.0},
}
}

// Clear the histogram.
func (h *StandardHistogram) Clear() {
h.mutex.Lock()
Expand Down
65 changes: 46 additions & 19 deletions meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,45 @@ type Meter interface {
RateMean() float64
}

// Create a new Meter. Create the communication channels and start the
// synchronizing goroutine.
func NewMeter() Meter {
if UseNilMetrics {
return NilMeter{}
}
m := &StandardMeter{
make(chan int64),
make(chan meterV),
time.NewTicker(5e9),
}
go m.arbiter()
return m
}

// No-op Meter.
type NilMeter struct{}

// Force the compiler to check that NilMeter implements Meter.
var _ Meter = NilMeter{}

// No-op.
func (m NilMeter) Count() int64 { return 0 }

// No-op.
func (m NilMeter) Mark(n int64) {}

// No-op.
func (m NilMeter) Rate1() float64 { return 0.0 }

// No-op.
func (m NilMeter) Rate5() float64 { return 0.0 }

// No-op.
func (m NilMeter) Rate15() float64 { return 0.0 }

// No-op.
func (m NilMeter) RateMean() float64 { return 0.0 }

// The standard implementation of a Meter uses a goroutine to synchronize
// its calculations and another goroutine (via time.Ticker) to produce
// clock ticks.
Expand All @@ -28,25 +67,6 @@ type StandardMeter struct {
// Force the compiler to check that StandardMeter implements Meter.
var _ Meter = &StandardMeter{}

// A meterV contains all the values that would need to be passed back
// from the synchronizing goroutine.
type meterV struct {
count int64
rate1, rate5, rate15, rateMean float64
}

// Create a new meter. Create the communication channels and start the
// synchronizing goroutine.
func NewMeter() *StandardMeter {
m := &StandardMeter{
make(chan int64),
make(chan meterV),
time.NewTicker(5e9),
}
go m.arbiter()
return m
}

// Return the count of events seen.
func (m *StandardMeter) Count() int64 {
return (<-m.out).count
Expand Down Expand Up @@ -105,3 +125,10 @@ func (m *StandardMeter) arbiter() {
}
}
}

// A meterV contains all the values that would need to be passed back
// from the synchronizing goroutine.
type meterV struct {
count int64
rate1, rate5, rate15, rateMean float64
}
7 changes: 7 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@
//
// Coda Hale's original work: <https://github.com/codahale/metrics>
package metrics

// UseNilMetrics is checked by the constructor functions for all of the
// standard metrics. If it is true, the metric returned is a stub.
//
// This global kill-switch helps quantify the observer effect and makes
// for less cluttered pprof profiles.
var UseNilMetrics bool = false
Loading