Skip to content

Release session resources on partial-setup and error paths#88

Merged
Soph merged 1 commit into
mainfrom
fix/session-resource-leaks
Jun 18, 2026
Merged

Release session resources on partial-setup and error paths#88
Soph merged 1 commit into
mainfrom
fix/session-resource-leaks

Conversation

@Soph

@Soph Soph commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Problem

Two leaks in the syncer session lifecycle:

  1. newSession partial-setup failure. It opened the source transport (and started the memory-measurement ticker goroutine) before listing refs and setting up the target. If any later step failed it returned nil, err — so the caller had nothing to finish(), leaking the source conn (an SSH transport spawns a process), a half-open target conn, and the ticker goroutine.
  2. finish() never stopped the measurement ticker. It was only stopped when a Result was built, so any early error return from runSync et al. leaked the goroutine even on a fully-constructed session.

Fix

  • Add a success-guarded defer s.finish() in newSession, so a partial-setup failure releases everything; success = true is set only just before handing the session back (callers still defer s.finish() on success, so finish runs exactly once either way).
  • Hand the target conn to s.target as soon as it's opened, so cleanup can close it even if a target ref-listing step fails.
  • Have finish() also invoke the sync.Once-guarded measurementDone, stopping the ticker on every path without disturbing the happy-path measurement value.

Tests

TestStartMeasurementStopsGoroutineAndIsIdempotent confirms the closure stops its goroutine and is safe to call twice (the property finish() now relies on); TestStartMeasurementDisabledIsInert covers the disabled case. Full go test ./... passes.

🤖 Generated with Claude Code


Note

Medium Risk
Touches core session lifecycle and connection cleanup on failure paths; behavior change is limited to resource teardown but affects every sync entry point that uses newSession.

Overview
Fixes resource leaks when newSession fails mid-setup or when a sync exits before building a Result.

newSession now uses a success-guarded defer s.finish() so partial failures still tear down the source/target transports (including SSH subprocesses) and the memory-measurement ticker started at the beginning of setup. The target connection is attached to s.target as soon as it is opened so later ref-listing errors still get closed.

finish() also invokes the sync.Once-guarded measurementDone closure, stopping the measurement goroutine on error paths without changing the value already captured on the happy path.

New tests in measurement_test.go assert that startMeasurement stops its goroutine and remains safe when called twice (the behavior finish() now depends on).

Reviewed by Cursor Bugbot for commit f81cf1c. Configure here.

Two leaks in the syncer session lifecycle:

  - newSession opened the source transport (and started the memory-measurement
    ticker goroutine) before listing refs and setting up the target. If any of
    those steps failed it returned nil, err, so the caller had no session to
    finish() — leaking the source conn (an SSH transport spawns a process), a
    half-open target conn, and the ticker goroutine.
  - finish() never stopped the measurement ticker; it was only stopped when a
    Result was built, so any early error return from runSync et al. leaked the
    goroutine even on a fully-constructed session.

Add a success-guarded deferred cleanup in newSession that calls finish() unless
the session is handed back, hand the target conn to the session as soon as it
is opened so cleanup can close it, and have finish() also invoke the
sync.Once-guarded measurementDone so the ticker stops on every path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: e6526831fa74
@Soph Soph merged commit 52aa33b into main Jun 18, 2026
4 checks passed
@Soph Soph deleted the fix/session-resource-leaks branch June 18, 2026 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants