mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
util/lru, util/limiter: add debug helper to dump state as HTML
For use in tsweb debug handlers, so that we can easily inspect cache and limiter state when troubleshooting. Updates tailscale/corp#3601 Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:

committed by
Dave Anderson

parent
d23b8ffb13
commit
95082a8dde
@@ -4,6 +4,12 @@
|
||||
// Package lru contains a typed Least-Recently-Used cache.
|
||||
package lru
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Cache is container type keyed by K, storing V, optionally evicting the least
|
||||
// recently used items if a maximum size is exceeded.
|
||||
//
|
||||
@@ -171,3 +177,31 @@ func (c *Cache[K, V]) deleteElement(ent *entry[K, V]) {
|
||||
}
|
||||
delete(c.lookup, ent.key)
|
||||
}
|
||||
|
||||
// ForEach calls fn for each entry in the cache, from most recently
|
||||
// used to least recently used.
|
||||
func (c *Cache[K, V]) ForEach(fn func(K, V)) {
|
||||
if c.head == nil {
|
||||
return
|
||||
}
|
||||
cur := c.head
|
||||
for {
|
||||
fn(cur.key, cur.value)
|
||||
cur = cur.next
|
||||
if cur == c.head {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DumpHTML writes the state of the cache to the given writer,
|
||||
// formatted as an HTML table.
|
||||
func (c *Cache[K, V]) DumpHTML(w io.Writer) {
|
||||
io.WriteString(w, "<table><tr><th>Key</th><th>Value</th></tr>")
|
||||
c.ForEach(func(k K, v V) {
|
||||
kStr := html.EscapeString(fmt.Sprint(k))
|
||||
vStr := html.EscapeString(fmt.Sprint(v))
|
||||
fmt.Fprintf(w, "<tr><td>%s</td><td>%v</td></tr>", kStr, vStr)
|
||||
})
|
||||
io.WriteString(w, "</table>")
|
||||
}
|
||||
|
@@ -4,8 +4,12 @@
|
||||
package lru
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestLRU(t *testing.T) {
|
||||
@@ -44,6 +48,31 @@ func TestLRU(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDumpHTML(t *testing.T) {
|
||||
c := Cache[int, string]{MaxEntries: 3}
|
||||
|
||||
c.Set(1, "foo")
|
||||
c.Set(2, "bar")
|
||||
c.Set(3, "qux")
|
||||
c.Set(4, "wat")
|
||||
|
||||
var out bytes.Buffer
|
||||
c.DumpHTML(&out)
|
||||
|
||||
want := strings.Join([]string{
|
||||
"<table>",
|
||||
"<tr><th>Key</th><th>Value</th></tr>",
|
||||
"<tr><td>4</td><td>wat</td></tr>",
|
||||
"<tr><td>3</td><td>qux</td></tr>",
|
||||
"<tr><td>2</td><td>bar</td></tr>",
|
||||
"</table>",
|
||||
}, "")
|
||||
|
||||
if diff := cmp.Diff(out.String(), want); diff != "" {
|
||||
t.Fatalf("wrong DumpHTML output (-got+want):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRU(b *testing.B) {
|
||||
const lruSize = 10
|
||||
const maxval = 15 // 33% more keys than the LRU can hold
|
||||
|
Reference in New Issue
Block a user