Documentation
¶
Overview ¶
Package reuse provides a generic resource reuse mechanism with automatic lifecycle management. It manages the lifecycle of a shared resource with automatic cleanup and reuse, providing thread-safe access to resources that can be used by multiple concurrent users.
The package provides:
- Reuse type for managing resource lifecycle
- Automatic resource creation and cleanup
- Thread-safe concurrent access
- Flexible configuration options
- Support for different cleanup strategies
Example Usage:
reuse := reuse.Run(
func() (*MyResource, error) {
return &MyResource{}, nil
},
reuse.WithCleanupTimeout(10*time.Second),
)
entered, err := reuse.Enter(ctx)
if err != nil {
return err
}
defer entered.Exit()
resource := entered.Value()
// Use resource...
Index ¶
- Constants
- Variables
- type CleanupFunc
- type Cleanupper
- type Config
- type CreateFunc
- type Entered
- type Option
- func WithCleanupFunc[T any](cleanupFunc CleanupFunc[T]) Option[T]
- func WithCleanupTimeout[T any](cleanupTimeout time.Duration) Option[T]
- func WithLogger[T any](logger *log.Logger) Option[T]
- func WithManualCleanup[T any](manualCleanup bool) Option[T]
- func WithWaitUntilCleanup[T any](waitDuration time.Duration) Option[T]
- func WithWeight[T any](weight int) Option[T]
- type Reuse
Constants ¶
const ( // WeightUnlimited indicates no limit on concurrent users in the reuse instance. // Use this value with WithWeight to disable concurrency limiting. WeightUnlimited = -1 )
Variables ¶
var ErrReuseIsClosed = errors.New("reuse instance is closed")
ErrReuseIsClosed is returned when trying to enter a reuse instance that has been closed.
var ErrShutdown = errors.New("reuse in shutdown phase, any enter is blocked")
ErrShutdown is returned when trying to enter a reuse instance that is in shutdown phase. This error indicates that the instance is no longer accepting new users.
Functions ¶
This section is empty.
Types ¶
type CleanupFunc ¶
CleanupFunc is a function that performs custom cleanup on a resource. It is called before the built-in cleanup logic (Close/Terminate methods). This allows for additional cleanup operations beyond the standard Close/Terminate.
Parameters:
- ctx: Context for the cleanup operation, can be used for cancellation
- value: The resource to clean up
If an error is returned, it will be logged but the built-in cleanup will still proceed.
type Cleanupper ¶
type Cleanupper interface {
// Cleanup registers a function to be called when the test completes.
// This is used to ensure resources are properly cleaned up after tests.
Cleanup(doCleanup func())
}
Cleanupper is an interface for registering cleanup functions. This is typically implemented by *testing.T to integrate with test cleanup.
type Config ¶
type Config[T any] struct { // contains filtered or unexported fields }
Config holds the configuration options for a reuse instance. These options control behavior such as cleanup timing, concurrency limits, logging, and resource creation.
type CreateFunc ¶
CreateFunc is a function type that creates a new instance of type T. It's used by the reuse instance to create resources on-demand when the first user enters and no resource exists yet.
The function should return a new resource instance and any error that occurred during creation. If an error is returned, the Enter operation will fail.
Example:
createFunc := func() (*MyResource, error) {
return &MyResource{ID: uuid.New()}, nil
}
type Entered ¶
type Entered[T any] struct { // contains filtered or unexported fields }
Entered represents a successful entry into a reuse instance. It provides access to the resource and a way to exit when done.
Entered instances should be used with defer to ensure cleanup:
entered, err := reuse.Enter(ctx)
if err != nil {
return err
}
defer entered.Exit()
resource := entered.Value()
// Use resource...
func (Entered[T]) Exit ¶
func (e Entered[T]) Exit()
Exit releases the resource back to the reuse instance. This decrements the user count and may trigger cleanup if this was the last user.
Exit should be called when the caller is done using the resource. It's safe to call Exit multiple times, but only the first call has an effect.
type Option ¶
Option is a function that configures a reuse instance. Options can be passed to Run to customize the instance's behavior.
func WithCleanupFunc ¶
func WithCleanupFunc[T any](cleanupFunc CleanupFunc[T]) Option[T]
WithCleanupFunc sets a custom cleanup function that is called before the built-in cleanup logic (Close/Terminate methods).
Default: nil (no custom cleanup function)
The custom cleanup function is called first during cleanup, followed by the built-in cleanup. This allows you to perform additional cleanup operations such as releasing external resources, notifying other systems, or logging.
If the cleanup function returns an error, it will be logged but cleanup will continue with the built-in Close/Terminate method.
Example:
reuse := Run(createFunc,
WithCleanupFunc(func(ctx context.Context, r *MyResource) error {
// Custom cleanup logic here
r.ReleaseExternalResources()
return nil
}),
)
func WithCleanupTimeout ¶
WithCleanupTimeout sets the maximum duration to wait for cleanup operations.
Default: 5 * time.Second
Example:
reuse := Run(createFunc, WithCleanupTimeout(10*time.Second))
func WithLogger ¶
WithLogger sets a custom logger for logging phase changes and commands.
Default: logger that writes to io.Discard (no logging)
Example:
logger := log.New(os.Stdout, "reuse: ", log.LstdFlags) reuse := Run(createFunc, WithLogger(logger))
func WithManualCleanup ¶
WithManualCleanup determines whether cleanup is automatic or manual.
Default: false (automatic cleanup)
When false: Cleanup happens automatically after waitUntilCleanup duration. When true: Cleanup must be triggered explicitly by calling Cleanup().
Example:
// Manual cleanup mode reuse := Run(createFunc, WithManualCleanup(true)) // Later, trigger cleanup explicitly reuse.Shutdown() or reuse.Kill()
func WithWaitUntilCleanup ¶
WithWaitUntilCleanup sets how long to wait after the last user exits before cleanup. This grace period allows new users to reuse the resource before it's cleaned up.
Default: 1 * time.Second
Example:
reuse := Run(createFunc, WithWaitUntilCleanup(30*time.Second))
func WithWeight ¶
WithWeight limits the number of concurrent users. Use WeightUnlimited (-1) for no limit.
Default: WeightUnlimited (no limit)
When weight > 0, it acts as a semaphore. Enter will block if the limit is reached.
Example:
// Allow up to 10 concurrent users reuse := Run(createFunc, WithWeight(10))
type Reuse ¶
type Reuse[T any] struct { // contains filtered or unexported fields }
Reuse manages the lifecycle of a shared resource with automatic cleanup and reuse. It provides thread-safe access to resources that can be used by multiple concurrent users.
Reuse implements a phase-based state machine to manage resource lifecycle:
- Initial: Resource not yet created
- Active: Resource created and being used
- WaitUntilCleanup: All users exited, waiting for grace period
- Shutdown: Instance is shutting down
func Run ¶
func Run[T any]( createFunc CreateFunc[T], opts ...Option[T], ) *Reuse[T]
Run T must be safe to use by many goroutines Run creates and starts a new reuse instance with the given configuration. This is the primary constructor for creating reuse instances.
Parameters:
- createFunc: Factory function that creates new resource instances
- opts: Optional configuration options
Returns:
- *Reuse[T]: A new reuse instance ready for use
Example:
reuse := Run(
func() (*MyResource, error) {
return &MyResource{}, nil
},
WithCleanupTimeout(10*time.Second),
WithWeight(5), // Limit to 5 concurrent users
)
func RunForTesting ¶
func RunForTesting[T any]( cleanupper Cleanupper, createFunc CreateFunc[T], opts ...Option[T], ) *Reuse[T]
RunForTesting creates a reuse instance for use in tests with automatic cleanup. This is a convenience function that integrates with testing frameworks to ensure resources are properly cleaned up after tests complete.
Parameters:
- cleanupper: Interface for registering cleanup (typically *testing.T)
- createFunc: Factory function that creates new resource instances
- opts: Optional configuration options
Returns:
- *Reuse[T]: A new reuse instance with automatic cleanup registered
The function automatically registers the reuse.Shutdown method as a cleanup function, ensuring resources are properly released when the test completes.
Example:
func TestWithReuse(t *testing.T) {
reuse := RunForTesting(t,
func() (*MyResource, error) {
return &MyResource{}, nil
},
WithWeight(5),
)
// reuse will be automatically shut down when test completes
entered, err := reuse.Enter(ctx)
require.NoError(t, err)
defer entered.Exit()
// Use resource in test...
}
func (*Reuse[T]) Enter ¶
Enter attempts to enter the reuse instance and access the resource. If successful, it returns an Entered instance that provides access to the resource.
Enter blocks until:
- The resource is available (if weight limit is set)
- The resource is created (if this is the first user)
- Context is canceled
Parameters:
- ctx: Context for cancellation and timeout
Returns:
- Entered[T]: Provides access to the resource
- error: Non-nil if entry fails (e.g., instance is shutdown)
Example:
entered, err := reuse.Enter(ctx)
if err != nil {
return err
}
defer entered.Exit()
resource := entered.Value()
// Use resource...
func (*Reuse[T]) Kill ¶
func (r *Reuse[T]) Kill()
Kill forcefully terminates the reuse instance. This immediately stops all operations, cleans up resources, and does not wait for existing users to exit.
Kill should only be used in emergency situations or when immediate cleanup is required. For graceful shutdown, use Shutdown instead.
func (*Reuse[T]) Shutdown ¶
func (r *Reuse[T]) Shutdown()
Shutdown gracefully shuts down the reuse instance. This stops accepting new users and waits for existing users to exit before cleanup.
Shutdown is safe to call multiple times - subsequent calls have no effect. This is the preferred way to stop a reuse instance.