Skip to content

New coffin implementation#1227

Draft
ajscholl wants to merge 2 commits into
mainfrom
graveyard
Draft

New coffin implementation#1227
ajscholl wants to merge 2 commits into
mainfrom
graveyard

Conversation

@ajscholl

Copy link
Copy Markdown
Contributor

Split between graveyard and coffin to address the problems we have with the current coffin

TODO: maybe write some release notes here;

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements a new coffin package to replace the existing tomb-based implementation, addressing concurrency and reusability issues with the current coffin system. The new implementation provides better goroutine lifecycle management and enhanced monitoring capabilities.

  • Replaces the tomb-based coffin implementation with a custom solution that supports coffin reuse and improved error handling
  • Updates all coffin usage across the codebase to use the new API with labeled goroutines for better observability
  • Adds comprehensive test coverage and fixes various configuration paths and import issues

Reviewed Changes

Copilot reviewed 80 out of 81 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pkg/coffin/coffin.go Complete rewrite of the coffin implementation with new API and improved concurrency handling
pkg/coffin/tomb.go New tomb interface for backwards compatibility
pkg/coffin/coffin_test.go Comprehensive test suite for the new coffin implementation
pkg/stream/producer_daemon.go Updated to use new coffin API with labeled goroutines
pkg/kernel/stage.go Refactored to use new coffin context management
Various other files Updated coffin usage throughout the codebase
Comments suppressed due to low confidence (1)

pkg/stream/consumer_batch_test.go:211

  • Changing from EqualError to Error + Contains reduces test precision. The specific error message validation is important for ensuring correct error propagation.
	s.Error(err)

Comment thread pkg/coffin/coffin.go
g.ctx, g.cancelCtx = context.WithCancelCause(g.baseCtx)
g.dead = make(chan Void)
g.dying = make(chan Void)
g.alive = mdl.Box[int32](1)

Copilot AI Jul 21, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Using mdl.Box[int32] for a boolean-like flag is unclear. Consider using a more descriptive type or direct atomic operations with int32.

Suggested change
g.alive = mdl.Box[int32](1)
var alive int32 = 1
g.alive = alive

Copilot uses AI. Check for mistakes.
Comment thread pkg/coffin/coffin.go
option(&opts)
}

go RunLabeled(opts.ctx, fmt.Sprintf("%s/%s", pkg, name), func() {

Copilot AI Jul 21, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] String formatting in the hot path of goroutine creation could impact performance. Consider pre-computing the label or using a string builder.

Suggested change
go RunLabeled(opts.ctx, fmt.Sprintf("%s/%s", pkg, name), func() {
label := pkg + "/" + name
go RunLabeled(opts.ctx, label, func() {

Copilot uses AI. Check for mistakes.
Comment thread pkg/coffin/coffin.go

func (c *coffin) Started() int {
return int(atomic.LoadInt32(&c.started))
func (g *coffin) closeIfOpen(c chan Void) {

Copilot AI Jul 21, 2025

Copy link

Choose a reason for hiding this comment

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

The closeIfOpen method assumes lock protection but this isn't enforced. Consider adding a comment or assertion to make this requirement explicit.

Copilot uses AI. Check for mistakes.
Comment thread pkg/kernel/stage.go
Comment thread pkg/log/status/module_test.go
election *concDdb.DdbLeaderElection
}

func (s *DdbLeaderElectionTestCase) SetupTest() {

Copilot AI Jul 21, 2025

Copy link

Choose a reason for hiding this comment

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

The change from NewLogger to NewLoggerMock appears unrelated to the coffin implementation and should be explained or moved to a separate change.

Suggested change
func (s *DdbLeaderElectionTestCase) SetupTest() {
func (s *DdbLeaderElectionTestCase) SetupTest() {
// Using NewLoggerMock to provide a mock logger implementation for testing purposes.
// This ensures that log messages are captured and validated during the test execution.

Copilot uses AI. Check for mistakes.
@@ -1,3 +1,4 @@
-- +goose Up

Copilot AI Jul 21, 2025

Copy link

Choose a reason for hiding this comment

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

Adding goose migration directives appears unrelated to the coffin implementation changes and should be explained or moved to a separate PR.

Copilot uses AI. Check for mistakes.
cfn, coffinCtx := coffin.WithContext(ctx)
cancelableCoffinCtx, cancel := context.WithCancel(coffinCtx)
cfn := coffin.New(ctx)
cancelableCoffinCtx, cancel := context.WithCancel(cfn.Ctx())

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.

I think we should first create the cancelContext from ctx, and then already pass that in New(). That makes the context structure in this function clearer and ensures that functions created without a WithContext Option are also cancelled (though this should be a smaller issue, as we should ensure during review that the context structure is correct).

cfn, cfnCtx := coffin.WithContext(ctx)
persisterCtx, cancelPersister := exec.WithManualCancelContext(cfnCtx)
cfn := coffin.New(ctx)
persisterCtx, cancelPersister := exec.WithManualCancelContext(cfn.Ctx())

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.

same here

Comment thread pkg/coffin/coffin.go
Ctx() context.Context
// Go spawns a new goroutine and runs the given function. If the go routine panics or returns an error, Err will return it.
Go(name string, f func() error, options ...Option)
// TODO: make GoWithContext default and drop Go?

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.

yes, i would advocate for the context variants being the default

Comment thread pkg/coffin/coffin.go
type Option func(options *coffinOptions)

// WithContext allows you to overwrite the context for a single goroutine as well as setting the default context for a Coffin.
func WithContext(ctx context.Context) Option {

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.

It feels somewhat weird that i can pass this to set a different default context when i am already passing a context to New. I wonder if it wouldnt be best to remove the context from New's signature and only pass options

Comment thread pkg/coffin/coffin.go
}
}

labelSet := pprof.Labels(labelArgs...)

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.

i think we need to save the default labels on the coffin. Right now they are only in the context, and when i am running a routine with the context option i would loose the default labels which is unexpected.

Comment thread pkg/coffin/coffin.go
// Terminated returns the number of goroutines that have already returned.
Terminated() int
// Entomb returns a Tomb for the current state of the Coffin. You can use it to wait on a channel until all goroutines are finished, or
// kill the currently running goroutines. If you kill a Tomb, you can no longer reuse the Coffin.

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.

Why is that ? From looking at the code it was not directly apparent to me why you would not be able to reuse the coffin, since once all routines are done and i schedule new one we would still reset the coffin, allowing a new tomb to be generated.
The only case i see where a coffin is not reusable is if the baseCtx is canceled.

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