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,137 @@
|
||||
package actor_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/actor"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
)
|
||||
|
||||
// LogMsg is a message type for the TellOnly example.
|
||||
type LogMsg struct {
|
||||
actor.BaseMessage
|
||||
Text string
|
||||
}
|
||||
|
||||
// MessageType implements actor.Message.
|
||||
func (m LogMsg) MessageType() string { return "LogMsg" }
|
||||
|
||||
// LoggerActorBehavior is a simple actor behavior that logs messages. It doesn't
|
||||
// produce a meaningful response for Ask, so it's a good candidate for TellOnly
|
||||
// interactions.
|
||||
type LoggerActorBehavior struct {
|
||||
mu sync.Mutex
|
||||
logs []string
|
||||
actorID string
|
||||
}
|
||||
|
||||
func NewLoggerActorBehavior(id string) *LoggerActorBehavior {
|
||||
return &LoggerActorBehavior{actorID: id}
|
||||
}
|
||||
|
||||
// Receive processes LogMsg messages by appending them to an internal log. The
|
||||
// response type is 'any' as it's not typically used with Ask.
|
||||
func (l *LoggerActorBehavior) Receive(ctx context.Context,
|
||||
msg actor.Message) fn.Result[any] {
|
||||
|
||||
logMessage, ok := msg.(LogMsg)
|
||||
if !ok {
|
||||
return fn.Err[any](fmt.Errorf("unexpected message "+
|
||||
"type: %s", msg.MessageType()))
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
entry := fmt.Sprintf("[%s from %s]: %s", time.Now().Format("15:04:05"),
|
||||
l.actorID, logMessage.Text)
|
||||
l.logs = append(l.logs, entry)
|
||||
|
||||
// For Tell, the result is often ignored, but we must return something.
|
||||
return fn.Ok[any](nil)
|
||||
}
|
||||
|
||||
func (l *LoggerActorBehavior) GetLogs() []string {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
copiedLogs := make([]string, len(l.logs))
|
||||
copy(copiedLogs, l.logs)
|
||||
|
||||
return copiedLogs
|
||||
}
|
||||
|
||||
// ExampleTellOnlyRef demonstrates using a TellOnlyRef for fire-and-forget
|
||||
// messaging with an actor.
|
||||
func ExampleTellOnlyRef() {
|
||||
system := actor.NewActorSystem()
|
||||
defer system.Shutdown()
|
||||
|
||||
// The logger actor doesn't really have a response type for Ask, so we
|
||||
// use 'any'.
|
||||
loggerServiceKey := actor.NewServiceKey[actor.Message, any](
|
||||
"tell-only-logger-service",
|
||||
)
|
||||
|
||||
actorID := "my-logger"
|
||||
loggerLogic := NewLoggerActorBehavior(actorID)
|
||||
|
||||
// Spawn the actor.
|
||||
fullRef, err := loggerServiceKey.Spawn(system, actorID, loggerLogic)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to spawn actor: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Actor %s spawned.\n", fullRef.ID())
|
||||
|
||||
// Get a TellOnlyRef for the actor. We can get this from the Actor
|
||||
// instance itself if we had it, or by type assertion if we know the
|
||||
// underlying ref supports it. Since fullRef is ActorRef[actor.Message,
|
||||
// any], it already satisfies TellOnlyRef[actor.Message].
|
||||
//
|
||||
// Or, if we had the *Actor instance: tellOnlyLogger =
|
||||
// actorInstance.TellRef()
|
||||
var tellOnlyLogger actor.TellOnlyRef[actor.Message] = fullRef
|
||||
|
||||
fmt.Printf("Obtained TellOnlyRef for %s.\n", tellOnlyLogger.ID())
|
||||
|
||||
// Send messages using Tell.
|
||||
tellOnlyLogger.Tell(
|
||||
context.Background(), LogMsg{Text: "First log entry."},
|
||||
)
|
||||
tellOnlyLogger.Tell(
|
||||
context.Background(), LogMsg{Text: "Second log entry."},
|
||||
)
|
||||
|
||||
// Allow some time for messages to be processed.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Retrieve logs directly from the behavior for verification in this
|
||||
// example. In a real scenario, this might not be possible or desired.
|
||||
logs := loggerLogic.GetLogs()
|
||||
fmt.Println("Logged entries:")
|
||||
for _, entry := range logs {
|
||||
// Strip the timestamp and actor ID for consistent example
|
||||
// output. Example entry: "[15:04:05 from my-logger]: Actual log
|
||||
// text"
|
||||
parts := strings.SplitN(entry, "]: ", 2)
|
||||
if len(parts) == 2 {
|
||||
fmt.Println(parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
// Attempting to Ask using tellOnlyLogger would be a compile-time error:
|
||||
// tellOnlyLogger.Ask(context.Background(), LogMsg{Text: "This would
|
||||
// fail"})
|
||||
|
||||
// Output:
|
||||
// Actor my-logger spawned.
|
||||
// Obtained TellOnlyRef for my-logger.
|
||||
// Logged entries:
|
||||
// First log entry.
|
||||
// Second log entry.
|
||||
}
|
||||
Reference in new issue
Block a user