CBOR serialization in Goakt V4
Even though Goakt has always been a great technology for me to adopt, I was always reluctant to use it in my application because writing and managing Protobuf files wasn’t feel really elegant for my use cases (skill issue). Competing libraries like Ergo & Hollywood allows you to use actor without writing Protobuf files.
Fortunately, in Goakt v4, the author has added a new pluggable serializer feature and you can use CBOR serializer. You can read more about its implementation details in its source code on Github. This means you can just use Go structs to serialize messages when communicating between actors over the network.
In my opinion, this is a big deal because there is no more learning curve in adopting Goakt and I have been using it a lot in my personal project. No more protoc or buf commands to run, all types are referenced from actual Go packages that I can import everywhere.
Example: Live web counter#
Init the Go project with the following command:
$ go mod init pageview_counter
Write our first grain actor:
// pageview.go
package main
import (
"context"
"github.com/tochemey/goakt/v4/actor"
)
type PageView struct {
id string
count int64
}
type IncrementCount struct {}
type GetCountRequest struct {}
var _ actor.Grain = (*PageView)(nil)
func (g *PageView) OnActivate(ctx context.Context, props *actor.GrainProps) error {
return g.recoverState(ctx)
}
func (g *PageView) OnDeactivate(ctx context.Context, props *actor.GrainProps) error {
return g.writeState(ctx)
}
func (g *PageView) OnReceive(ctx *actor.GrainContext) {
switch ctx.Message().(type) {
case IncrementCount:
g.count++
case GetCountRequest:
ctx.Response(g.count)
default:
ctx.Unhandled()
}
}
func (g *PageView) recoverState(_ context.Context) error {
// Fetch the state from datastore
// and then assign as the grain state
// example: g.count = fetchFromDb()
return nil
}
func (g *PageView) writeState(_ context.Context) error {
// Save state into the datastore
// example: saveInDatastore(g.id, g.state)
return nil
}
And now we can interact with the grain actors.
package main
import (
"context"
"fmt"
"time"
"github.com/tochemey/goakt/v4/actor"
)
func main() {
ctx := context.Background()
fmt.Println("Starting goakt actor system")
actorSystem, _ := actor.NewActorSystem("PageView")
// Handle error
actorSystem.Start(ctx)
// Get grain identity
identity, _ := actorSystem.GrainIdentity(ctx, "grainId", func(ctx context.Context) (actor.Grain, error) {
return &PageView{}, nil
})
// Increment the page count
actorSystem.TellGrain(ctx, identity, IncrementCount{})
// Get the page count
count, _ := actorSystem.AskGrain(ctx, identity, GetCountRequest{}, 1*time.Second)
fmt.Println(count.(int64)) // Cast the result into the right type
// Handle error
actorSystem.Stop(ctx)
}
You can wire this actor system into whatever interface you wish to expose to the users. You can create HTTP handler and then make TellGrain() and AskGrain() call, create gRPC handler, Kafka message handler, up to you. I’ll leave this to you to try on your own.
Further readings
You can read more about CBOR implementation in Goakt in its official documentation. You may also see official example project in its goakt-example repo. Last but not least, if you wish to dig into the code, this PR implements the pluggable serialization for Goakt library.
Enjoy!