Skip to content
quarry Artifact [6a319e872d]
Login

Artifact [6a319e872d]

Artifact 6a319e872dd1ece37e116a2b69042c8b970a4ed5ecd69a9313d2789a4a943062:


package obs

import (
	"sync"
	"testing"
	"time"
)

// A route that is slow a thousand times must notify once. errlog's
// rule, for errlog's reason: an alert channel that repeats itself gets
// muted, and a muted channel is worse than none because it reads as
// healthy.
func TestSlowRouteNotifiesOnceThenStaysQuiet(t *testing.T) {
	r := NewRecorder(50, nil)
	var got []string
	r.OnSlow = func(v Render) { got = append(got, v.Path) }

	for range 20 {
		r.RecordRender(Render{Path: "/slow", Elapsed: Budget + time.Millisecond})
	}
	if len(got) != 1 || got[0] != "/slow" {
		t.Fatalf("notified %v, want exactly one /slow", got)
	}

	// A DIFFERENT route crossing is its own news.
	r.RecordRender(Render{Path: "/other", Elapsed: Budget * 3})
	if len(got) != 2 || got[1] != "/other" {
		t.Errorf("notified %v, want /other reported separately", got)
	}
}

// Inside the budget is silence. The threshold is exclusive: a render
// exactly at the budget has not exceeded it.
func TestFastRoutesNeverNotify(t *testing.T) {
	r := NewRecorder(50, nil)
	n := 0
	r.OnSlow = func(Render) { n++ }
	r.RecordRender(Render{Path: "/a", Elapsed: Budget - time.Millisecond})
	r.RecordRender(Render{Path: "/b", Elapsed: Budget})
	if n != 0 {
		t.Errorf("notified %d times, want silence at or under the budget", n)
	}
}

// A nil hook is the ordinary case — most forges have no notification
// plane — and must cost nothing rather than panic.
func TestNoHookIsSafe(t *testing.T) {
	r := NewRecorder(4, nil)
	r.RecordRender(Render{Path: "/slow", Elapsed: Budget * 10})
}

// The decision is made under the lock, so two slow requests racing on
// the same route cannot both report it. Run with -race.
func TestConcurrentCrossingsReportOnce(t *testing.T) {
	r := NewRecorder(200, nil)
	var mu sync.Mutex
	n := 0
	r.OnSlow = func(Render) { mu.Lock(); n++; mu.Unlock() }
	var wg sync.WaitGroup
	for range 32 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			r.RecordRender(Render{Path: "/same", Elapsed: Budget * 2})
		}()
	}
	wg.Wait()
	mu.Lock()
	defer mu.Unlock()
	if n != 1 {
		t.Errorf("notified %d times under concurrency, want 1", n)
	}
}