Skip to content

collect: fix lazy channel draining leaks and align collection edge-case behavior#1409

Merged
hwbrzzl merged 6 commits intomasterfrom
copilot/address-review-comments-collection-library
Mar 12, 2026
Merged

collect: fix lazy channel draining leaks and align collection edge-case behavior#1409
hwbrzzl merged 6 commits intomasterfrom
copilot/address-review-comments-collection-library

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 11, 2026

This follow-up addresses high-impact issues from the collection rollout: LazyCollection early-exit paths could leave upstream channel producers blocked, and several Collection behaviors were inconsistent with expected semantics (zip length handling, duplicates, where equality, splice bounds, and where-in ergonomics).

  • LazyCollection: prevent blocked producers on early exits

    • Added channel draining in early-return/break paths for:
      • Every, First, FirstWhere, Some, Take, TakeWhile, Zip
    • Added internal drainChannel helper to consume remaining items when downstream stops early.
  • Collection behavior alignment and edge-case fixes

    • Zip now truncates to the shorter input (no partial trailing pairs).
    • Duplicates now returns each duplicated value once.
    • Splice now treats negative deleteCount as 0.
    • Where equality/inequality now supports numerically equivalent simple mixed types (e.g. int field vs numeric string input).
    • WhereIn / WhereNotIn now accept variadic inputs while preserving compatibility with existing []interface{} calls.
  • Performance and correctness refinements

    • Random now uses a shared RNG instance (mutex-protected) instead of per-call generator initialization.
    • Renamed lazy max/min locals to maxVal/minVal to avoid built-in name shadowing.
    • Corrected MapCollect doc comment grammar and GoDoc style.
    • Hardened comparable-type checks against nil-type reflection panics.
  • Focused coverage additions

    • Added regression tests for lazy early-exit drain behavior.
    • Added closed-channel iterator test.
    • Updated tests for zip truncation, unique duplicate reporting, variadic WhereIn, numeric string/int equality in Where, and negative Splice deleteCount.
type Product struct{ Price int }
products := collect.Of([]Product{{Price: 100}})

products.Where("Price", "=", "100").Count() // now matches (1)

collect.New(1, 2).Zip(collect.New(3))       // now [[1, 3]]
collect.New(3, 3, 3).Duplicates().All()     // now [3]
Original prompt

This section details on the original issue you should resolve

<issue_title>Collection Library: Address Review Comments and Improvements from PR #1134</issue_title>
<issue_description>## Overview

This issue tracks improvements and fixes needed for the Collection library that was merged in #1134. While the implementation is solid and provides comprehensive Laravel-style collection functionality, several issues were identified during review that should be addressed in follow-up PRs.


Critical Issues

1. Goroutine Leaks in LazyCollection

Files: support/collect/lazy_collection.go

Multiple methods in LazyCollection may leak goroutines by not draining channels after early returns/breaks:

  • Zip (line 853-859): May leave ch2 goroutine running if ch1 completes first
  • Take (line 669-676): Breaks early without draining input channel
  • TakeWhile (line 694-700): Similar issue to Take
  • Some (line 617-621): Returns early without consuming remaining items
  • First (line 248-251): Returns early without draining channel
  • FirstWhere (line 261-268): Same issue
  • Every (line 210-218): Same issue

Fix: Drain channels after early returns to prevent blocking upstream goroutines:

// Example fix for Take
for item := range input {
    if taken >= n {
        // Drain remaining items to prevent blocking upstream
        go func() { for range input {} }()
        break
    }
    output <- item
    taken++
}

2. Performance Issues

Random() Generator Initialization (line 800)

  • Initializes a new random generator on every call
  • Fix: Use a global generator and reuse it

Unique() and Key Generation Performance (line 1058, 265, 735)

  • Uses fmt.Sprintf("%v", ...) for key generation which is extremely slow for large structs
  • Example: For a User struct with large fields, this allocates massive strings and uses reflection
  • Affects: Unique, Duplicates, Diff, Intersect, GroupBy, and all methods using seen maps
  • Fix: Accept a keyFunc parameter allowing users to provide efficient key extraction

Behavioral Issues

3. Zip Inconsistency (line 1239)

  • Collection.Zip: includes partial pairs when lengths differ → [[1], [2, 3]] for [1,2] and [3]
  • LazyCollection.Zip: stops at shorter collection → [] for same inputs
  • Fix: Align both to Laravel style (stop at shorter collection)

