Skip to content

A lightweight library for writing concurrent programs in Go using the Actor model.

License

Notifications You must be signed in to change notification settings

grafana/go-actor

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-actor

test lint coverage Go Report Card GoDoc Release

goactor-cover

go-actor is a lightweight library for writing concurrent programs in Go using the Actor model.

Motivation

The goal of go-actor is to bring the actor model closer to Go developers by providing a design pattern that facilitates the development of scalable and high-performance concurrent applications.

Without reusable design principles, maintaining a complex codebase can be challenging. Golang's unique concurrency model, based on goroutines and channels, naturally aligns with the actor model, making it an ideal fit for building actor-based programs.

Advantage

  • Unified Design Principles: Model the entire codebase using the same principles, where each actor is a fundamental building block.
  • Natural Fit with Go: Leverage Go's goroutines and channels, which directly translate to actors and mailboxes.
  • Avoid Mutexes: Design systems without the need for mutexes, reducing the potential for deadlocks and improving performance in complex components.
  • Optimal Scheduling: Enhance performance by optimizing for Go's goroutine scheduler.
  • Easy Transition: Legacy codebases can transition to an actor-based design due to the simple interfaces provided by go-actor, allowing for seamless integration.
  • Zero Overhead: Ensure optimal performance in highly concurrent environments.

Abstractions

The core abstraction layer of go-actor consists of three primary interfaces:

  • actor.Actor: Represents any entity that implements the Start() and Stop() methods. Actors created using the actor.New(actor.Worker) function spawn a dedicated goroutine to execute the supplied actor.Worker.
  • actor.Worker: Encapsulates the executable logic of an actor. This is the primary interface developers need to implement to define an actor's behavior.
  • actor.Mailbox: An interface for message transport mechanisms between actors, created using the actor.NewMailbox(...) function.

Examples

Explore the examples repository to see go-actor in action. Reviewing these examples is highly recommended, as they will greatly enhance your understanding of the library.

// This example will demonstrate how to create actors for producer-consumer use case.
// Producer will create incremented number on every 1 second interval and
// consumer will print whatever number it receives.
func main() {
	mbx := actor.NewMailbox[int]()

	// Produce and consume workers are created with same mailbox
	// so that produce worker can send messages directly to consume worker
	p := actor.New(&producerWorker{mailbox: mbx})
	c1 := actor.New(&consumerWorker{mailbox: mbx, id: 1})

	// Note: Example creates two consumers for the sake of demonstration
	// since having one or more consumers will produce the same result. 
	// Message on stdout will be written by first consumer that reads from mailbox.
	c2 := actor.New(&consumerWorker{mailbox: mbx, id: 2})

	// Combine all actors to singe actor so we can start and stop all at once
	a := actor.Combine(mbx, p, c1, c2).Build()
	a.Start()
	defer a.Stop()
	
	// Stdout output:
	// consumed 1      (worker 1)
	// consumed 2      (worker 2)
	// consumed 3      (worker 1)
	// consumed 4      (worker 2)
	// ...

	select {}
}

// producerWorker will produce incremented number on 1 second interval
type producerWorker struct {
	mailbox actor.MailboxSender[int]
	num  int
}

func (w *producerWorker) DoWork(ctx actor.Context) actor.WorkerStatus {
	select {
	case <-ctx.Done():
		return actor.WorkerEnd

	case <-time.After(time.Second):
		w.num++
		w.mailbox.Send(ctx, w.num)

		return actor.WorkerContinue
	}
}

// consumerWorker will consume numbers received on mailbox
type consumerWorker struct {
	mailbox actor.MailboxReceiver[int]
	id  int
}

func (w *consumerWorker) DoWork(ctx actor.Context) actor.WorkerStatus {
	select {
	case <-ctx.Done():
		return actor.WorkerEnd

	case num := <-w.mailbox.ReceiveC():
		fmt.Printf("consumed %d \t(worker %d)\n", num, w.id)

		return actor.WorkerContinue
	}
}

Add-ons

While go-actor is designed to be a minimal library with lean interfaces, developers can extend its functionality with domain-specific add-ons. Some notable add-ons include:

  • super: An add-on for unifying the testing of actors and workers.
  • commence: An add-on that provides a mechanism for waiting for actor execution to begin.
  • netbox: Mailbox implemented with different network functionality.

Pro Tips

To enhance code quality in projects that heavily rely on the actor model with go-actor, consider adhering to best practices and reviewing common hurdles for frequently encountered issues.

Design Decisions

You can find detailed design decisions here.

Versioning

The go-actor library adopts a versioning scheme structured as x.y.z.

Initially, the library will utilize the format 0.y.z as it undergoes refinement until it attains a level of stability where fundamental interfaces and core principles no longer necessitate significant alterations. Within this semantic, the y component signifies a version that is not backward-compatible. It is advisable for developers to review the release notes carefully to gain insight into these modifications. Furthermore, the final component, z, denotes releases incorporating changes that are backward-compatible.

Contribution

All contributions are useful, whether it is a simple typo, a more complex change, or just pointing out an issue. We welcome any contribution so feel free to open PR or issue.

Continue reading here.

Happy coding 🌞

About

A lightweight library for writing concurrent programs in Go using the Actor model.

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 98.0%
  • Makefile 2.0%