Skip to content

Commit 0f31d50

Browse files
craig[bot]AlexTalksrickystewartsjbaragDrewKimball
committed
107110: kvserver: avoid running intensive decommission test under deadlock r=AlexTalks a=AlexTalks The kvserver test `TestDecommission`, which runs a 5-node cluster and decommissions 4 of those 5 nodes, has trouble completing fast enough when under a race or deadlock configuration. While race configurations were already skipped, this modifies the test to be skipped under deadlock configurations as well. Fixes: cockroachdb#106096 Release note: None 107111: compose: start `docker-compose` with a non-empty `PATH` r=rail a=rickystewart `docker-compose` invokes `docker`, but obviously this will fail if there is nothing in the `PATH`. Epic: none Release note: None 107129: dev: reject builds when CRL JS dependencies are 'pnpm link'ed r=rickystewart a=sjbarag When working with first-party JS dependencies that aren't in this monorepo, the idiomatic development workflow uses pnpm link [1] to link multiple JS packages together. Specifically, running `pnpm link /path/to/github.com/cockroachdb/ui/packages/foo` from within pkg/ui/workspaces/* creates a symbolic link at node_modules/`@cockroachlabs/foo` that points to ../../(…)/ui/packages/foo. This works quite smoothly for local development, as changes in the 'foo' package are automatically seen by a `pnpm webpack --watch` running in CRDB. The two packages act like they're maintained in the same repo, while allowing independent version history, CI processes, etc. Unfortunately, rules_js currently offers no way to link outside of the Bazel workspace. Such a symlink is either ignored by rules_js (since it doesn't appear in pnpm-lock.yaml) or is rejected if it's manually added to the lockfile [2]. Allowing Bazel-based builds of CockroachDB when 'pnpm link'ed packages are present introduces dependency skew between Bazel and non-Bazel builds. To allow `pnpm link` to be used safely, pre-emptively reject builds of 'cockroach' and 'cockroach-oss' that are run through the 'dev' helper when linked `@cockroachlabs/` packages are detected. [1] https://pnpm.io/cli/link [2] aspect-build/rules_js#1165 Release note: None Epic: none ----- Example output: <img width="998" alt="image" src="https://github.com/cockroachdb/cockroach/assets/665775/3fd43abe-f5c2-4ddd-bc60-16a73db12836"> Total duration is 16ms on my machine since we're checking so few files. We can drop these into a goroutine per first-party JS if we want, but this is certainly fast enough to be conversational. 107149: opt: make functional dependency calculation deterministic r=DrewKimball a=DrewKimball We recently added additional logic for inferring functional dependencies for join expressions. However, this logic iterates through a map, which leads to nondeterminism in which order functional dependencies are added to the FD set. Functional dependency calculation is best-effort, so this can lead to a different resulting FD set, which causes flaky tests. This patch makes the calculation deterministic by iterating instead through a `intsets.Fast` set. Fixes cockroachdb#107148 Fixes cockroachdb#107162 Release note: None 107181: dev: when cross-building, use `-c opt` r=rail a=rickystewart This enables optimizations which you probably want for a cross-build. Epic: CRDB-17171 Release note: None 107183: acceptance: add log dir as a writable path r=rail a=rickystewart Otherwise you get a sandbox error. Epic: CRDB-17171 Release note: None Co-authored-by: Alex Sarkesian <[email protected]> Co-authored-by: Ricky Stewart <[email protected]> Co-authored-by: Sean Barag <[email protected]> Co-authored-by: Drew Kimball <[email protected]>
7 parents 30acaf9 + f3b8cf6 + 865e6e7 + 3ba9bcb + 75898dd + 5e47f7c + 24fead2 commit 0f31d50

File tree

10 files changed

+245
-39
lines changed

10 files changed

+245
-39
lines changed

dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fi
88
set -euo pipefail
99

1010
# Bump this counter to force rebuilding `dev` on all machines.
11-
DEV_VERSION=80
11+
DEV_VERSION=81
1212

1313
THIS_DIR=$(cd "$(dirname "$0")" && pwd)
1414
BINARY_DIR=$THIS_DIR/bin/dev-versions

