all: rename variables with lowercase-l/uppercase-I

See http://go/no-ell

Signed-off-by: Alex Chan <alexc@tailscale.com>

Updates #cleanup

Change-Id: I8c976b51ce7a60f06315048b1920516129cc1d5d
This commit is contained in:
Alex Chan
2025-11-17 18:13:44 +00:00
committed by Alex Chan
parent 9048ea25db
commit c2e474e729
81 changed files with 923 additions and 923 deletions
+34 -34
View File
@@ -94,59 +94,59 @@ type bucket struct {
// Allow charges the key one token (up to the overdraft limit), and
// reports whether the key can perform an action.
func (l *Limiter[K]) Allow(key K) bool {
return l.allow(key, time.Now())
func (lm *Limiter[K]) Allow(key K) bool {
return lm.allow(key, time.Now())
}
func (l *Limiter[K]) allow(key K, now time.Time) bool {
l.mu.Lock()
defer l.mu.Unlock()
return l.allowBucketLocked(l.getBucketLocked(key, now), now)
func (lm *Limiter[K]) allow(key K, now time.Time) bool {
lm.mu.Lock()
defer lm.mu.Unlock()
return lm.allowBucketLocked(lm.getBucketLocked(key, now), now)
}
func (l *Limiter[K]) getBucketLocked(key K, now time.Time) *bucket {
if l.cache == nil {
l.cache = &lru.Cache[K, *bucket]{MaxEntries: l.Size}
} else if b := l.cache.Get(key); b != nil {
func (lm *Limiter[K]) getBucketLocked(key K, now time.Time) *bucket {
if lm.cache == nil {
lm.cache = &lru.Cache[K, *bucket]{MaxEntries: lm.Size}
} else if b := lm.cache.Get(key); b != nil {
return b
}
b := &bucket{
cur: l.Max,
lastUpdate: now.Truncate(l.RefillInterval),
cur: lm.Max,
lastUpdate: now.Truncate(lm.RefillInterval),
}
l.cache.Set(key, b)
lm.cache.Set(key, b)
return b
}
func (l *Limiter[K]) allowBucketLocked(b *bucket, now time.Time) bool {
func (lm *Limiter[K]) allowBucketLocked(b *bucket, now time.Time) bool {
// Only update the bucket quota if needed to process request.
if b.cur <= 0 {
l.updateBucketLocked(b, now)
lm.updateBucketLocked(b, now)
}
ret := b.cur > 0
if b.cur > -l.Overdraft {
if b.cur > -lm.Overdraft {
b.cur--
}
return ret
}
func (l *Limiter[K]) updateBucketLocked(b *bucket, now time.Time) {
now = now.Truncate(l.RefillInterval)
func (lm *Limiter[K]) updateBucketLocked(b *bucket, now time.Time) {
now = now.Truncate(lm.RefillInterval)
if now.Before(b.lastUpdate) {
return
}
timeDelta := max(now.Sub(b.lastUpdate), 0)
tokenDelta := int64(timeDelta / l.RefillInterval)
b.cur = min(b.cur+tokenDelta, l.Max)
tokenDelta := int64(timeDelta / lm.RefillInterval)
b.cur = min(b.cur+tokenDelta, lm.Max)
b.lastUpdate = now
}
// peekForTest returns the number of tokens for key, also reporting
// whether key was present.
func (l *Limiter[K]) tokensForTest(key K) (int64, bool) {
l.mu.Lock()
defer l.mu.Unlock()
if b, ok := l.cache.PeekOk(key); ok {
func (lm *Limiter[K]) tokensForTest(key K) (int64, bool) {
lm.mu.Lock()
defer lm.mu.Unlock()
if b, ok := lm.cache.PeekOk(key); ok {
return b.cur, true
}
return 0, false
@@ -159,12 +159,12 @@ func (l *Limiter[K]) tokensForTest(key K) (int64, bool) {
// DumpHTML blocks other callers of the limiter while it collects the
// state for dumping. It should not be called on large limiters
// involved in hot codepaths.
func (l *Limiter[K]) DumpHTML(w io.Writer, onlyLimited bool) {
l.dumpHTML(w, onlyLimited, time.Now())
func (lm *Limiter[K]) DumpHTML(w io.Writer, onlyLimited bool) {
lm.dumpHTML(w, onlyLimited, time.Now())
}
func (l *Limiter[K]) dumpHTML(w io.Writer, onlyLimited bool, now time.Time) {
dump := l.collectDump(now)
func (lm *Limiter[K]) dumpHTML(w io.Writer, onlyLimited bool, now time.Time) {
dump := lm.collectDump(now)
io.WriteString(w, "<table><tr><th>Key</th><th>Tokens</th></tr>")
for _, line := range dump {
if onlyLimited && line.Tokens > 0 {
@@ -183,13 +183,13 @@ func (l *Limiter[K]) dumpHTML(w io.Writer, onlyLimited bool, now time.Time) {
}
// collectDump grabs a copy of the limiter state needed by DumpHTML.
func (l *Limiter[K]) collectDump(now time.Time) []dumpEntry[K] {
l.mu.Lock()
defer l.mu.Unlock()
func (lm *Limiter[K]) collectDump(now time.Time) []dumpEntry[K] {
lm.mu.Lock()
defer lm.mu.Unlock()
ret := make([]dumpEntry[K], 0, l.cache.Len())
l.cache.ForEach(func(k K, v *bucket) {
l.updateBucketLocked(v, now) // so stats are accurate
ret := make([]dumpEntry[K], 0, lm.cache.Len())
lm.cache.ForEach(func(k K, v *bucket) {
lm.updateBucketLocked(v, now) // so stats are accurate
ret = append(ret, dumpEntry[K]{k, v.cur})
})
return ret
+71 -71
View File
@@ -16,7 +16,7 @@ const testRefillInterval = time.Second
func TestLimiter(t *testing.T) {
// 1qps, burst of 10, 2 keys tracked
l := &Limiter[string]{
limiter := &Limiter[string]{
Size: 2,
Max: 10,
RefillInterval: testRefillInterval,
@@ -24,48 +24,48 @@ func TestLimiter(t *testing.T) {
// Consume entire burst
now := time.Now().Truncate(testRefillInterval)
allowed(t, l, "foo", 10, now)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", 0)
allowed(t, limiter, "foo", 10, now)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", 0)
allowed(t, l, "bar", 10, now)
denied(t, l, "bar", 1, now)
hasTokens(t, l, "bar", 0)
allowed(t, limiter, "bar", 10, now)
denied(t, limiter, "bar", 1, now)
hasTokens(t, limiter, "bar", 0)
// Refill 1 token for both foo and bar
now = now.Add(time.Second + time.Millisecond)
allowed(t, l, "foo", 1, now)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", 0)
allowed(t, limiter, "foo", 1, now)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", 0)
allowed(t, l, "bar", 1, now)
denied(t, l, "bar", 1, now)
hasTokens(t, l, "bar", 0)
allowed(t, limiter, "bar", 1, now)
denied(t, limiter, "bar", 1, now)
hasTokens(t, limiter, "bar", 0)
// Refill 2 tokens for foo and bar
now = now.Add(2*time.Second + time.Millisecond)
allowed(t, l, "foo", 2, now)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", 0)
allowed(t, limiter, "foo", 2, now)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", 0)
allowed(t, l, "bar", 2, now)
denied(t, l, "bar", 1, now)
hasTokens(t, l, "bar", 0)
allowed(t, limiter, "bar", 2, now)
denied(t, limiter, "bar", 1, now)
hasTokens(t, limiter, "bar", 0)
// qux can burst 10, evicts foo so it can immediately burst 10 again too
allowed(t, l, "qux", 10, now)
denied(t, l, "qux", 1, now)
notInLimiter(t, l, "foo")
denied(t, l, "bar", 1, now) // refresh bar so foo lookup doesn't evict it - still throttled
allowed(t, limiter, "qux", 10, now)
denied(t, limiter, "qux", 1, now)
notInLimiter(t, limiter, "foo")
denied(t, limiter, "bar", 1, now) // refresh bar so foo lookup doesn't evict it - still throttled
allowed(t, l, "foo", 10, now)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", 0)
allowed(t, limiter, "foo", 10, now)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", 0)
}
func TestLimiterOverdraft(t *testing.T) {
// 1qps, burst of 10, overdraft of 2, 2 keys tracked
l := &Limiter[string]{
limiter := &Limiter[string]{
Size: 2,
Max: 10,
Overdraft: 2,
@@ -74,51 +74,51 @@ func TestLimiterOverdraft(t *testing.T) {
// Consume entire burst, go 1 into debt
now := time.Now().Truncate(testRefillInterval).Add(time.Millisecond)
allowed(t, l, "foo", 10, now)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", -1)
allowed(t, limiter, "foo", 10, now)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", -1)
allowed(t, l, "bar", 10, now)
denied(t, l, "bar", 1, now)
hasTokens(t, l, "bar", -1)
allowed(t, limiter, "bar", 10, now)
denied(t, limiter, "bar", 1, now)
hasTokens(t, limiter, "bar", -1)
// Refill 1 token for both foo and bar.
// Still denied, still in debt.
now = now.Add(time.Second)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", -1)
denied(t, l, "bar", 1, now)
hasTokens(t, l, "bar", -1)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", -1)
denied(t, limiter, "bar", 1, now)
hasTokens(t, limiter, "bar", -1)
// Refill 2 tokens for foo and bar (1 available after debt), try
// to consume 4. Overdraft is capped to 2.
now = now.Add(2 * time.Second)
allowed(t, l, "foo", 1, now)
denied(t, l, "foo", 3, now)
hasTokens(t, l, "foo", -2)
allowed(t, limiter, "foo", 1, now)
denied(t, limiter, "foo", 3, now)
hasTokens(t, limiter, "foo", -2)
allowed(t, l, "bar", 1, now)
denied(t, l, "bar", 3, now)
hasTokens(t, l, "bar", -2)
allowed(t, limiter, "bar", 1, now)
denied(t, limiter, "bar", 3, now)
hasTokens(t, limiter, "bar", -2)
// Refill 1, not enough to allow.
now = now.Add(time.Second)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", -2)
denied(t, l, "bar", 1, now)
hasTokens(t, l, "bar", -2)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", -2)
denied(t, limiter, "bar", 1, now)
hasTokens(t, limiter, "bar", -2)
// qux evicts foo, foo can immediately burst 10 again.
allowed(t, l, "qux", 1, now)
hasTokens(t, l, "qux", 9)
notInLimiter(t, l, "foo")
allowed(t, l, "foo", 10, now)
denied(t, l, "foo", 1, now)
hasTokens(t, l, "foo", -1)
allowed(t, limiter, "qux", 1, now)
hasTokens(t, limiter, "qux", 9)
notInLimiter(t, limiter, "foo")
allowed(t, limiter, "foo", 10, now)
denied(t, limiter, "foo", 1, now)
hasTokens(t, limiter, "foo", -1)
}
func TestDumpHTML(t *testing.T) {
l := &Limiter[string]{
limiter := &Limiter[string]{
Size: 3,
Max: 10,
Overdraft: 10,
@@ -126,13 +126,13 @@ func TestDumpHTML(t *testing.T) {
}
now := time.Now().Truncate(testRefillInterval).Add(time.Millisecond)
allowed(t, l, "foo", 10, now)
denied(t, l, "foo", 2, now)
allowed(t, l, "bar", 4, now)
allowed(t, l, "qux", 1, now)
allowed(t, limiter, "foo", 10, now)
denied(t, limiter, "foo", 2, now)
allowed(t, limiter, "bar", 4, now)
allowed(t, limiter, "qux", 1, now)
var out bytes.Buffer
l.DumpHTML(&out, false)
limiter.DumpHTML(&out, false)
want := strings.Join([]string{
"<table>",
"<tr><th>Key</th><th>Tokens</th></tr>",
@@ -146,7 +146,7 @@ func TestDumpHTML(t *testing.T) {
}
out.Reset()
l.DumpHTML(&out, true)
limiter.DumpHTML(&out, true)
want = strings.Join([]string{
"<table>",
"<tr><th>Key</th><th>Tokens</th></tr>",
@@ -161,7 +161,7 @@ func TestDumpHTML(t *testing.T) {
// organically.
now = now.Add(3 * time.Second)
out.Reset()
l.dumpHTML(&out, false, now)
limiter.dumpHTML(&out, false, now)
want = strings.Join([]string{
"<table>",
"<tr><th>Key</th><th>Tokens</th></tr>",
@@ -175,29 +175,29 @@ func TestDumpHTML(t *testing.T) {
}
}
func allowed(t *testing.T, l *Limiter[string], key string, count int, now time.Time) {
func allowed(t *testing.T, limiter *Limiter[string], key string, count int, now time.Time) {
t.Helper()
for i := range count {
if !l.allow(key, now) {
toks, ok := l.tokensForTest(key)
if !limiter.allow(key, now) {
toks, ok := limiter.tokensForTest(key)
t.Errorf("after %d times: allow(%q, %q) = false, want true (%d tokens available, in cache = %v)", i, key, now, toks, ok)
}
}
}
func denied(t *testing.T, l *Limiter[string], key string, count int, now time.Time) {
func denied(t *testing.T, limiter *Limiter[string], key string, count int, now time.Time) {
t.Helper()
for i := range count {
if l.allow(key, now) {
toks, ok := l.tokensForTest(key)
if limiter.allow(key, now) {
toks, ok := limiter.tokensForTest(key)
t.Errorf("after %d times: allow(%q, %q) = true, want false (%d tokens available, in cache = %v)", i, key, now, toks, ok)
}
}
}
func hasTokens(t *testing.T, l *Limiter[string], key string, want int64) {
func hasTokens(t *testing.T, limiter *Limiter[string], key string, want int64) {
t.Helper()
got, ok := l.tokensForTest(key)
got, ok := limiter.tokensForTest(key)
if !ok {
t.Errorf("key %q missing from limiter", key)
} else if got != want {
@@ -205,9 +205,9 @@ func hasTokens(t *testing.T, l *Limiter[string], key string, want int64) {
}
}
func notInLimiter(t *testing.T, l *Limiter[string], key string) {
func notInLimiter(t *testing.T, limiter *Limiter[string], key string) {
t.Helper()
if tokens, ok := l.tokensForTest(key); ok {
if tokens, ok := limiter.tokensForTest(key); ok {
t.Errorf("key %q unexpectedly tracked by limiter, with %d tokens", key, tokens)
}
}