Skip to content

Commit c2c042f

Browse files
craig[bot]stevendannatbgherkolategan
committed
150509: gossip: use crtime.Mono r=iskettaneh a=stevendanna crtime.NowMono is a little more efficient. I haven't seen this on any profiles but noted it when reviewing some code recently. We also remove an extra clock reading that wasn't needed. Epic: none Release note: None 152661: changefeedccl: clean up temporary files during parquet writer tests r=asg0451 a=stevendanna Nearly every nightly failure I look at has a temporary directory filled with these files. Epic: None Release note: None 152959: asimview: add interactive web viewer for asim test results r=tbg a=tbg This adds asimview, a Go tool that serves an interactive web viewer for ASIM test JSON output files, replacing the previous static HTML approach with significant improvements. Key features: - Auto-discovery of test files with fuzzy search and multi-select - Instant file loading with persistent file selections across sessions - Metric filtering with persistent preferences (localStorage) - Default metrics: cpu, qps, write_bytes_per_second, replicas, leases - Synchronized zoom across all charts with reliable undo functionality - Copy timeseries data as JSON - Performance optimizations for (relatively) snappy rendering Usage: - go run pkg/kv/kvserver/asim/tests/cmd/asimview - open browser at http://localhost:8080 The tool automatically finds the repo root and serves files from testdata/generated/. The old static viewer.html has been replaced with a README pointing to the new tool. 🤖 Generated with [Claude Code](https://claude.ai/code) Cost: $48.59 Epic: CRDB-49117 153461: roachprod: load balancing mode r=srosenberg,golgeek a=herkolategan Previously, the load balancer was set to use utilization, but for most testing purposes we want this to be round-robin. This change updates the load balancer to use a max connections setting per instance group which essentially causes the load balancer to use round-robin amongst each group. Epic: None Release note: None Co-authored-by: Steven Danna <[email protected]> Co-authored-by: Tobias Grieger <[email protected]> Co-authored-by: Herko Lategan <[email protected]>
5 parents 249793c + 32723e5 + 405dc22 + 4d4415e + e1b27d0 commit c2c042f

File tree

14 files changed

+1432
-438
lines changed

14 files changed

+1432
-438
lines changed

