Documentation
¶
Overview ¶
Package shrinkmap provides a thread-safe, generic map implementation with automatic memory management.
ShrinkableMap automatically shrinks its internal storage when items are deleted, addressing the common issue where Go's built-in maps don't release memory after deleting elements.
Key features:
- Thread-safe operations with minimal locking overhead
- Automatic memory shrinking with configurable policies
- Generic type support for compile-time type safety
- Comprehensive error handling with structured error types
- Performance metrics and monitoring capabilities
- Batch operations for improved performance
- Safe iteration with snapshot support
Basic usage:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
defer sm.Stop() // Always call Stop() to prevent goroutine leaks
// Basic operations
sm.Set("key1", 42)
if value, exists := sm.Get("key1"); exists {
fmt.Printf("Value: %d\n", value)
}
sm.Delete("key1")
For production use, always call Stop() when the map is no longer needed to prevent goroutine leaks from the automatic shrinking background process.
Index ¶
- Variables
- func IsCapacityExceeded(err error) bool
- func IsMapStopped(err error) bool
- type BatchOpType
- type BatchOperation
- type BatchOperations
- type Builder
- func (b *Builder[K, V]) Build() *ShrinkableMap[K, V]
- func (b *Builder[K, V]) BuildWithValidation() (*ShrinkableMap[K, V], error)
- func (b *Builder[K, V]) WithAutoShrink(enabled bool) *Builder[K, V]
- func (b *Builder[K, V]) WithCapacityGrowthFactor(factor float64) *Builder[K, V]
- func (b *Builder[K, V]) WithInitialCapacity(capacity int) *Builder[K, V]
- func (b *Builder[K, V]) WithMaxMapSize(size int) *Builder[K, V]
- func (b *Builder[K, V]) WithMinShrinkInterval(interval time.Duration) *Builder[K, V]
- func (b *Builder[K, V]) WithShrinkInterval(interval time.Duration) *Builder[K, V]
- func (b *Builder[K, V]) WithShrinkRatio(ratio float64) *Builder[K, V]
- type Config
- func (c Config) Validate() error
- func (c Config) WithAutoShrinkEnabled(enabled bool) Config
- func (c Config) WithCapacityGrowthFactor(factor float64) Config
- func (c Config) WithInitialCapacity(capacity int) Config
- func (c Config) WithMaxMapSize(size int) Config
- func (c Config) WithMinShrinkInterval(d time.Duration) Config
- func (c Config) WithShrinkInterval(d time.Duration) Config
- func (c Config) WithShrinkRatio(ratio float64) Config
- type ErrCode
- type ErrorRecord
- type Iterator
- type KeyValue
- type MapBuilder
- func (mb *MapBuilder[K, V]) Delete(key K) *MapBuilder[K, V]
- func (mb *MapBuilder[K, V]) Done() *ShrinkableMap[K, V]
- func (mb *MapBuilder[K, V]) Map() *ShrinkableMap[K, V]
- func (mb *MapBuilder[K, V]) Set(key K, value V) *MapBuilder[K, V]
- func (mb *MapBuilder[K, V]) SetIfAbsent(key K, value V) *MapBuilder[K, V]
- type Metrics
- func (m *Metrics) ErrorHistory() []ErrorRecord
- func (m *Metrics) LastError() *ErrorRecord
- func (m *Metrics) LastPanicTime() time.Time
- func (m *Metrics) LastShrinkDuration() time.Duration
- func (m *Metrics) PeakSize() int32
- func (m *Metrics) RecordError(err error, stack string)
- func (m *Metrics) RecordPanic(r interface{}, stack string)
- func (m *Metrics) Reset()
- func (m *Metrics) TotalErrors() int64
- func (m *Metrics) TotalItemsProcessed() int64
- func (m *Metrics) TotalPanics() int64
- func (m *Metrics) TotalShrinks() int64
- type ShrinkMapError
- type ShrinkableMap
- func (sm *ShrinkableMap[K, V]) ApplyBatch(batch BatchOperations[K, V]) error
- func (sm *ShrinkableMap[K, V]) Contains(key K) bool
- func (sm *ShrinkableMap[K, V]) Delete(key K) bool
- func (sm *ShrinkableMap[K, V]) ForceShrink() bool
- func (sm *ShrinkableMap[K, V]) Get(key K) (V, bool)
- func (sm *ShrinkableMap[K, V]) GetMetrics() Metrics
- func (sm *ShrinkableMap[K, V]) GetOrSet(key K, value V) (V, bool, error)
- func (sm *ShrinkableMap[K, V]) Len() int64
- func (sm *ShrinkableMap[K, V]) NewIterator() *Iterator[K, V]
- func (sm *ShrinkableMap[K, V]) Set(key K, value V) error
- func (sm *ShrinkableMap[K, V]) SetIf(key K, value V, condition func(oldValue V, exists bool) bool) (bool, error)
- func (sm *ShrinkableMap[K, V]) SetIfAbsent(key K, value V) (bool, error)
- func (sm *ShrinkableMap[K, V]) Snapshot() []KeyValue[K, V]
- func (sm *ShrinkableMap[K, V]) Stop()
- func (sm *ShrinkableMap[K, V]) TryShrink() bool
Constants ¶
This section is empty.
Variables ¶
var ( ErrMapStopped = NewShrinkMapError(ErrCodeMapStopped, "operation", "map has been stopped") ErrCapacityExceeded = NewShrinkMapError(ErrCodeCapacityExceeded, "set", "maximum capacity exceeded") ErrInvalidConfig = NewShrinkMapError(ErrCodeInvalidConfig, "config", "invalid configuration") ErrShrinkFailed = NewShrinkMapError(ErrCodeShrinkFailed, "shrink", "shrink operation failed") ErrBatchFailed = NewShrinkMapError(ErrCodeBatchFailed, "batch", "batch operation failed") )
Common error instances
Functions ¶
func IsCapacityExceeded ¶ added in v0.0.5
IsCapacityExceeded checks if the error indicates capacity exceeded
func IsMapStopped ¶ added in v0.0.5
IsMapStopped checks if the error indicates a stopped map
Types ¶
type BatchOpType ¶ added in v0.0.4
type BatchOpType int
const ( BatchSet BatchOpType = iota BatchDelete )
type BatchOperation ¶ added in v0.0.4
type BatchOperation[K comparable, V any] struct { Type BatchOpType Key K Value V }
type BatchOperations ¶ added in v0.0.4
type BatchOperations[K comparable, V any] struct { Operations []BatchOperation[K, V] }
BatchOperations provides batch operation capabilities
type Builder ¶ added in v0.0.5
type Builder[K comparable, V any] struct { // contains filtered or unexported fields }
Builder provides a fluent interface for creating and configuring ShrinkableMap instances. It allows method chaining for a more readable and convenient API.
func NewBuilder ¶ added in v0.0.5
func NewBuilder[K comparable, V any]() *Builder[K, V]
NewBuilder creates a new Builder with default configuration. This is the starting point for creating a ShrinkableMap with fluent syntax.
Example:
sm := shrinkmap.NewBuilder[string, int](). WithShrinkRatio(0.5). WithInitialCapacity(100). Build() defer sm.Stop()
func (*Builder[K, V]) Build ¶ added in v0.0.5
func (b *Builder[K, V]) Build() *ShrinkableMap[K, V]
Build creates the ShrinkableMap with the configured settings. This is the final method in the builder chain that returns the actual map. The returned map is ready for use and will start background shrinking if enabled.
Important: Always call Stop() on the returned map when done to prevent goroutine leaks.
func (*Builder[K, V]) BuildWithValidation ¶ added in v0.0.5
func (b *Builder[K, V]) BuildWithValidation() (*ShrinkableMap[K, V], error)
BuildWithValidation creates the ShrinkableMap with validation of the configuration. Returns an error if the configuration is invalid.
func (*Builder[K, V]) WithAutoShrink ¶ added in v0.0.5
WithAutoShrink enables or disables automatic shrinking and returns the builder for chaining.
func (*Builder[K, V]) WithCapacityGrowthFactor ¶ added in v0.0.5
WithCapacityGrowthFactor sets the capacity growth factor and returns the builder for chaining. This controls how much extra space is allocated when shrinking.
func (*Builder[K, V]) WithInitialCapacity ¶ added in v0.0.5
WithInitialCapacity sets the initial capacity and returns the builder for chaining. This is the initial capacity of the underlying map.
func (*Builder[K, V]) WithMaxMapSize ¶ added in v0.0.5
WithMaxMapSize sets the maximum map size and returns the builder for chaining. Set to 0 for unlimited size.
func (*Builder[K, V]) WithMinShrinkInterval ¶ added in v0.0.5
WithMinShrinkInterval sets the minimum shrink interval and returns the builder for chaining. This prevents shrinking too frequently even if conditions are met.
func (*Builder[K, V]) WithShrinkInterval ¶ added in v0.0.5
WithShrinkInterval sets the shrink interval and returns the builder for chaining. This controls how often the map checks for shrinking opportunities.
func (*Builder[K, V]) WithShrinkRatio ¶ added in v0.0.5
WithShrinkRatio sets the shrink ratio and returns the builder for chaining. The shrink ratio determines what percentage of deleted items triggers shrinking.
type Config ¶
type Config struct {
// Duration values (8 bytes each)
ShrinkInterval time.Duration // How often to check if the map needs shrinking
MinShrinkInterval time.Duration // Minimum time between shrinks
// Float64 values (8 bytes each)
ShrinkRatio float64 // Ratio of deleted items that triggers shrinking (0.0 to 1.0)
CapacityGrowthFactor float64 // Extra capacity factor when creating new map (e.g., 1.2 for 20% extra space)
// Int values (8 bytes on 64-bit)
InitialCapacity int // Initial capacity of the map
MaxMapSize int // Maximum map size before forcing a shrink
// Bool values (1 byte each)
AutoShrinkEnabled bool // Enable/disable automatic shrinking
}
Config defines the configuration options for ShrinkableMap
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the default configuration for ShrinkableMap. These settings are optimized for general use cases with reasonable performance and memory management characteristics.
Default values:
- ShrinkInterval: 5 minutes (how often to check for shrinking)
- ShrinkRatio: 0.25 (shrink when 25% of items are deleted)
- InitialCapacity: 16 (starting capacity)
- AutoShrinkEnabled: true (automatic shrinking is enabled)
- MinShrinkInterval: 30 seconds (minimum time between shrinks)
- MaxMapSize: 1,000,000 (maximum items before forced shrink)
- CapacityGrowthFactor: 1.2 (20% extra space when shrinking)
Example:
config := shrinkmap.DefaultConfig() config.ShrinkRatio = 0.5 // Customize shrink ratio sm := shrinkmap.New[string, int](config)
func (Config) Validate ¶
Validate checks if the configuration is valid and returns an error if not. This method should be called before creating a new ShrinkableMap to ensure the configuration parameters are within acceptable ranges.
Validation rules:
- ShrinkInterval must be positive
- ShrinkRatio must be between 0 and 1 (exclusive)
- InitialCapacity must be non-negative
- MinShrinkInterval must be positive
- MaxMapSize must be non-negative (0 means unlimited)
- CapacityGrowthFactor must be greater than 1
Example:
config := shrinkmap.DefaultConfig()
config.ShrinkRatio = 0.5
if err := config.Validate(); err != nil {
log.Fatal("Invalid configuration:", err)
}
func (Config) WithAutoShrinkEnabled ¶
WithAutoShrinkEnabled sets auto shrinking and returns the modified config
func (Config) WithCapacityGrowthFactor ¶
WithCapacityGrowthFactor sets the capacity growth factor and returns the modified config
func (Config) WithInitialCapacity ¶
WithInitialCapacity sets the initial capacity and returns the modified config
func (Config) WithMaxMapSize ¶
WithMaxMapSize sets the maximum map size and returns the modified config
func (Config) WithMinShrinkInterval ¶
WithMinShrinkInterval sets the minimum shrink interval and returns the modified config
func (Config) WithShrinkInterval ¶
WithShrinkInterval sets the shrink interval and returns the modified config
func (Config) WithShrinkRatio ¶
WithShrinkRatio sets the shrink ratio and returns the modified config
type ErrCode ¶ added in v0.0.5
type ErrCode int
ErrCode represents different types of errors that can occur in ShrinkableMap
const ( // ErrCodeInvalidConfig indicates configuration validation failed ErrCodeInvalidConfig ErrCode = iota // ErrCodeMapStopped indicates operation attempted on stopped map ErrCodeMapStopped // ErrCodeShrinkFailed indicates shrink operation failed ErrCodeShrinkFailed // ErrCodeBatchFailed indicates batch operation failed ErrCodeBatchFailed // ErrCodeCapacityExceeded indicates maximum capacity exceeded ErrCodeCapacityExceeded )
type ErrorRecord ¶ added in v0.0.2
type ErrorRecord struct {
Timestamp time.Time
Error interface{} // panic 값이나 error 둘 다 저장 가능
Stack string // 스택 트레이스 저장
}
ErrorRecord represents a single error or panic occurrence
type Iterator ¶ added in v0.0.4
type Iterator[K comparable, V any] struct { // contains filtered or unexported fields }
Iterator provides a safe way to iterate over map entries. The iterator works with a snapshot of the map taken at creation time, making it safe to use even if the map is modified during iteration.
type KeyValue ¶ added in v0.0.2
type KeyValue[K comparable, V any] struct { Key K Value V }
KeyValue represents a key-value pair for iteration purposes
type MapBuilder ¶ added in v0.0.5
type MapBuilder[K comparable, V any] struct { // contains filtered or unexported fields }
MapBuilder provides additional fluent methods for map operations after creation.
func NewMapBuilder ¶ added in v0.0.5
func NewMapBuilder[K comparable, V any](sm *ShrinkableMap[K, V]) *MapBuilder[K, V]
NewMapBuilder creates a MapBuilder from an existing ShrinkableMap. This allows for fluent operations on the map.
func (*MapBuilder[K, V]) Delete ¶ added in v0.0.5
func (mb *MapBuilder[K, V]) Delete(key K) *MapBuilder[K, V]
Delete removes a key and returns the builder for chaining.
func (*MapBuilder[K, V]) Done ¶ added in v0.0.5
func (mb *MapBuilder[K, V]) Done() *ShrinkableMap[K, V]
Done is a convenience method that returns the underlying map. This is useful at the end of a builder chain.
func (*MapBuilder[K, V]) Map ¶ added in v0.0.5
func (mb *MapBuilder[K, V]) Map() *ShrinkableMap[K, V]
Map returns the underlying ShrinkableMap.
func (*MapBuilder[K, V]) Set ¶ added in v0.0.5
func (mb *MapBuilder[K, V]) Set(key K, value V) *MapBuilder[K, V]
Set stores a key-value pair and returns the builder for chaining. If an error occurs, it can be retrieved using the LastError method.
func (*MapBuilder[K, V]) SetIfAbsent ¶ added in v0.0.5
func (mb *MapBuilder[K, V]) SetIfAbsent(key K, value V) *MapBuilder[K, V]
SetIfAbsent sets a key-value pair only if the key doesn't exist and returns the builder for chaining.
type Metrics ¶
type Metrics struct {
// contains filtered or unexported fields
}
Metrics tracks performance and error metrics of the map
func (*Metrics) ErrorHistory ¶ added in v0.0.2
func (m *Metrics) ErrorHistory() []ErrorRecord
func (*Metrics) LastError ¶ added in v0.0.2
func (m *Metrics) LastError() *ErrorRecord
func (*Metrics) LastPanicTime ¶ added in v0.0.2
func (*Metrics) LastShrinkDuration ¶
func (*Metrics) RecordError ¶ added in v0.0.2
func (*Metrics) RecordPanic ¶ added in v0.0.2
func (*Metrics) TotalErrors ¶ added in v0.0.2
func (*Metrics) TotalItemsProcessed ¶
func (*Metrics) TotalPanics ¶ added in v0.0.2
func (*Metrics) TotalShrinks ¶
type ShrinkMapError ¶ added in v0.0.5
type ShrinkMapError struct {
Code ErrCode
Message string
Operation string
Timestamp time.Time
Details map[string]interface{}
}
ShrinkMapError represents a structured error from ShrinkableMap operations
func NewShrinkMapError ¶ added in v0.0.5
func NewShrinkMapError(code ErrCode, operation, message string) *ShrinkMapError
NewShrinkMapError creates a new ShrinkMapError
func (*ShrinkMapError) Error ¶ added in v0.0.5
func (e *ShrinkMapError) Error() string
Error implements the error interface
func (*ShrinkMapError) Is ¶ added in v0.0.5
func (e *ShrinkMapError) Is(target error) bool
Is allows error comparison using errors.Is
func (*ShrinkMapError) WithDetails ¶ added in v0.0.5
func (e *ShrinkMapError) WithDetails(key string, value interface{}) *ShrinkMapError
WithDetails adds details to the error
type ShrinkableMap ¶
type ShrinkableMap[K comparable, V any] struct { // contains filtered or unexported fields }
ShrinkableMap provides a generic map structure with automatic shrinking capabilities Note: Each ShrinkableMap instance creates its own goroutine for auto-shrinking when AutoShrinkEnabled is true. The goroutine will continue to run until Stop() is called, even if there are no more references to the map. For transient use cases, ensure to call Stop() when the map is no longer needed to prevent goroutine leaks.
func New ¶
func New[K comparable, V any](config Config) *ShrinkableMap[K, V]
New creates a new ShrinkableMap with the given configuration. The returned map is ready for concurrent use and will automatically start background shrinking if AutoShrinkEnabled is true in the config.
Important: Always call Stop() when the map is no longer needed to prevent goroutine leaks from the automatic shrinking process.
Example:
config := shrinkmap.DefaultConfig() config.ShrinkRatio = 0.5 // Shrink when 50% of items are deleted sm := shrinkmap.New[string, int](config) defer sm.Stop()
func (*ShrinkableMap[K, V]) ApplyBatch ¶ added in v0.0.4
func (sm *ShrinkableMap[K, V]) ApplyBatch(batch BatchOperations[K, V]) error
ApplyBatch applies multiple operations atomically. This method is more efficient than individual operations when processing multiple items as it acquires the lock only once. Returns an error if the map is stopped or capacity is exceeded.
Example:
batch := shrinkmap.BatchOperations[string, int]{
Operations: []shrinkmap.BatchOperation[string, int]{
{Type: shrinkmap.BatchSet, Key: "key1", Value: 1},
{Type: shrinkmap.BatchSet, Key: "key2", Value: 2},
{Type: shrinkmap.BatchDelete, Key: "key3"},
},
}
if err := sm.ApplyBatch(batch); err != nil {
log.Printf("Batch operation failed: %v", err)
}
func (*ShrinkableMap[K, V]) Contains ¶ added in v0.0.5
func (sm *ShrinkableMap[K, V]) Contains(key K) bool
Contains checks if the key exists in the map. This is a convenience method that returns only the existence boolean. For better performance when you also need the value, use Get instead.
Example:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
sm.Set("key", 42)
if sm.Contains("key") {
fmt.Println("Key exists")
}
func (*ShrinkableMap[K, V]) Delete ¶
func (sm *ShrinkableMap[K, V]) Delete(key K) bool
Delete removes the entry for the given key. Returns true if the key existed and was deleted, false otherwise. This operation is safe for concurrent use and may trigger automatic shrinking.
Example:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
sm.Set("key", 42)
if deleted := sm.Delete("key"); deleted {
fmt.Println("Key was deleted")
}
func (*ShrinkableMap[K, V]) ForceShrink ¶
func (sm *ShrinkableMap[K, V]) ForceShrink() bool
ForceShrink immediately shrinks the map regardless of conditions
func (*ShrinkableMap[K, V]) Get ¶
func (sm *ShrinkableMap[K, V]) Get(key K) (V, bool)
Get retrieves the value associated with the given key. Returns the value and a boolean indicating whether the key exists. This operation is safe for concurrent use.
Example:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
sm.Set("key", 42)
if value, exists := sm.Get("key"); exists {
fmt.Printf("Value: %d\n", value)
}
func (*ShrinkableMap[K, V]) GetMetrics ¶
func (sm *ShrinkableMap[K, V]) GetMetrics() Metrics
GetMetrics returns a copy of the current metrics
func (*ShrinkableMap[K, V]) GetOrSet ¶ added in v0.0.5
func (sm *ShrinkableMap[K, V]) GetOrSet(key K, value V) (V, bool, error)
GetOrSet returns the existing value for the key, or sets and returns the provided value if key doesn't exist
func (*ShrinkableMap[K, V]) Len ¶
func (sm *ShrinkableMap[K, V]) Len() int64
Len returns the current number of items in the map. This operation is atomic and safe for concurrent use. The returned value reflects the actual number of accessible items, accounting for deletions that haven't been cleaned up yet.
Example:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
sm.Set("key1", 1)
sm.Set("key2", 2)
fmt.Printf("Map contains %d items\n", sm.Len()) // Output: Map contains 2 items
func (*ShrinkableMap[K, V]) NewIterator ¶ added in v0.0.4
func (sm *ShrinkableMap[K, V]) NewIterator() *Iterator[K, V]
NewIterator creates a new iterator for the map. The iterator takes a snapshot of the current map state, making it safe to use even if the map is modified during iteration. The snapshot is taken with a read lock for consistency.
Example:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
sm.Set("key1", 1)
sm.Set("key2", 2)
iter := sm.NewIterator()
for iter.Next() {
key, value := iter.Get()
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
func (*ShrinkableMap[K, V]) Set ¶
func (sm *ShrinkableMap[K, V]) Set(key K, value V) error
Set stores a key-value pair in the map
func (*ShrinkableMap[K, V]) SetIf ¶ added in v0.0.5
func (sm *ShrinkableMap[K, V]) SetIf(key K, value V, condition func(oldValue V, exists bool) bool) (bool, error)
SetIf sets the value for the key only if the condition is true
func (*ShrinkableMap[K, V]) SetIfAbsent ¶ added in v0.0.5
func (sm *ShrinkableMap[K, V]) SetIfAbsent(key K, value V) (bool, error)
SetIfAbsent sets the value for the key only if the key doesn't exist
func (*ShrinkableMap[K, V]) Snapshot ¶ added in v0.0.2
func (sm *ShrinkableMap[K, V]) Snapshot() []KeyValue[K, V]
Snapshot returns a slice of key-value pairs representing the current state of the map Note: This operation requires a full lock of the map and may be expensive for large maps
func (*ShrinkableMap[K, V]) Stop ¶ added in v0.0.2
func (sm *ShrinkableMap[K, V]) Stop()
Stop terminates the auto-shrink goroutine if it's running. This should be called when the map is no longer needed to prevent goroutine leaks. After calling Stop(), the map remains functional but automatic shrinking is disabled. It's safe to call Stop() multiple times.
Example:
sm := shrinkmap.New[string, int](shrinkmap.DefaultConfig())
defer sm.Stop() // Ensure cleanup
// Use the map...
sm.Set("key", 42)
func (*ShrinkableMap[K, V]) TryShrink ¶
func (sm *ShrinkableMap[K, V]) TryShrink() bool
TryShrink attempts to shrink the map if conditions are met