Skip to content

Instantly share code, notes, and snippets.

@tikatoo
Last active June 29, 2019 02:38
Show Gist options
  • Save tikatoo/876f608492b28aa406c07f6913811c20 to your computer and use it in GitHub Desktop.
Save tikatoo/876f608492b28aa406c07f6913811c20 to your computer and use it in GitHub Desktop.
Plans for how to architect Tikabot as a service

Tikabot Service Architecture

As Tikabot moves away from an XML-file-driven client app and towards a server/service, it will need a way to co-ordinate its configuration. When it becomes a service, it will need to not only store configuration somewhere, but also be able to update it on the fly.

The nature of the bot demands the configuration be cached by each node using said configuration, so it must use a system of update messages rather than a central database that gets repeatedly queried. I have decided to use these update messages as the core of the configuration system as a whole, as I believe it to be a highly adaptable architecture.

Basic Components

The whole configuration system shall be built on a sequence of "patch" commands. When configuration is updated, the changes are submitted to an updates channel. Nodes that use this configuration for processing will update their internal cache, while storage nodes will update the persisted configuration all based on a single update message. Pulling the full configuration from storage will use a similar model.

A configuration is identified by a hash. When a configuration is updated, so is the hash. Updates against unknown hashes are ignored.

Redis' Pub/Sub system will be employed for this architecture. Nodes interested in updates will subscribe to updates:<hash>. When a configuration is updated, a message is posted to the hash's channel. When an update is received, a node should unsubscribe from that hash's channel, and subscribe to the channel for the new hash. Only the first update should be given any heed. Nodes wishing to receive all updates (e.g. storage nodes) will subscribe on updates:*.

Hash details

All configurations begin with a "root configuration". The encoding of this configuration is deterministic based on the connection info used (e.g. the root for the Twitch channel #tikatoo will always be exactly the same). The hash of the root configuration is the starting point for all subsequent hashes for this configuration. This is called the "root hash".

When a configuration is modified, the new hash is the hash of the patch (which encodes the old hash within). Thus, the hash tracks the update chain, not the full configuration.

Storage

Configuration is stored as key-value pairs. The key is the latest hash for a configuration, and the value is the patch that takes the root configuration up to the latest configuration. An additional mapping is created of root hashes to latest hashes.

The storage engine listens for updates to all hashes. When it hears an update, it:

  1. Loads the configuration;
  2. Applies the patch;
  3. Stores the resultant configuration;
  4. Updates the root hash mapping;
  5. Deletes the old hash.

Loading from storage

In order to load a configuration from storage (e.g. when a node is starting up), an update request is submitted. The update request consists of a root hash. A patch taking the root configuration to the current configuration is then sent on the update channel for the root hash.

A node wishing to fetch the full configuration must first subscribe to the channel for the root hash. It then posts that root hash to update_requests. The patch that is then sent on the root hash channel can be used as if it were any other update patch.

Internally, a node will usually establish a full connection based on the root configuration before sending the update request. This allows the exact same code that handles all configuration updates to be re-used in its entirety for loading an existing configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment