Skip to content

Fix empty diff being returned when single *cached* step had to be executed#567

Merged
mrnugget merged 8 commits into
mainfrom
mrn+es/fix-off-by-one-step-caching
Jul 20, 2021
Merged

Fix empty diff being returned when single *cached* step had to be executed#567
mrnugget merged 8 commits into
mainfrom
mrn+es/fix-off-by-one-step-caching

Conversation

@mrnugget

@mrnugget mrnugget commented Jul 14, 2021

Copy link
Copy Markdown
Contributor

This PR fixes a bug in the step-wise caching logic that @eseliger and I ran into last week.

Problem

When no cached result for the complete task was found, but a cached result for a single step and that step was the only left to execute then an empty diff was returned.

How to reproduce

  1. Execute a batch spec with the following steps

    steps:
      - run: echo "this is step 1" >> README.txt
        container: alpine:3
      - run: echo "this is step 2" >> README.md
        container: alpine:3

    the complete results and the results for each step are now cached.

  2. Update the batch spec and re-execute:

    steps:
      - run: echo "this is step 1" >> README.txt
        container: alpine:3

This will produce an empty diff because the Coordinator did not find a cached result for the complete task/batch spec, but for the first step.

runSteps didn't handle this case though, when only a single step had to be executed but that was also cached.

The fix

Take a look at my comments in the diffs here in GitHub to see what the fix is. Spoiler: it's just a condition and an early exit to handle the case of "cached step result == the only step to execute".

In order to get to this fix though I wrote a lot of tests, found one other bug (Files was not cached), and cleaned up the 300 line long runSteps function.

runSteps is now much easier to understand and doesn't contain all levels of abstraction. The outer runSteps concerns itself with which steps need to be executed and how to handle their results and executeSingleStep does the lower-level work of rendering templates, starting containers, setting up the logger, etc.

@mrnugget mrnugget force-pushed the mrn+es/fix-off-by-one-step-caching branch 2 times, most recently from ca99fb1 to 027fe5f Compare July 16, 2021 14:25
return execResult, stepResults, err
}

const workDir = "/work"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything below this line is "just" extracted code from runSteps. There is no new logic, it's the same code, but in separate functions.

Comment on lines +98 to +112
// If we have cached results and don't need to execute any more steps,
// we can quit
if startStep == len(opts.task.Steps) {
changes, err := git.ChangesInDiff(opts.task.CachedResult.Diff)
if err != nil {
return execResult, nil, errors.Wrap(err, "parsing cached step diff")
}

execResult.Diff = string(opts.task.CachedResult.Diff)
execResult.ChangedFiles = &changes
stepResults = append(stepResults, opts.task.CachedResult)

return execResult, stepResults, nil
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the fix for the problem this PR aims to address. The rest of the changes are either tests or were made to make this fix easy to make (like the removal of the whole results indexing, introducing previousStepResult, etc.)

@mrnugget mrnugget changed the title WIP: Fix step result caching if 1 step to execute and only step cache… Fix empty diff being returned when single *cached* step had to be executed Jul 19, 2021
@mrnugget mrnugget requested a review from a team July 19, 2021 13:33
@mrnugget mrnugget marked this pull request as ready for review July 19, 2021 13:33

@eseliger eseliger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love that cleanup. LGTM!

@courier-new courier-new left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good as far as I can tell! Nice cleanup, indeed!

@mrnugget mrnugget force-pushed the mrn+es/fix-off-by-one-step-caching branch from 16c5039 to 491487f Compare July 20, 2021 07:59
@mrnugget mrnugget merged commit e47e47d into main Jul 20, 2021
@mrnugget mrnugget deleted the mrn+es/fix-off-by-one-step-caching branch July 20, 2021 08:03
scjohns pushed a commit that referenced this pull request Apr 24, 2023
…cuted (#567)

This PR fixes a bug in the step-wise caching logic that @eseliger and I ran into last week.

### Problem

When no cached result for the *complete* task was found, but a cached result for a single step and that step was the only left to execute then an empty diff was returned.

### How to reproduce

1. Execute a batch spec with the following steps

    ```yaml
    steps:
      - run: echo "this is step 1" >> README.txt
        container: alpine:3
      - run: echo "this is step 2" >> README.md
        container: alpine:3
    ```
    the complete results and the results for each step are now cached.
2. Update the batch spec and re-execute:
    ```yaml
    steps:
      - run: echo "this is step 1" >> README.txt
        container: alpine:3
    ```

This will produce an empty diff because the `Coordinator` did not find a cached result for the complete task/batch spec, but for the first step.

`runSteps` didn't handle this case though, when only a single step had to be executed but that was also cached.

### The fix

Take a look at my comments in the diffs here in GitHub to see what the fix is. Spoiler: it's just a condition and an early exit to handle the case of "cached step result == the only step to execute".

In order to get to this fix though I wrote a lot of tests, found one other bug (`Files` was not cached), and cleaned up the 300 line long `runSteps` function.

`runSteps` is now much easier to understand and doesn't contain _all_ levels of abstraction. The outer `runSteps` concerns itself with which steps need to be executed and how to handle their results and `executeSingleStep` does the lower-level work of rendering templates, starting containers, setting up the logger, etc.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants