Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fail-fast flag to src batch #1049

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

gabtorre
Copy link

@gabtorre gabtorre commented Nov 23, 2023

This adds a new --fail-fast flag to the src batch commands. When enabled, any errors encountered while executing steps in a repository will stop the execution of the whole batch spec. The flag is off by default, so existing behavior is preserved.

  • A new CancelableContext type was added to support cancellation with errors. The batch commands now wrap the context with this to enable fail-fast behavior.
  • Updated contextCancelOnInterrupt to support cancellation with error causes.
  • Added error check in executeBatchSpec to surface fail-fast cancellation errors.
  • Errors now propagate up by calling the cancel context's Cause()
  • Minor code reorganizations around log file handling to return logs if -fail-fast and -keep-logs are used together.

Test plan

Updated tests.

@gabtorre gabtorre marked this pull request as ready for review November 30, 2023 05:02
@BolajiOlajide
Copy link
Contributor

Hey @gabtorre

Apologies for the long delay. I'll review this soon.
Thank you so much.

for _, task := range tasks {
fmt.Println(task.Repository)
Copy link
Contributor

Choose a reason for hiding this comment

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

Left over debug statement

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)

go func() {
select {
case <-c:
ctxCancel()
ctxCancel(errors.New("Ctrl-C hit"))
Copy link
Contributor

Choose a reason for hiding this comment

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

This is awesome. TIL I learned about the .WithCancelCause method.

@@ -101,7 +101,7 @@ Examples:
}

ctx, cancel := contextCancelOnInterrupt(context.Background())
defer cancel()
defer cancel(nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we should be passing in nil directly here, an error could occur at any point in the execution of a batch spec.

Is there a reason why the handler function doesn't return a named return value and we can then pass that into defer cancel?

handler := func(args []string) (err error) {
	...
	ctx, cancel := contextCancelOnInterrupt(context.Background())
	defer cancel(err)
	...
}

This way we are guaranteed to always have access to whatever error occurs in the lifecycle of an execution.

Comment on lines +52 to +55
cctx := executor.CancelableContext{
Context: ctx,
Cancel: failFastCancel,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a need for this struct? Is there a reason we can't pass in the context and failFastCancel as separate arguments?

Or better still have the failFastCancel as one of the fields in executeBatchSpecOpts, that way we are certain ctx is always going to be of type context.Context and wouldn't be confusing to anyone in the future;

@@ -177,7 +182,7 @@ func (c *Coordinator) buildSpecs(ctx context.Context, batchSpec *batcheslib.Batc

// ExecuteAndBuildSpecs executes the given tasks and builds changeset specs for the results.
// It calls the ui on updates.
func (c *Coordinator) ExecuteAndBuildSpecs(ctx context.Context, batchSpec *batcheslib.BatchSpec, tasks []*Task, ui TaskExecutionUI) ([]*batcheslib.ChangesetSpec, []string, error) {
func (c *Coordinator) ExecuteAndBuildSpecs(ctx CancelableContext, batchSpec *batcheslib.BatchSpec, tasks []*Task, ui TaskExecutionUI) ([]*batcheslib.ChangesetSpec, []string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the arguments to this method is getting bulky, we can move it into a struct and pass in the cancel function explicitly.

Suggested change
func (c *Coordinator) ExecuteAndBuildSpecs(ctx CancelableContext, batchSpec *batcheslib.BatchSpec, tasks []*Task, ui TaskExecutionUI) ([]*batcheslib.ChangesetSpec, []string, error) {
type ExecuteAndBuildSpecsArgs struct {
batchSpec *batcheslib.BatchSpec
tasks []*Task
ui TaskExecutionUI
cancelFunc context.CancelCauseFunc
}
func (c *Coordinator) ExecuteAndBuildSpecs(ctx context.Context, args ExecuteAndBuildSpecsArgs) ([]*batcheslib.ChangesetSpec, []string, error) {

Copy link
Contributor

Choose a reason for hiding this comment

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

The cancelFunc isn't being used here, I wonder if we need to pass it in here as an argument?

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.

2 participants