pkg/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,8 @@ GO_TARGETS = [
15171517
"//pkg/kv/kvserver/asim/state:state_test",
15181518
"//pkg/kv/kvserver/asim/storerebalancer:storerebalancer",
15191519
"//pkg/kv/kvserver/asim/storerebalancer:storerebalancer_test",
1520+
"//pkg/kv/kvserver/asim/tests/cmd/asimview:asimview",
1521+
"//pkg/kv/kvserver/asim/tests/cmd/asimview:asimview_lib",
15201522
"//pkg/kv/kvserver/asim/tests:tests",
15211523
"//pkg/kv/kvserver/asim/tests:tests_test",
15221524
"//pkg/kv/kvserver/asim/workload:workload",

pkg/gossip/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ go_library(
3838
"//pkg/util/syncutil",
3939
"//pkg/util/timeutil",
4040
"//pkg/util/uuid",
41+
"@com_github_cockroachdb_crlib//crtime",
4142
"@com_github_cockroachdb_errors//:errors",
4243
"@com_github_cockroachdb_logtags//:logtags",
4344
"@com_github_cockroachdb_redact//:redact",

pkg/gossip/gossip.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import (
6666
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
6767
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
6868
"github.com/cockroachdb/cockroach/pkg/util/uuid"
69+
"github.com/cockroachdb/crlib/crtime"
6970
"github.com/cockroachdb/errors"
7071
"github.com/cockroachdb/logtags"
7172
"github.com/cockroachdb/redact"
@@ -1620,7 +1621,7 @@ func (g *Gossip) TestingAddInfoProtoAndWaitForAllCallbacks(
16201621
method: func(_ string, _ roachpb.Value, _ int64) {
16211622
wg.Done()
16221623
},
1623-
schedulingTime: timeutil.Now(),
1624+
schedulingTime: crtime.NowMono(),
16241625
})
16251626
cb.cw.mu.Unlock()
16261627
}

pkg/gossip/infostore.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/cockroachdb/cockroach/pkg/util/stop"
2323
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
2424
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
25+
"github.com/cockroachdb/crlib/crtime"
2526
"github.com/cockroachdb/errors"
2627
"github.com/cockroachdb/redact"
2728
)
@@ -77,7 +78,7 @@ type callback struct {
7778
// a callback.
7879
type callbackWorkItem struct {
7980
// schedulingTime is the time when the callback was scheduled.
80-
schedulingTime time.Time
81+
schedulingTime crtime.Mono
8182
method Callback
8283
// key, content, origTimestamp are the parameters that will be passed to the
8384
// callback method. They are based on the infos added to the infostore.
@@ -269,17 +270,17 @@ func (is *infoStore) launchCallbackWorker(ambient log.AmbientContext, cw *callba
269270

270271
// Execute all the callbacks in the queue, making sure to update the
271272
// metrics accordingly.
273+
afterQueue := crtime.NowMono()
272274
for _, work := range wq {
273-
afterQueue := timeutil.Now()
274-
queueDur := afterQueue.Sub(work.schedulingTime)
275+
queueDur := work.schedulingTime.Sub(afterQueue)
275276
is.metrics.CallbacksPending.Dec(1)
276277
if queueDur >= minCallbackDurationToRecord {
277278
is.metrics.CallbacksPendingDuration.RecordValue(queueDur.Nanoseconds())
278279
}
279280

280281
work.method(work.key, work.content, work.origTimestamp)
281282

282-
afterProcess := timeutil.Now()
283+
afterProcess := crtime.NowMono()
283284
processDur := afterProcess.Sub(afterQueue)
284285
is.metrics.CallbacksProcessed.Inc(1)
285286
if processDur > minCallbackDurationToRecord {
@@ -476,7 +477,7 @@ func (is *infoStore) runCallbacks(
476477
}
477478

478479
// Add the callbacks to the callback work list.
479-
beforeQueue := timeutil.Now()
480+
beforeQueue := crtime.NowMono()
480481
for _, cb := range callbacks {
481482
cb.cw.mu.Lock()
482483
is.metrics.CallbacksPending.Inc(1)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
2+
3+
go_library(
4+
name = "asimview_lib",
5+
srcs = ["main.go"],
6+
embedsrcs = ["viewer.html"],
7+
importpath = "github.com/cockroachdb/cockroach/pkg/kv/kvserver/asim/tests/cmd/asimview",
8+
visibility = ["//visibility:private"],
9+
deps = [
10+
"//pkg/cli/exit",
11+
"@com_github_cockroachdb_errors//:errors",
12+
"@com_github_cockroachdb_errors//oserror",
13+
],
14+
)
15+
16+
go_binary(
17+
name = "asimview",
18+
embed = [":asimview_lib"],
19+
visibility = ["//visibility:public"],
20+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ASIM Test Results Viewer
2+
3+
Interactive web viewer for ASIM test JSON output files with fuzzy search and multi-file comparison.
4+
5+
## Usage
6+
7+
```bash
8+
# Default: serves files from repo's testdata/generated directory
9+
go run .
10+
11+
# Or specify a custom directory
12+
go run . /path/to/json/files
13+
14+
# Custom port
15+
go run . -port 8081
16+
```
17+
18+
Then open http://localhost:8080 in your browser.
19+
20+
## Features
21+
22+
- **Fuzzy Search**: Type any part of test name or file name to filter
23+
- **Multiple Selection**: Select multiple test files to compare side-by-side
24+
- **Synchronized Zoom**: Drag to zoom on any chart, all charts sync automatically
25+
- **Copy Data**: Click the clipboard button on any chart to copy its timeseries data as JSON
26+
- **Auto-discovery**: Recursively finds all JSON files in the specified directory
27+
- **Local Storage**: Remembers your last selection and zoom state. (Can refresh the browser window to update loaded files).
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2025 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the CockroachDB Software License
4+
// included in the /LICENSE file.
5+
6+
package main
7+
8+
import (
9+
_ "embed"
10+
"encoding/json"
11+
"flag"
12+
"fmt"
13+
"io"
14+
"net/http"
15+
"os"
16+
"os/exec"
17+
"path/filepath"
18+
"strings"
19+
20+
"github.com/cockroachdb/cockroach/pkg/cli/exit"
21+
"github.com/cockroachdb/errors"
22+
"github.com/cockroachdb/errors/oserror"
23+
)
24+
25+
//go:embed viewer.html
26+
var viewerHTML string
27+
28+
type FileInfo struct {
29+
Path string `json:"path"`
30+
Name string `json:"name"`
31+
TestName string `json:"testName"`
32+
}
33+
34+
func main() {
35+
var port int
36+
flag.IntVar(&port, "port", 8080, "Port to serve on")
37+
flag.Parse()
38+
39+
dir := flag.Arg(0)
40+
if err := mainErr(port, dir); err != nil {
41+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
42+
exit.WithCode(exit.UnspecifiedError())
43+
}
44+
}
45+
46+
func mainErr(port int, dir string) error {
47+
if dir == "" {
48+
// Find git repo root and default to generated testdata
49+
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
50+
output, err := cmd.Output()
51+
if err != nil {
52+
return errors.Errorf("failed to find git repo root: %w", err)
53+
}
54+
repoRoot := strings.TrimSpace(string(output))
55+
dir = filepath.Join(repoRoot, "pkg/kv/kvserver/asim/tests/testdata/generated")
56+
}
57+
58+
absDir, err := filepath.Abs(dir)
59+
if err != nil {
60+
return errors.Errorf("failed to resolve directory: %w", err)
61+
}
62+
63+
if _, err := os.Stat(absDir); oserror.IsNotExist(err) {
64+
return errors.Errorf("directory does not exist: %s", absDir)
65+
}
66+
67+
fmt.Printf("Serving files from: %s\n", absDir)
68+
fmt.Printf("Viewer available at: http://localhost:%d\n", port)
69+
70+
http.HandleFunc("/", serveViewer)
71+
http.HandleFunc("/api/files", makeFileListHandler(absDir))
72+
http.HandleFunc("/api/file/", makeFileHandler(absDir))
73+
74+
return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
75+
}
76+
77+
func serveViewer(w http.ResponseWriter, r *http.Request) {
78+
w.Header().Set("Content-Type", "text/html")
79+
w.Write([]byte(viewerHTML))
80+
}
81+
82+
func makeFileListHandler(baseDir string) http.HandlerFunc {
83+
return func(w http.ResponseWriter, r *http.Request) {
84+
var files []FileInfo
85+
86+
err := filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error {
87+
if err != nil {
88+
return err
89+
}
90+
if !info.IsDir() && strings.HasSuffix(path, ".json") {
91+
relPath, _ := filepath.Rel(baseDir, path)
92+
93+
// Extract test name from file name only (ignore directories)
94+
baseName := filepath.Base(path)
95+
testName := strings.TrimSuffix(baseName, ".json")
96+
97+
files = append(files, FileInfo{
98+
Path: relPath,
99+
Name: baseName,
100+
TestName: testName,
101+
})
102+
}
103+
return nil
104+
})
105+
106+
if err != nil {
107+
http.Error(w, err.Error(), http.StatusInternalServerError)
108+
return
109+
}
110+
111+
w.Header().Set("Content-Type", "application/json")
112+
w.Header().Set("Access-Control-Allow-Origin", "*")
113+
json.NewEncoder(w).Encode(files)
114+
}
115+
}
116+
117+
func makeFileHandler(baseDir string) http.HandlerFunc {
118+
return func(w http.ResponseWriter, r *http.Request) {
119+
// Extract file path from URL
120+
filePath := strings.TrimPrefix(r.URL.Path, "/api/file/")
121+
122+
// Prevent directory traversal
123+
cleanPath := filepath.Clean(filePath)
124+
if strings.Contains(cleanPath, "..") {
125+
http.Error(w, "Invalid path", http.StatusBadRequest)
126+
return
127+
}
128+
129+
fullPath := filepath.Join(baseDir, cleanPath)
130+
131+
// Check if file exists and is within baseDir
132+
if !strings.HasPrefix(fullPath, baseDir) {
133+
http.Error(w, "Invalid path", http.StatusBadRequest)
134+
return
135+
}
136+
137+
file, err := os.Open(fullPath)
138+
if err != nil {
139+
http.Error(w, "File not found", http.StatusNotFound)
140+
return
141+
}
142+
defer file.Close()
143+
144+
w.Header().Set("Content-Type", "application/json")
145+
w.Header().Set("Access-Control-Allow-Origin", "*")
146+
io.Copy(w, file)
147+
}
148+
}

0 commit comments

Comments
 (0)