Actor System Guide¶
This guide covers the core concepts and usage of the Pulsing Actor System.
What is an Actor?¶
An Actor is a computational unit that: - Encapsulates state and behavior - Communicates only through messages - Processes messages asynchronously - Has a unique identifier
Creating Actors¶
Using the Actor Base Class¶
from pulsing.actor import Actor, Message, SystemConfig, create_actor_system
class MyActor(Actor):
def __init__(self):
self.state = {}
async def receive(self, msg: Message) -> Message:
# Handle messages
return Message.single("response", b"data")
async def main():
system = await create_actor_system(SystemConfig.standalone())
actor_ref = await system.spawn(MyActor(), "my-actor")
await system.shutdown()
Using @as_actor Decorator¶
The @as_actor decorator automatically converts a class into an Actor:
from pulsing.actor import as_actor, SystemConfig, create_actor_system
@as_actor
class Counter:
def __init__(self, value: int = 0):
self.value = value
def get(self) -> int:
return self.value
def increment(self, n: int = 1) -> int:
self.value += n
return self.value
async def main():
system = await create_actor_system(SystemConfig.standalone())
counter = await Counter.local(system, value=10)
print(await counter.get()) # 10
await system.shutdown()
Message Passing¶
Ask Pattern (Request-Response)¶
Tell Pattern (Fire-and-Forget)¶
Streaming Messages¶
msg = Message.stream("process", b"data")
async for chunk in actor_ref.ask_stream(msg):
print(f"Received: {chunk}")
Actor Lifecycle¶
Actors have a simple lifecycle: 1. Creation: Actor is spawned in the system 2. Active: Actor processes messages 3. Stopped: Actor is removed from the system
# Create actor
actor_ref = await system.spawn(MyActor(), "my-actor")
# Actor is now active and can receive messages
response = await actor_ref.ask(msg)
# Stop actor
await system.stop("my-actor")
State Management¶
Actors encapsulate state. Each actor instance has its own isolated state:
@as_actor
class StatefulActor:
def __init__(self):
self.counter = 0
self.data = {}
def increment(self) -> int:
self.counter += 1
return self.counter
def set_data(self, key: str, value: str) -> None:
self.data[key] = value
def get_data(self, key: str):
return self.data.get(key)
Error Handling¶
Errors in actor message handling are propagated to the caller:
Best Practices¶
- Keep actors focused: Each actor should have a single responsibility
- Use immutable data: Prefer immutable data structures when possible
- Handle errors gracefully: Always handle potential errors in message processing
- Use async methods: For I/O operations, use async methods
- Clean shutdown: Always call
system.shutdown()when done
Next Steps¶
- Learn about Remote Actors for cluster communication
- Check the Design Documents for implementation details