Core design

High level overview

This document focuses on XUDD’s core design. XUDD follows the actor model. The high level of this is that

There are three essential components to XUDD’s design:

  • Actors: Actors are encapsulations of some functionality. They run independently of each other and manage their own resources.

    Actors do not directly interfere with each others resources, but they have mechanisms, via message passing, to get properties of each other, request changes, or some other actions. Actors can also spawn other actors via their relationship with their Hive. Actors do not get direct access to other actors as objects, but instead just get references to their ids. This is a feature: in theory this means that actors can just as easily communicate with an actor as if it is local as if it were over the network.

    In XUDD’s design, Actors are generally not “always running”... instead, they are woken up as needed to process messages. (The exception to this being “Dedicated Actors”; more on this later.)

  • Messages: As said, actors communicate via message passing. Messages are easy to understand: like email, they have information about who the message is from, instructions on who the message can go to, as well as a body of information (in this case, a dictionary/hashtable).

    They also contain some other information, such as “directives” that specify what action the receiving actor should take (assuming they know how to handle such things), and can inform the receiving actor that they are waiting on a response (more on this and coroutines later).

    Messages also include tooling so they can be serialized and sent between processes or over a network.

  • The Hive: Every actor is associated with a “Hive”, which manages a set of actors. The Hive is responsible for passing messages from actor to actor. For standard actors, the Hive also handles “waking actors up” and handling their execution of tasks. (More on this later, since that wording is possibly confusing.)

    Actors do not get direct access to the Hive, but instead have a “HiveProxy” object. They use this to send messages from actor to actor, intializing new actors, or requesting shutdown of the hive and all actors.

These concepts are expanded on later in this document. Additional features/components that are planned as part of XUDD’s design (some of these are yet to be implemented):

  • Inter-hive messaging:
  • Dedicated actors:
  • Actor “event” subscriptions:
  • Property API:
  • Actor serialization:

Detailed design

Actors, hives, and other actors

So, the above explains the relationships between actors, messaging, and hives.

ACTOR AND HIVE BASICS

  .--.     .--.     .--.
 ( o_o)   ( =_=)   (@_@ )
 ,'--',   ,'--',   ,'--',
 |  A |   |  B |   | C  |
 '----'   '----'   '----'
  .  ^     .  ^     .  ^
  |  |     |  |     |  |
[HP] |   [HP] |   [HP] |
  |  |     |  |     |  |
  V  '     V  '     V  '
.------------------------.
|         HIVE           |
'------------------------'

Here we have the basic relationship between a hive and three of its actors, A B and C. Each one has its own unique id, shared by no other actor on the hive. You can see that there are also relationships between an actor on the hive. The hive has direct access to an actor, but actors don’t have direct access to the hive... they have to go through a HiveProxy. There’s good reason for this: hives may have more methods than should be exposed to actors. In fact, it’s entirely possible for an actor to be hooked up to a hive that operates very differently than the “basic” hive that XUDD ships with. By using the HiveProxy, the actor doesn’t actually need to know anything about how the Hive works: as long as it uses the HiveProxy methods, those operate just fine.

You can see how this works in code:

Sending messages from actor to actor

class xudd.message.Message(to, directive, from_id, id, body=None, in_reply_to=None, wants_reply=False, hive_proxy=None)

Encapsulation of message data.

This is what’s actually put in an actor’s message queue. While messages can actually be serialized into json or msgpack data, (and methods for that are provided,) this is the standard representation for passing around messages in XUDD itself.

Usually, however, actors themselves do not construct Message objects: these are instead constructed by the Hive itself. Actors send off messages using their HiveProxy.send_message() method.

Args:

  • to: the id of the receiving actor

  • directive: what kind of action or request we’re making of the receiving actor. Usually this is some kind of useful instruction or request. For example, we might be communicating with a Dragon actor, and we might give it the directive “breathe_fire”, which a dragon actor knows how to handle. However, if we’re just replying to messages, frequently this directive is simply “reply”.

    In the future, there will also be a standardized set of common “error” directives :)

  • from_id: the id of the actor sending this message

  • id: the id of this message itself. Usually constructed by the Hive itself (but available to the actor sending the message also, often used to track “waiting on replies” for coroutines-in-waiting)

  • body: a dictionary of data; the payload of the message. (if None, will be converted to an empty dict.) This can be anything, with a couple of caveats:

    • If there’s any possibility of sending this across the wire via inter-hive communication, the contents of “body” ABSOLUTELY MUST be json encodeable.
    • If the message is just being sent for local actor to local actor, it’s acceptable to pass along whatever, but keep in mind that you are effectively breaking any possibility of inter-hive communication between these actors!
    • If you are sending along ANY mutable structures, your actor must NEVER ACCESS THOSE OBJECTS AGAIN. Not for reading, not for writing. If you do otherwise, consider yourself breaking the rules, and you are on THIN ICE. This includes basic structures, such as lists. If you have any doubt, consider using copy.deepcopy() on objects you’re passing into here.
    • “sanitize” options (with some performance pentalties) may be added in the future that will force-transform into json or msgpack and back, but those don’t exist yet.
  • in_reply_to: The message id of a previous message that we’re responding to. This may be used by the actor we’re sending this to for waking back up coroutines that are awaiting this response.

  • wants_reply: Informs the actor receiving this that we want some kind of response. In general, actors will respect this; if a message requests a response, an actor absolutely should provide one, one way or another. The plus side is that we have some tooling built in to make this easy. See Replying to messages for details.

  • hive_proxy: In order for the auto-replying tools to work, a hive_proxy must be constructed, which generally is the same hive_proxy the receiving actor has. When constructing a Message object, you don’t necessarily have to pass this in when initializing the object, but you should attach this to the message.hive_proxy object before passing to the message queue of the actor.

Message queues and the two types of actors

Basic actors

Dedicated actors

Yielding for replies

Replying to messages

Hives

Variants and meta-discussion

Hives

The standard hive

Variants on the standard Hive