Skip to content

Conversation

@JustinDFuller
Copy link
Owner

Note

This PR was generated with Claude Code (claude.ai/code)

Summary

  • Fixes the issue where slow OnStateChange callbacks could block the ticker and all nozzle operations
  • Callbacks now execute asynchronously in separate goroutines
  • Adds proper context cancellation support

Problem

The OnStateChange callback was executed synchronously while holding the mutex lock. This meant:

  1. A slow callback would delay the next ticker iteration
  2. All DoBool/DoError operations would be blocked during callback execution
  3. The ticker couldn't maintain its configured interval

Solution

  • Execute callbacks asynchronously using goroutines
  • Add context.Context parameter for proper cancellation signaling
  • Add Timestamp field to StateSnapshot to record when changes actually occurred
  • Implement panic recovery to prevent callback crashes

Breaking Changes

The callback signature has changed from:

OnStateChange func(StateSnapshot)

to:

OnStateChange func(context.Context, StateSnapshot)

Testing

Added comprehensive tests to verify:

  • Slow callbacks don't block the ticker
  • Panics in callbacks are recovered gracefully
  • Context is properly cancelled on Close()
  • Timestamps accurately reflect state change times
  • All existing tests continue to pass

Test Results

PASS
ok  	github.com/justindfuller/nozzle	77.289s

🤖 Generated with Claude Code

JustinDFuller and others added 5 commits September 12, 2025 21:46
The OnStateChange callback was executed synchronously while holding
the mutex, which could delay the ticker and block all nozzle
operations if the callback was slow.

Changes:
- Execute callbacks asynchronously in separate goroutines
- Add context.Context parameter for cancellation support
- Add Timestamp field to StateSnapshot to record when changes occur
- Add panic recovery to prevent callback panics from crashing
- Update all tests and examples for new callback signature
- Add comprehensive tests for async behavior

This is a breaking change to the callback signature but fixes the
blocking issue completely.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This is a v0 library, so breaking changes are expected.
Removed explicit breaking change notices from documentation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use ctx.Err() != nil instead of select statement for one-off
context cancellation checks. This is simpler and more idiomatic.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Use underscore for unused parameters in callbacks
- Rename short variable names (mu -> mutex)
- Add proper error handling for Close() calls
- Remove unused Timestamp.IsZero() check

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Rename short variable 'n' to 'noz' throughout test files
- Add required whitespace before defer statements per wsl linter
- Fix all golangci-lint warnings

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@JustinDFuller JustinDFuller marked this pull request as ready for review September 13, 2025 12:53
@JustinDFuller JustinDFuller merged commit cf70f01 into main Sep 13, 2025
4 checks passed
@JustinDFuller JustinDFuller deleted the fix/onstatechange-blocking branch September 13, 2025 12:53
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.

2 participants