actor: add example files
In this commit, we add a series of examples that show how the package can be used in the wild. They can be run as normal Example tests.
This commit is contained in:
4 files changed
+511
No files matched your search
@@ -0,0 +1,153 @@
|
||||
package actor_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/actor"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
)
|
||||
|
||||
// CounterMsg is a message type for the stateful counter actor.
|
||||
// It can be used to increment the counter or get its current value.
|
||||
type CounterMsg struct {
|
||||
actor.BaseMessage
|
||||
Increment int
|
||||
GetValue bool
|
||||
Who string
|
||||
}
|
||||
|
||||
// MessageType implements actor.Message.
|
||||
func (m CounterMsg) MessageType() string { return "CounterMsg" }
|
||||
|
||||
// CounterResponse is a response type for the counter actor.
|
||||
type CounterResponse struct {
|
||||
Value int
|
||||
Responder string
|
||||
}
|
||||
|
||||
// StatefulCounterActor demonstrates an actor that maintains internal state (a
|
||||
// counter) and processes messages to modify or query that state.
|
||||
type StatefulCounterActor struct {
|
||||
counter int
|
||||
actorID string
|
||||
}
|
||||
|
||||
// NewStatefulCounterActor creates a new counter actor.
|
||||
func NewStatefulCounterActor(id string) *StatefulCounterActor {
|
||||
return &StatefulCounterActor{
|
||||
actorID: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Receive is the message handler for the StatefulCounterActor.
|
||||
// It implements the actor.ActorBehavior interface implicitly when wrapped.
|
||||
func (s *StatefulCounterActor) Receive(ctx context.Context,
|
||||
msg CounterMsg) fn.Result[CounterResponse] {
|
||||
|
||||
if msg.Increment > 0 {
|
||||
// For increment, we can just acknowledge or return the new
|
||||
// value. Messages are sent serially, so we don't need to worry
|
||||
// about a mutex here.
|
||||
s.counter += msg.Increment
|
||||
|
||||
return fn.Ok(CounterResponse{
|
||||
Value: s.counter,
|
||||
Responder: s.actorID,
|
||||
})
|
||||
}
|
||||
|
||||
if msg.GetValue {
|
||||
return fn.Ok(CounterResponse{
|
||||
Value: s.counter,
|
||||
Responder: s.actorID,
|
||||
})
|
||||
}
|
||||
|
||||
return fn.Err[CounterResponse](fmt.Errorf("invalid CounterMsg"))
|
||||
}
|
||||
|
||||
// ExampleActor_stateful demonstrates creating an actor whose behavior is defined
|
||||
// by a struct with methods, allowing it to maintain internal state.
|
||||
func ExampleActor_stateful() {
|
||||
system := actor.NewActorSystem()
|
||||
defer system.Shutdown()
|
||||
|
||||
counterServiceKey := actor.NewServiceKey[CounterMsg, CounterResponse](
|
||||
"struct-counter-service",
|
||||
)
|
||||
|
||||
// Create an instance of our stateful actor logic.
|
||||
actorID := "counter-actor-1"
|
||||
counterLogic := NewStatefulCounterActor(actorID)
|
||||
|
||||
// Spawn the actor.
|
||||
// The counterLogic instance itself satisfies the ActorBehavior
|
||||
// interface because its Receive method matches the required signature.
|
||||
counterRef, err := counterServiceKey.Spawn(
|
||||
system, actorID, counterLogic,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to spawn actor: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Actor %s spawned.\n", counterRef.ID())
|
||||
|
||||
// Send messages to increment the counter.
|
||||
for i := 1; i <= 3; i++ {
|
||||
askCtx, askCancel := context.WithTimeout(
|
||||
context.Background(), 1*time.Second,
|
||||
)
|
||||
futureResp := counterRef.Ask(askCtx,
|
||||
CounterMsg{
|
||||
Increment: i,
|
||||
Who: fmt.Sprintf("Incrementer-%d", i),
|
||||
},
|
||||
)
|
||||
awaitCtx, awaitCancel := context.WithTimeout(
|
||||
context.Background(), 1*time.Second,
|
||||
)
|
||||
resp := futureResp.Await(awaitCtx)
|
||||
|
||||
resp.WhenOk(func(r CounterResponse) {
|
||||
fmt.Printf("Incremented by %d, new value: %d "+
|
||||
"(from %s)\n", i, r.Value, r.Responder)
|
||||
})
|
||||
resp.WhenErr(func(e error) {
|
||||
fmt.Printf("Error incrementing: %v\n", e)
|
||||
})
|
||||
awaitCancel()
|
||||
askCancel()
|
||||
}
|
||||
|
||||
// Send a message to get the current value.
|
||||
askCtx, askCancel := context.WithTimeout(
|
||||
context.Background(), 1*time.Second,
|
||||
)
|
||||
futureResp := counterRef.Ask(
|
||||
askCtx, CounterMsg{GetValue: true, Who: "Getter"},
|
||||
)
|
||||
|
||||
awaitCtx, awaitCancel := context.WithTimeout(
|
||||
context.Background(), 1*time.Second,
|
||||
)
|
||||
|
||||
finalValueResp := futureResp.Await(awaitCtx)
|
||||
finalValueResp.WhenOk(func(r CounterResponse) {
|
||||
fmt.Printf("Final counter value: %d (from %s)\n",
|
||||
r.Value, r.Responder)
|
||||
})
|
||||
finalValueResp.WhenErr(func(e error) {
|
||||
fmt.Printf("Error getting value: %v\n", e)
|
||||
})
|
||||
awaitCancel()
|
||||
askCancel()
|
||||
|
||||
// Output:
|
||||
// Actor counter-actor-1 spawned.
|
||||
// Incremented by 1, new value: 1 (from counter-actor-1)
|
||||
// Incremented by 2, new value: 3 (from counter-actor-1)
|
||||
// Incremented by 3, new value: 6 (from counter-actor-1)
|
||||
// Final counter value: 6 (from counter-actor-1)
|
||||
}
|
||||
Reference in new issue
Block a user