Skip to content

cli/internal/transformerpipeline/pipeline.go: goroutine leaks on pipeline close #23165

Description

@praneshnikhar

CLI: Goroutine leaks in cli/internal/transformerpipeline/pipeline.go

File: cli/internal/transformerpipeline/pipeline.go

Bug 1: Send method — unbuffered channel + blocking send

Problem: Send creates an unbuffered sendCh and spawns a goroutine that sends lp.client.Send(data) into it. The goroutine blocks forever writing to sendCh if the receive falls through the select (e.g., because the pipeline is closed). The goroutine leaks.

Current code:

sendCh := make(chan error) // unbuffered

// Send can block forever (e.g. if grpc buffer is full), so we run it asynchronously
// and check if pipeline is closed every second.

Fix: Make sendCh buffered (capacity 1) so the send goroutine never blocks.

Bug 2: startBlocking — unbuffered channels + missing close guard

Problem: startBlocking creates unbuffered recvCh and errCh, then spawns a goroutine that blocks forever on client.Recv(). When the pipeline closes, the goroutine never exits. Also missing isClosed guard — the goroutine should return early when the pipeline is shut down.

Current code:

recvCh := make(chan *plugin.Transform_Request) // unbuffered
errCh := make(chan error)                       // unbuffered

go func() {
    for {
        data, err := s.client.Recv()
        if err != nil {
            errCh <- err
        } else {
            recvCh <- &plugin.Transform_Request{Record: data.Record}
        }
    }
}()

Fix: Make both channels buffered (capacity 1) and add s.isClosed.Load() check at the top of the goroutine loop.

Reproduction

Run a sync with a transformer plugin configured. Cancel the sync (Ctrl+C). Check for goroutine leaks via pprof — leaked goroutines from startBlocking and Send persist after shutdown.

Example:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 2)
// Look for goroutines stuck in:
// - pipeline.go:sendCh <- err  (Send goroutine)
// - pipeline.go:recvCh <- ...  (startBlocking Recv goroutine)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions