Skip to content

Commit e5f28d4

Browse files
authored
Implement step-wise caching for batch spec execution (#540)
This adds step-wise caching for the execution of batch specs. In short, given a batch spec that contains the following ```yaml on: - repository: github.com/sourcegraph/automation-testing - repository: github.com/sourcegraph-testing/mkcert steps: - run: echo "this is step 1" >> caching.txt container: alpine:3 - run: echo "this is step 2" >> README.md container: alpine:3 - run: echo "this is step 3" >> README.md container: alpine:3 outputs: myOutput: value: "what is up" - run: echo "this is step 4" >> caching.txt if: ${{ eq repository.name "github.com/sourcegraph/automation-testing" }} container: alpine:3 ``` this PR would cause `src` to cache the results _per step and per repository. In practice, this means that if we would change the batch spec from the above so it would look like this ```yaml steps: - run: echo "this is step 0" >> caching.txt container: alpine:3 - run: echo "this is step 1" >> README.md container: alpine:3 # vvvv LOOK HERE vvv - run: echo "this is step 2 WITH A CHANGE" >> README.md container: alpine:3 outputs: myOutput: value: "what is up" - run: echo "this is step 3" >> caching.txt if: ${{ eq repository.name "github.com/sourcegraph/automation-testing" }} container: alpine:3 ``` and we'd then re-execute the batch spec, **the steps 0 and 1 would not need to be re-executed**. Only step 2 would need to be re-executed for both repositories and step 3 would only need to be re-executed for both repositories.
1 parent 41ec9ae commit e5f28d4

21 files changed

+862
-410
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ All notable changes to `src-cli` are documented in this file.
1515

1616
### Changed
1717

18+
- `src batch [apply|preview]` now cache the results of each step when executing a batch spec. That can make re-execution a lot faster when only a subset of the steps has been changed. [#540](https://github.com/sourcegraph/src-cli/pull/540)
19+
1820
### Fixed
1921

2022
### Removed

internal/batches/executor/changeset_specs_test.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,15 @@ import (
88
"github.com/sourcegraph/batch-change-utils/overridable"
99
"github.com/sourcegraph/src-cli/internal/batches"
1010
"github.com/sourcegraph/src-cli/internal/batches/git"
11-
"github.com/sourcegraph/src-cli/internal/batches/graphql"
1211
)
1312

1413
func TestCreateChangesetSpecs(t *testing.T) {
15-
srcCLI := &graphql.Repository{
16-
ID: "src-cli",
17-
Name: "github.com/sourcegraph/src-cli",
18-
DefaultBranch: &graphql.Branch{Name: "main", Target: graphql.Target{OID: "d34db33f"}},
19-
}
20-
2114
defaultChangesetSpec := &batches.ChangesetSpec{
22-
BaseRepository: srcCLI.ID,
15+
BaseRepository: testRepo1.ID,
2316
CreatedChangeset: &batches.CreatedChangeset{
24-
BaseRef: srcCLI.DefaultBranch.Name,
25-
BaseRev: srcCLI.DefaultBranch.Target.OID,
26-
HeadRepository: srcCLI.ID,
17+
BaseRef: testRepo1.DefaultBranch.Name,
18+
BaseRev: testRepo1.DefaultBranch.Target.OID,
19+
HeadRepository: testRepo1.ID,
2720
HeadRef: "refs/heads/my-branch",
2821
Title: "The title",
2922
Body: "The body",
@@ -58,7 +51,7 @@ func TestCreateChangesetSpecs(t *testing.T) {
5851
},
5952
Published: parsePublishedFieldString(t, "false"),
6053
},
61-
Repository: srcCLI,
54+
Repository: testRepo1,
6255
}
6356

6457
taskWith := func(t *Task, f func(t *Task)) *Task {

internal/batches/executor/coordinator.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type Coordinator struct {
2828

2929
cache ExecutionCache
3030
exec taskExecutor
31-
logManager *log.Manager
31+
logManager log.LogManager
3232
}
3333

3434
type repoNameResolver func(ctx context.Context, name string) (*graphql.Repository, error)
@@ -154,6 +154,14 @@ func (c *Coordinator) cacheAndBuildSpec(ctx context.Context, taskResult taskResu
154154
return nil, errors.Wrapf(err, "caching result for %q", taskResult.task.Repository.Name)
155155
}
156156

157+
// Save the per-step results
158+
for _, stepResult := range taskResult.stepResults {
159+
key := StepsCacheKey{Task: taskResult.task, StepIndex: stepResult.StepIndex}
160+
if err := c.cache.SetStepResult(ctx, key, stepResult); err != nil {
161+
return nil, errors.Wrapf(err, "caching result for step %d in %q", stepResult.StepIndex, taskResult.task.Repository.Name)
162+
}
163+
}
164+
157165
// If the steps didn't result in any diff, we don't need to create a
158166
// changeset spec that's displayed to the user and send to the server.
159167
if taskResult.result.Diff == "" {
@@ -203,6 +211,27 @@ func (c *Coordinator) Execute(ctx context.Context, tasks []*Task, spec *batches.
203211
}()
204212
}
205213

214+
// If we are here, that means we didn't find anything in the cache for the
215+
// complete task. So, what if we have cached results for the steps?
216+
for _, t := range tasks {
217+
// We start at the back so that we can find the _last_ cached step,
218+
// then restart execution on the following step.
219+
for i := len(t.Steps) - 1; i > -1; i-- {
220+
key := StepsCacheKey{Task: t, StepIndex: i}
221+
222+
result, found, err := c.cache.GetStepResult(ctx, key)
223+
if err != nil {
224+
return nil, nil, errors.Wrapf(err, "checking for cached diff for step %d", i)
225+
}
226+
227+
if found {
228+
t.CachedResultFound = true
229+
t.CachedResult = result
230+
break
231+
}
232+
}
233+
}
234+
206235
// Run executor
207236
c.exec.Start(ctx, tasks, status)
208237
results, err := c.exec.Wait(ctx)

0 commit comments

Comments
 (0)