Skip to content

Commit 44d8725

Browse files
committed
Add CPU core detection.
1 parent 496fe8d commit 44d8725

File tree

3 files changed

+80
-10
lines changed

3 files changed

+80
-10
lines changed

cmd/src/batch_common.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/sourcegraph/src-cli/internal/api"
2525
"github.com/sourcegraph/src-cli/internal/batches"
26+
"github.com/sourcegraph/src-cli/internal/batches/docker"
2627
"github.com/sourcegraph/src-cli/internal/batches/executor"
2728
"github.com/sourcegraph/src-cli/internal/batches/graphql"
2829
"github.com/sourcegraph/src-cli/internal/batches/service"
@@ -121,8 +122,8 @@ func newBatchExecuteFlags(flagSet *flag.FlagSet, workspaceExecution bool, cacheD
121122
)
122123

123124
flagSet.IntVar(
124-
&caf.parallelism, "j", runtime.GOMAXPROCS(0),
125-
"The maximum number of parallel jobs. Default is GOMAXPROCS.",
125+
&caf.parallelism, "j", 0,
126+
"The maximum number of parallel jobs. Default (or 0) is the number of CPU cores available to Docker, or GOMAXPROCS if Docker cannot report its number of cores.",
126127
)
127128
flagSet.DurationVar(
128129
&caf.timeout, "timeout", 60*time.Minute,
@@ -275,7 +276,11 @@ func executeBatchSpec(ctx context.Context, ui ui.ExecUI, opts executeBatchSpecOp
275276
return err
276277
}
277278

278-
if err := checkExecutable("docker", "version"); err != nil {
279+
// In the past, we checked `docker version`, but now we retrieve the number
280+
// of CPUs, since we need that anyway and it performs the same check (is
281+
// Docker working _at all_?).
282+
parallelism, err := getBatchParallelism(ctx, opts.flags.parallelism)
283+
if err != nil {
279284
return err
280285
}
281286

@@ -307,7 +312,7 @@ func executeBatchSpec(ctx context.Context, ui ui.ExecUI, opts executeBatchSpecOp
307312
if len(batchSpec.Steps) > 0 {
308313
ui.PreparingContainerImages()
309314
images, err := svc.EnsureDockerImages(
310-
ctx, batchSpec.Steps, opts.flags.parallelism,
315+
ctx, batchSpec.Steps, parallelism,
311316
ui.PreparingContainerImagesProgress,
312317
)
313318
if err != nil {
@@ -354,7 +359,7 @@ func executeBatchSpec(ctx context.Context, ui ui.ExecUI, opts executeBatchSpecOp
354359
Cache: executor.NewDiskCache(opts.flags.cacheDir),
355360
SkipErrors: opts.flags.skipErrors,
356361
CleanArchives: opts.flags.cleanArchives,
357-
Parallelism: opts.flags.parallelism,
362+
Parallelism: parallelism,
358363
Timeout: opts.flags.timeout,
359364
KeepLogs: opts.flags.keepLogs,
360365
TempDir: opts.flags.tempDir,
@@ -386,7 +391,7 @@ func executeBatchSpec(ctx context.Context, ui ui.ExecUI, opts executeBatchSpecOp
386391
}
387392
ui.CheckingCacheSuccess(len(specs), len(uncachedTasks))
388393

389-
taskExecUI := ui.ExecutingTasks(*verbose, opts.flags.parallelism)
394+
taskExecUI := ui.ExecutingTasks(*verbose, parallelism)
390395
freshSpecs, logFiles, execErr := coord.ExecuteAndBuildSpecs(ctx, batchSpec, uncachedTasks, taskExecUI)
391396
// Add external changeset specs.
392397
importedSpecs, importErr := svc.CreateImportChangesetSpecs(ctx, batchSpec)
@@ -533,3 +538,25 @@ func contextCancelOnInterrupt(parent context.Context) (context.Context, func())
533538
ctxCancel()
534539
}
535540
}
541+
542+
func getBatchParallelism(ctx context.Context, flag int) (int, error) {
543+
if flag > 0 {
544+
return flag, nil
545+
}
546+
547+
ncpu, err := docker.NCPU(ctx)
548+
var terr docker.TimeoutError
549+
if errors.As(err, &terr) {
550+
return 0, err
551+
} else if err != nil {
552+
// In the case of errors from Docker itself, we want to fall back to
553+
// GOMAXPROCS, since it's possible Docker just doesn't have access to
554+
// the CPU core count (either due to permissions, or being too old).
555+
//
556+
// It would obviously be better if we had a global logger available to
557+
// log this.
558+
return runtime.GOMAXPROCS(0), nil
559+
}
560+
561+
return ncpu, nil
562+
}

cmd/src/batch_exec.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ func executeBatchSpecInWorkspaces(ctx context.Context, ui *ui.JSONLines, opts ex
9292
if err := checkExecutable("git", "version"); err != nil {
9393
return err
9494
}
95-
if err := checkExecutable("docker", "version"); err != nil {
95+
96+
// In the past, we checked `docker version`, but now we retrieve the number
97+
// of CPUs, since we need that anyway and it performs the same check (is
98+
// Docker working _at all_?).
99+
parallelism, err := getBatchParallelism(ctx, opts.flags.parallelism)
100+
if err != nil {
96101
return err
97102
}
98103

@@ -112,7 +117,7 @@ func executeBatchSpecInWorkspaces(ctx context.Context, ui *ui.JSONLines, opts ex
112117
if len(input.Steps) > 0 {
113118
ui.PreparingContainerImages()
114119
images, err := svc.EnsureDockerImages(
115-
ctx, input.Steps, opts.flags.parallelism,
120+
ctx, input.Steps, parallelism,
116121
ui.PreparingContainerImagesProgress,
117122
)
118123
if err != nil {
@@ -138,7 +143,7 @@ func executeBatchSpecInWorkspaces(ctx context.Context, ui *ui.JSONLines, opts ex
138143
Cache: &executor.ServerSideCache{Writer: ui},
139144
SkipErrors: opts.flags.skipErrors,
140145
CleanArchives: opts.flags.cleanArchives,
141-
Parallelism: opts.flags.parallelism,
146+
Parallelism: parallelism,
142147
Timeout: opts.flags.timeout,
143148
KeepLogs: opts.flags.keepLogs,
144149
TempDir: opts.flags.tempDir,
@@ -154,7 +159,7 @@ func executeBatchSpecInWorkspaces(ctx context.Context, ui *ui.JSONLines, opts ex
154159
return err
155160
}
156161

157-
taskExecUI := ui.ExecutingTasks(*verbose, opts.flags.parallelism)
162+
taskExecUI := ui.ExecutingTasks(*verbose, parallelism)
158163
err = coord.Execute(ctx, tasks, taskExecUI)
159164
if err == nil || opts.flags.skipErrors {
160165
if err == nil {

internal/batches/docker/info.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package docker
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"runtime"
7+
"strconv"
8+
9+
"github.com/sourcegraph/sourcegraph/lib/errors"
10+
11+
"github.com/sourcegraph/src-cli/internal/exec"
12+
)
13+
14+
// NCPU returns the number of CPU cores available to Docker.
15+
func NCPU(ctx context.Context) (int, error) {
16+
ncpu := runtime.GOMAXPROCS(0)
17+
18+
dctx, cancel, err := withFastCommandContext(ctx)
19+
if err != nil {
20+
return ncpu, err
21+
}
22+
defer cancel()
23+
24+
args := []string{"info", "--format", "{{ .NCPU }}"}
25+
out, err := exec.CommandContext(dctx, "docker", args...).CombinedOutput()
26+
if errors.Is(errors.Cause(err), context.DeadlineExceeded) {
27+
return ncpu, newFastCommandTimeoutError(dctx, args...)
28+
} else if err != nil {
29+
return ncpu, err
30+
}
31+
32+
dcpu, err := strconv.Atoi(string(bytes.TrimSpace(out)))
33+
if err != nil {
34+
return ncpu, err
35+
}
36+
37+
return dcpu, nil
38+
}

0 commit comments

Comments
 (0)