Problem
Currently RemoveExpired() is called on EVERY port-selector run (line 176 in main.go), even when there are plenty of free ports available. This is inefficient and unnecessary.
The TTL mechanism should only be used as a last resort when the port range is exhausted.
Current Behavior
Every time a user runs port-selector:
- All allocations are scanned for expiration
- Expired allocations are removed (wasting cycles)
- Then search for free port proceeds
Example: With 1000 ports and only 100 allocated, we still scan all for TTL on every run.
Desired Behavior
Lazy cleanup approach:
- Try to find a free port in the configured range (normal allocation)
- If all ports are busy/frozen:
- Check for expired allocations (last resort)
- Find the oldest (by
LastUsedAt or AssignedAt)
- Verify it's actually free with
port.IsPortFree()
- Remove and immediately allocate to current directory
- If after cleanup still no free ports: return error
Benefits
- ✅ Faster normal operation (skip TTL scan)
- ✅ Safer cleanup (only remove when necessary, verify port is free)
- ✅ More predictable behavior
- ✅ Prevent race conditions (remove + allocate atomically within
WithStore)
Related Code
main.go:176 - unconditional RemoveExpired() call
main.go:195-220 - port finding logic
allocations.go:439-464 - RemoveExpired() function
internal/port/checker.go - IsPortFree() function
Implementation Strategy
- Move
RemoveExpired() call from line 176 into the port finding loop
- Only call when no free port found in range:
// Find free port (excluding frozen/locked)
freePort, err := port.FindFreePortWithExclusions(...)
if err != nil {
if errors.Is(err, port.ErrAllPortsBusy) {
// Last resort: try cleanup
removed := store.RemoveExpired(ttl)
if removed > 0 {
// Retry finding free port
freePort, err = port.FindFreePortWithExclusions(...)
}
}
if err != nil {
return fmt.Errorf("all ports busy or frozen")
}
}
- Optionally: add
GetOldestAllocation() method to prefer oldest allocations for removal
- Consider: log when cleanup is triggered (warning/info level)
Testing
- Unit test: verify cleanup is NOT called when ports available
- Integration test: verify cleanup IS called when all ports busy
- Integration test: verify removed port can be immediately reallocated
Problem
Currently
RemoveExpired()is called on EVERYport-selectorrun (line 176 in main.go), even when there are plenty of free ports available. This is inefficient and unnecessary.The TTL mechanism should only be used as a last resort when the port range is exhausted.
Current Behavior
Every time a user runs
port-selector:Example: With 1000 ports and only 100 allocated, we still scan all for TTL on every run.
Desired Behavior
Lazy cleanup approach:
LastUsedAtorAssignedAt)port.IsPortFree()Benefits
WithStore)Related Code
main.go:176- unconditionalRemoveExpired()callmain.go:195-220- port finding logicallocations.go:439-464-RemoveExpired()functioninternal/port/checker.go-IsPortFree()functionImplementation Strategy
RemoveExpired()call from line 176 into the port finding loopGetOldestAllocation()method to prefer oldest allocations for removalTesting