Watch and Unwatch

Watch and Unwatch provide a mechanism for actors to monitor the lifecycle of other actors and receive notifications when they terminate. This is essential for building fault-tolerant, reactive systems that can respond to actor failures or shutdowns.

Watch is a mechanism that allows one actor (the watcher) to subscribe to termination notifications for another actor (the watched). When the watched actor terminatesβ€”either gracefully or due to a failureβ€”the watcher receives a Terminated message.

Key characteristics:

  • Non-intrusive: The watched actor is unaware it's being watched

  • Asynchronous: Notification arrives as a regular message

  • Idempotent: Watching the same actor multiple times has no adverse effect

  • Automatic cleanup: Watch relationships are removed when either actor terminates

  • Local only: Only local actors can be watched (not remote actors)

Importance

Use watch when you need to:

  • Monitor dependencies: Detect when a service or dependency actor fails

  • Coordinate lifecycle: Perform cleanup when related actors terminate

  • Build resilient patterns: Implement circuit breakers, health checks

  • Resource management: Release resources when actors shutdown

  • Dynamic supervision: Implement custom supervision logic beyond the built-in supervisor

  • State synchronization: React to actor termination in distributed systems

  • Connection management: Detect disconnections and reconnect

Watching

An actor can watch another actor while handling messages in the Receive method using the ctx.Watch or when having using its PID.Watch method.

An actor can watch multiple actors; each termination sends one Terminated. Watch is local only (no remote actors).

Un-watching

To un-watch an actor can use the ctx.Unwatch or PID.Unwatch methods.

Automatic Cleanup

Watch relationships are automatically cleaned up when:

  • The watched actor terminates (you receive Terminated message)

  • The watcher terminates

  • The actor system shuts down

Idempotent operations:

  • Watching an already-watched actor is safe (no duplicate notifications)

  • Un-watching an actor that isn't watched is safe (no-op)

  • Un-watching after receiving Terminated is safe (already cleaned up)

Terminated message

When a watched actor terminates, the watcher receives a Terminated message defined in the goaktpb package:

Handling Terminated Messages

Handle *goaktpb.Terminated in Receive; use msg.GetAddress() to identify the actor (compare with pid.ID()). Clean up your state and optionally restart or notify other.

Terminated Message Properties

address :

  • Unique identifier of the terminated actor

  • Format: name@system:host:port

  • Can be compared with PID.ID() to identify which actor terminated

terminated_at :

  • Timestamp when the actor was terminated

  • Type: google.protobuf.Timestamp

  • Access via msg.GetTerminatedAt().AsTime() for Go time.Time

Watch vs Supervision

Watch and supervision serve different purposes and work together:

  • Supervision: Parent supervises children; automatic restart/stop/resume via strategies. Use for built-in fault tolerance (see Supervision).

  • Watch: Any actor can watch any other; you receive Terminated and implement custom logic. Use for lifecycle awareness, resource cleanup, and monitoring non-children.

Scenario
Use

Standard error recovery

Supervision

Child actor restart on failure

Supervision

Custom termination logic

Watch

Monitor non-child actors

Watch

Resource cleanup on termination

Watch

Both together

Supervision + Watch for full fault handling

Best Practices

  1. Always handle Terminated: Add a case *goaktpb.Terminated: in Receive; unhandled notifications go to dead letters.

  2. Match by address: Use msg.GetAddress() and compare with pid.ID() to identify which actor terminated.

  3. Un-watch when done: Call ctx.UnWatch(pid) when you no longer need the notification (e.g. on unregister).

  4. Combine with supervision: Use supervision for automatic recovery and watch for custom cleanup or coordination.

  5. Don't watch yourself: ctx.Watch(ctx.Self()) is unnecessary overhead.

  6. Don't assume immediate delivery: After pid.Shutdown(ctx), the Terminated message is asynchronous; don't rely on it having arrived yet.

  7. Don't leak watches: Unwatch or handle termination so relationships don't accumulate indefinitely.

Notes

  • Watching the same actor multiple times does not create duplicate notifications. Un-watching an actor you are not watching is a no-op.

  • Watch relationships are removed when the watched actor terminates (you get Terminated), when the watcher terminates, or when the system shuts down.

  • Terminated is delivered like a normal message: queued in the watcher's mailbox and processed in order. Delivery is best-effort if the watcher is alive; it may be delayed if the mailbox is full.

  • Watch works only with local actors in the same actor system. Remote actors cannot be watched.

Last updated