pkg/cmd/dev/acceptance.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (d *dev) acceptance(cmd *cobra.Command, commandLine []string) error {
9393
args = append(args, "--test_arg", "-test.v")
9494
}
9595
args = append(args, fmt.Sprintf("--test_arg=-l=%s", logDir))
96+
args = append(args, fmt.Sprintf("--sandbox_writable_path=%s", logDir))
9697
args = append(args, "--test_env=TZ=America/New_York")
9798
args = append(args, fmt.Sprintf("--test_arg=-b=%s", cockroachBin))
9899
args = append(args, additionalBazelArgs...)

pkg/cmd/dev/build.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ import (
2929
)
3030

3131
const (
32-
crossFlag = "cross"
33-
nogoDisableFlag = "--//build/toolchains:nogo_disable_flag"
34-
geosTarget = "//c-deps:libgeos"
35-
devTarget = "//pkg/cmd/dev:dev"
32+
crossFlag = "cross"
33+
cockroachTargetOss = "//pkg/cmd/cockroach-oss:cockroach-oss"
34+
cockroachTarget = "//pkg/cmd/cockroach:cockroach"
35+
nogoDisableFlag = "--//build/toolchains:nogo_disable_flag"
36+
geosTarget = "//c-deps:libgeos"
37+
devTarget = "//pkg/cmd/dev:dev"
3638
)
3739