4. Duplicates() Unexpected Behavior (line 272)

  • Input: [3, 3, 3]
  • Current output: [3, 3]
  • Expected: [3] (each duplicate value once)
  • Fix: Track which values have already been added to duplicates

5. Where Type Comparison Issues (line 1270)

type Product struct{ Price int }
c := Of([]Product{{Price: 100}})

c.Where("Price", ">", "50").Count()   // Returns 1 (works)
c.Where("Price", "=", "100").Count()  // Returns 0 (broken!)
  • Comparison operators behave inconsistently with type mismatches
  • Fix: Add proper type conversion or stricter type checking

API Design Issues

6. Pointer Returns (lines 858, 871, 961)

  • Many methods return *T but it's unclear why
  • Users can simply use pointer type for T if needed: Collection[*User]
  • Affected: First, Last, Get, Random, etc.
  • Consider: Change to return (T, bool) pattern or just T with zero value

7. WhereIn/WhereNotIn Usability (line 1172)

  • Requires []any conversion which is cumbersome:
    c.WhereIn("ID", []any{1, 2, 3}) // Need to convert
  • Other Where methods accept ...any which is easier:
    c.Where("ID", "=", 1) // No conversion needed
  • Fix: Change signature to accept ...any instead of []any

8. Index Parameter Position (line 806)

  • Currently inconsistent: some methods pass (item, index), others (index, item)
  • Fix: Standardize to (item, index) everywhere for consistency with Go idioms

9. Mutability Inconsistency (line 1052)

  • Some methods return new collections, others modify in place
  • Transform modifies original, others don't
  • Fix: Document which methods are mutating, consider making all immutable for safety

Edge Cases & Bugs

10. Splice Negative deleteCount (line 968)

  • Doesn't validate negative deleteCount
  • Results in element duplication instead of error
  • Fix: Add validation: if deleteCount < 0 { deleteCount = 0 }

11. Slice Duplicate Check (line 896)

  • Has duplicate check for start < 0
  • Fix: Remove redundant check

12. Variable Naming Collision (line 613)

  • Variable named max/min collides with Go built-in functions
  • Fix: Rename to maxVal/minVal

13. Debug/Dump Missing Implementation (line 955)

  • Methods exist but do nothing meaningful
  • Fix: Implement proper debug output or remove if not needed

Additional Issues Identified

14. Thread Safety

  • Collections are not thread-safe but this isn't documented
  • Concurrent modificati...

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits March 11, 2026 08:33
Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com>
Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix goroutine leaks in LazyCollection methods collect: fix lazy channel draining leaks and align collection edge-case behavior Mar 11, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 11, 2026

Codecov Report

❌ Patch coverage is 93.72385% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.27%. Comparing base (5457bef) to head (08402e7).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
support/collect/collection.go 85.43% 7 Missing and 8 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1409      +/-   ##
==========================================
+ Coverage   66.42%   67.27%   +0.85%     
==========================================
  Files         344      344              
  Lines       25638    25574      -64     
==========================================
+ Hits        17030    17206     +176     
+ Misses       7872     7639     -233     
+ Partials      736      729       -7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hwbrzzl hwbrzzl marked this pull request as ready for review March 12, 2026 03:26
@hwbrzzl hwbrzzl requested a review from a team as a code owner March 12, 2026 03:26
@hwbrzzl
Copy link
Copy Markdown
Contributor

hwbrzzl commented Mar 12, 2026

@copilot CI failed

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 12, 2026

@copilot CI failed

Investigated CI for this branch at commit c57f84d. There are two failures: (1) Check PR Title is failing because the PR title seen by the workflow is still "[WIP] Fix goroutine leaks in LazyCollection methods" (doesn’t match Conventional Commits), and (2) Test PR / windows (1.26) failed in process package (TestRunningPool_BasicFunctions_Windows/PIDs_returns_process_IDs, expected PID != 0), which appears unrelated to the collection changes. No code change in this PR is needed for the collection files; please update the PR title and rerun checks.

@hwbrzzl hwbrzzl merged commit e44c483 into master Mar 12, 2026
18 of 21 checks passed
@hwbrzzl hwbrzzl deleted the copilot/address-review-comments-collection-library branch March 12, 2026 13:41
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.

Collection Library: Address Review Comments and Improvements from PR #1134

2 participants