Tideland Go Actor provides a lightweight implementation of the Actor model pattern for Go applications. The package ensures thread-safe operations by executing all actions sequentially in a dedicated background goroutine, eliminating the need for explicit locking mechanisms.
- Sequential Execution: All actions run in a dedicated goroutine
- Operation Modes: Both synchronous and asynchronous execution
- Timeout Control: Global and per-action timeouts
- Queue Monitoring: Track queue status and capacity
- Error Handling: Built-in panic recovery
- Zero Dependencies: Pure Go implementation
go get tideland.dev/go/actor
Here's a simple thread-safe counter implementation:
type Counter struct {
value int
act *actor.Actor
}
func NewCounter() (*Counter, error) {
// Create actor with default configuration
cfg := actor.DefaultConfig()
cfg.ActionTimeout = 5 * time.Second // Default timeout for all actions
act, err := actor.Go(cfg)
if err != nil {
return nil, err
}
return &Counter{act: act}, nil
}
// Increment asynchronously with timeout
func (c *Counter) Increment() error {
return c.act.DoAsyncTimeout(time.Second, func() {
c.value++
})
}
// Value returns current count synchronously
func (c *Counter) Value() (int, error) {
var v int
err := c.act.DoSync(func() {
v = c.value
})
return v, err
}
// Stop terminates the actor
func (c *Counter) Stop() {
c.act.Stop()
}
The actor package uses a Config
struct for initialization:
cfg := actor.Config{
Context: ctx, // Controls actor lifetime
QueueCap: 1000, // Action queue capacity
ActionTimeout: 5 * time.Second, // Default timeout for actions
Recoverer: func(r any) error {
log.Printf("Panic: %v", r)
return nil
},
Finalizer: func(err error) error {
if err != nil {
log.Printf("Stopped: %v", err)
}
return err
},
}
act, err := actor.Go(cfg)
Monitor queue status to prevent overload:
status := act.QueueStatus()
fmt.Printf("Queue: %d/%d (full: %v)\n",
status.Length, status.Capacity, status.IsFull)
Three ways to handle timeouts:
// 1. Global timeout in configuration
cfg := actor.DefaultConfig()
cfg.ActionTimeout = 5 * time.Second
// 2. Per-action timeout
err := act.DoSyncTimeout(2*time.Second, func() {
// Operation with 2s timeout
})
// 3. Context timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
err = act.DoSyncWithContext(ctx, func() {
// Operation with context timeout
})
- Keep Actions Small: Design actions to be quick and focused
- Use Timeouts: Set appropriate timeouts to prevent hanging
- Monitor Queue: Check queue status to prevent overload
- Error Handling: Always check returned errors
- Resource Management: Call
Stop()
when done
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the BSD License - see the LICENSE file for details.
- Frank Mueller (https://github.com/themue / https://github.com/tideland / https://themue.dev)