3840
type buildTarget struct {
@@ -77,9 +79,9 @@ var buildTargetMapping = map[string]string{
7779
"bazel-remote": bazelRemoteTarget,
7880
"buildifier": "@com_github_bazelbuild_buildtools//buildifier:buildifier",
7981
"buildozer": "@com_github_bazelbuild_buildtools//buildozer:buildozer",
80-
"cockroach": "//pkg/cmd/cockroach:cockroach",
82+
"cockroach": cockroachTarget,
8183
"cockroach-sql": "//pkg/cmd/cockroach-sql:cockroach-sql",
82-
"cockroach-oss": "//pkg/cmd/cockroach-oss:cockroach-oss",
84+
"cockroach-oss": cockroachTargetOss,
8385
"cockroach-short": "//pkg/cmd/cockroach-short:cockroach-short",
8486
"crlfmt": "@com_github_cockroachdb_crlfmt//:crlfmt",
8587
"dev": devTarget,
@@ -95,7 +97,7 @@ var buildTargetMapping = map[string]string{
9597
"obsservice": "//pkg/obsservice/cmd/obsservice:obsservice",
9698
"optgen": "//pkg/sql/opt/optgen/cmd/optgen:optgen",
9799
"optfmt": "//pkg/sql/opt/optgen/cmd/optfmt:optfmt",
98-
"oss": "//pkg/cmd/cockroach-oss:cockroach-oss",
100+
"oss": cockroachTargetOss,
99101
"reduce": "//pkg/cmd/reduce:reduce",
100102
"roachprod": "//pkg/cmd/roachprod:roachprod",
101103
"roachprod-stress": "//pkg/cmd/roachprod-stress:roachprod-stress",
@@ -173,6 +175,10 @@ func (d *dev) build(cmd *cobra.Command, commandLine []string) error {
173175
configArgs := getConfigArgs(args)
174176
configArgs = append(configArgs, getConfigArgs(additionalBazelArgs)...)
175177

178+
if err := d.assertNoLinkedNpmDeps(buildTargets); err != nil {
179+
return err
180+
}
181+
176182
if cross == "" {
177183
// Do not log --build_event_binary_file=... because it is not relevant to the actual call
178184
// from the user perspective.
@@ -200,7 +206,7 @@ func (d *dev) crossBuild(
200206
volume string,
201207
dockerArgs []string,
202208
) error {
203-
bazelArgs = append(bazelArgs, fmt.Sprintf("--config=%s", crossConfig), "--config=ci")
209+
bazelArgs = append(bazelArgs, fmt.Sprintf("--config=%s", crossConfig), "--config=ci", "-c", "opt")
204210
configArgs := getConfigArgs(bazelArgs)
205211
dockerArgs, err := d.getDockerRunArgs(ctx, volume, false, dockerArgs)
206212
if err != nil {

pkg/cmd/dev/io/os/os.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,34 @@ func (o *OS) Readlink(filename string) (string, error) {
208208
})
209209
}
210210

211+
// ReadDir is a thin wrapper around os.ReadDir, which returns the names of files
212+
// or directories within dirname.
213+
func (o *OS) ReadDir(dirname string) ([]string, error) {
214+
command := fmt.Sprintf("ls %s", dirname)
215+
if !o.knobs.silent {
216+
o.logger.Print(command)
217+
}
218+
219+
output, err := o.Next(command, func() (string, error) {
220+
var ret []string
221+
entries, err := os.ReadDir(dirname)
222+
if err != nil {
223+
return "", err
224+
}
225+
226+
for _, entry := range entries {
227+
ret = append(ret, entry.Name())
228+
}
229+
230+
return fmt.Sprintf("%s\n", strings.Join(ret, "\n")), nil
231+
})
232+
233+
if err != nil {
234+
return nil, err
235+
}
236+
return strings.Split(strings.TrimSpace(output), "\n"), nil
237+
}
238+
211239
// IsDir wraps around os.Stat, which returns the os.FileInfo of the named
212240
// directory. IsDir returns true if and only if it is an existing directory.
213241
// If there is an error, it will be of type *PathError.

pkg/cmd/dev/testdata/datadriven/dev-build

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ cp sandbox/pkg/cmd/cockroach-short/cockroach-short_/cockroach-short crdb-checkou
4949
exec
5050
dev build -- --verbose_failures --sandbox_debug
5151
----
52+
bazel info workspace --color=no
53+
ls crdb-checkout/pkg/ui/node_modules/@cockroachlabs
54+
ls crdb-checkout/pkg/ui/workspaces/eslint-plugin-crdb/node_modules/@cockroachlabs
55+
ls crdb-checkout/pkg/ui/workspaces/db-console/src/js/node_modules/@cockroachlabs
56+
ls crdb-checkout/pkg/ui/workspaces/db-console/ccl/src/js/node_modules/@cockroachlabs
57+
ls crdb-checkout/pkg/ui/workspaces/cluster-ui/node_modules/@cockroachlabs
58+
ls crdb-checkout/pkg/ui/workspaces/db-console/node_modules/@cockroachlabs
59+
ls crdb-checkout/pkg/ui/workspaces/e2e-tests/node_modules/@cockroachlabs
5260
bazel build //pkg/cmd/cockroach:cockroach --verbose_failures --sandbox_debug --build_event_binary_file=/tmp/path
5361
bazel info workspace --color=no
5462
mkdir crdb-checkout/bin

pkg/cmd/dev/ui.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package main
1111

1212
import (
13+
"errors"
1314
"fmt"
1415
"log"
1516
"net/url"
@@ -48,6 +49,8 @@ func makeUICmd(d *dev) *cobra.Command {
4849

4950
// UIDirectories contains the absolute path to the root of each UI sub-project.
5051
type UIDirectories struct {
52+
workspace string
53+
// workspace is the absolute path to ./ .
5154
// root is the absolute path to ./pkg/ui.
5255
root string
5356
// clusterUI is the absolute path to ./pkg/ui/workspaces/cluster-ui.
@@ -58,6 +61,10 @@ type UIDirectories struct {
5861
e2eTests string
5962
// eslintPlugin is the absolute path to ./pkg/ui/workspaces/eslint-plugin-crdb.
6063
eslintPlugin string
64+
// protoOss is the absolute path to ./pkg/ui/workspaces/db-console/src/js/.
65+
protoOss string
66+
// protoCcl is the absolute path to ./pkg/ui/workspaces/db-console/ccl/src/js/.
67+
protoCcl string
6168
}
6269

6370
// getUIDirs computes the absolute path to the root of each UI sub-project.
@@ -68,14 +75,170 @@ func getUIDirs(d *dev) (*UIDirectories, error) {
6875
}
6976

7077
return &UIDirectories{
78+
workspace: workspace,
7179
root: filepath.Join(workspace, "./pkg/ui"),
7280
clusterUI: filepath.Join(workspace, "./pkg/ui/workspaces/cluster-ui"),
7381
dbConsole: filepath.Join(workspace, "./pkg/ui/workspaces/db-console"),
7482
e2eTests: filepath.Join(workspace, "./pkg/ui/workspaces/e2e-tests"),
7583
eslintPlugin: filepath.Join(workspace, "./pkg/ui/workspaces/eslint-plugin-crdb"),
84+
protoOss: filepath.Join(workspace, "./pkg/ui/workspaces/db-console/src/js"),
85+
protoCcl: filepath.Join(workspace, "./pkg/ui/workspaces/db-console/ccl/src/js"),
7686
}, nil
7787
}
7888

89+
// assertNoLinkedNpmDeps looks for JS packages linked outside the Bazel
90+
// workspace (typically via `pnpm link`). It returns an error if:
91+
//
92+
// 'targets' contains a Bazel target that requires the web UI
93+
// AND
94+
// a node_modules/ tree exists within pkg/ui (or its subtrees)
95+
// AND
96+
// a @cockroachlabs-scoped package is symlinked to an external directory
97+
//
98+
// (or if any error occurs while performing one of those checks).
99+
func (d *dev) assertNoLinkedNpmDeps(targets []buildTarget) error {
100+
uiWillBeBuilt := false
101+
for _, target := range targets {
102+
// TODO: This could potentially be a bazel query, e.g.
103+
// 'somepath(${target.fullName}, //pkg/ui/workspaces/db-console:*)' or
104+
// similar, but with only two eligible targets it doesn't seem quite
105+
// worth it.
106+
if target.fullName == cockroachTarget || target.fullName == cockroachTargetOss {
107+
uiWillBeBuilt = true
108+
break
109+
}
110+
}
111+
if !uiWillBeBuilt {
112+
// If no UI build is required, the presence of an externally-linked
113+
// package doesn't matter.
114+
return nil
115+
}
116+
117+
// Find the current workspace and build some relevant absolute paths.
118+
uiDirs, err := getUIDirs(d)
119+
if err != nil {
120+
return fmt.Errorf("could not check for linked NPM dependencies: %w", err)
121+
}
122+
123+
jsPkgRoots := []string{
124+
uiDirs.root,
125+
uiDirs.eslintPlugin,
126+
uiDirs.protoOss,
127+
uiDirs.protoCcl,
128+
uiDirs.clusterUI,
129+
uiDirs.dbConsole,
130+
uiDirs.e2eTests,
131+
}
132+
133+
type LinkedPackage struct {
134+
name string
135+
dir string
136+
}
137+
138+
anyPackageEscapesWorkspace := false
139+
140+
// Check for symlinks in each package's node_modules/@cockroachlabs/ dir.
141+
for _, jsPkgRoot := range jsPkgRoots {
142+
crlModulesPath := filepath.Join(jsPkgRoot, "node_modules/@cockroachlabs")
143+
crlDeps, err := d.os.ReadDir(crlModulesPath)
144+
145+
// If node_modules/@cockroachlabs doesn't exist, it's likely that JS
146+
// dependencies haven't been installed outside the Bazel workspace.
147+
// This is expected for non-UI devs, and is a safe state.
148+
if errors.Is(err, os.ErrNotExist) {
149+
continue
150+
}
151+
if err != nil {
152+
return fmt.Errorf("could not @cockroachlabs/ packages: %w", err)
153+
}
154+
155+
linkedPackages := []LinkedPackage{}
156+
157+
// For each dependency in node_modules/@cockroachlabs/ ...
158+
for _, depName := range crlDeps {
159+
// Ignore empty strings, which are produced by d.os.ReadDir in
160+
// dry-run mode.
161+
if depName == "" {
162+
continue
163+
}
164+
165+
// Resolve the possible symlink.
166+
depPath := filepath.Join(crlModulesPath, depName)
167+
resolved, err := d.os.Readlink(depPath)
168+
if err != nil {
169+
return fmt.Errorf("could not evaluate symlink %s: %w", depPath, err)
170+
}
171+
172+
// Convert it to a path relative to the Bazel workspace root to make
173+
// it obvious when a link escapes the workspace.
174+
relativeToWorkspace, err := filepath.Rel(
175+
uiDirs.workspace,
176+
filepath.Join(crlModulesPath, resolved),
177+
)
178+
if err != nil {
179+
return fmt.Errorf("could not relativize path %s: %w", resolved, err)
180+
}
181+
182+
// If it doesn't start with '..', it doesn't escape the Bazel
183+
// workspace.
184+
// TODO: Once Go 1.20 is supported here, switch to filepath.IsLocal.
185+
if !strings.HasPrefix(relativeToWorkspace, "..") {
186+
continue
187+
}
188+
189+
// This package escapes the Bazel workspace! Add it to the queue
190+
// with its absolute path for simpler presentation to users.
191+
abs, err := filepath.Abs(relativeToWorkspace)
192+
if err != nil {
193+
return fmt.Errorf("could not absolutize path %s: %w", resolved, err)
194+
}
195+
196+
linkedPackages = append(
197+
linkedPackages,
198+
LinkedPackage{
199+
name: "@cockroachlabs/" + depName,
200+
dir: abs,
201+
},
202+
)
203+
}
204+
205+
// If this internal package has no dependencies provided by pnpm link,
206+
// move on without logging anything.
207+
if len(linkedPackages) == 0 {
208+
continue
209+
}
210+
211+
if !anyPackageEscapesWorkspace {
212+
anyPackageEscapesWorkspace = true
213+
log.Println("Externally-linked package(s) detected:")
214+
}
215+
216+
log.Printf("pkg/ui/workspaces/%s:", filepath.Base(jsPkgRoot))
217+
for _, pkg := range linkedPackages {
218+
log.Printf(" %s <- %s\n", pkg.name, pkg.dir)
219+
}
220+
log.Println()
221+
}
222+
223+
if anyPackageEscapesWorkspace {
224+
msg := strings.TrimSpace(`
225+
At least one JS dependency is linked to another directory on your machine.
226+
Bazel cannot see changes in these packages, which could lead to both
227+
false-positive and false-negative behavior in the UI.
228+
This build has been pre-emptively failed.
229+
230+
To build without the UI, run:
231+
dev build short
232+
To remove all linked dependencies, run:
233+
dev ui clean --all
234+
`) + "\n"
235+
236+
return fmt.Errorf("%s", msg)
237+
}
238+
239+
return nil
240+
}
241+
79242
// makeUIWatchCmd initializes the 'ui watch' subcommand, which sets up a
80243
// live-reloading HTTP server for db-console and a file-watching rebuilder for
81244
// cluster-ui.

pkg/compose/compose_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func TestComposeCompare(t *testing.T) {
115115
fmt.Sprintf("COMPARE_DIR_PATH=%s", compareDir),
116116
fmt.Sprintf("ARTIFACTS=%s", *flagArtifacts),
117117
fmt.Sprintf("COCKROACH_DEV_LICENSE=%s", envutil.EnvOrDefaultString("COCKROACH_DEV_LICENSE", "")),
118+
fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
118119
}
119120
out, err := cmd.CombinedOutput()
120121
if err != nil {

pkg/kv/kvserver/client_decommission_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ func TestDecommission(t *testing.T) {
3434
defer leaktest.AfterTest(t)()
3535
defer log.Scope(t).Close(t)
3636

37-
// Five nodes is too much to reliably run under testrace with our aggressive
38-
// liveness timings.
39-
skip.UnderRace(t, "#39807 and #37811")
37+
// Five nodes is too much to reliably run under race/deadlock with our
38+
// aggressive liveness timings.
39+
skip.UnderRaceWithIssue(t, 39807, "#39807 and #37811")
40+
skip.UnderDeadlockWithIssue(t, 39807, "#39807 and #37811")
4041

4142
ctx := context.Background()
4243
tc := testcluster.StartTestCluster(t, 5, base.TestClusterArgs{

0 commit comments

Comments
 (0)