npm package: @r2d2bzh/broker-autobot
Description: self-configuring robotic lifeform to manage centrally the configuration of all Moleculer brokers
Author: r2d2bzh
Latest version: 0.0.2

:source-highlighter: highlightjs
ifdef::env-github[]
:status:
:outfilesuffix: .adoc
:caution-caption: :fire:
:important-caption: :exclamation:
:note-caption: :paperclip:
:tip-caption: :bulb:
:warning-caption: :warning:
endif::[]

:wikipedia-autobot: https://en.wikipedia.org/wiki/Autobot
:lodash-merge: https://lodash.com/docs/#merge
:moleculer: https://moleculer.services/
:moleculer-doc: {moleculer}/docs/0.14
:moleculer-action: {moleculer-doc}/actions.html
:moleculer-broker: {moleculer-doc}/broker.html
:moleculer-event: {moleculer-doc}/events.html
:moleculer-service: {moleculer-doc}/services.html

= The broker autobot

The {wikipedia-autobot}[Autobots] are benevolent, sentient, **self-configuring** robotic lifeforms from the planet Cybertron.
This particular autobot is designed to ease the configuration updates of {moleculer}[Moleculer] services {moleculer-broker}[brokers].

== Typical use case

You manage multiple {moleculer-service}[Moleculer services] and you want to easily update the configuration of the brokers hosting the service instances at once.
For instance, you have common settings for timeouts or log levels for all these brokers.

On start, `broker-autobot` will:

. get these common settings through a configurable {moleculer-action}[Moleculer action] (a temporary broker is used)
. parse these settings through a customizable parser (`broker-autobot` setting)
. create a new broker with these settings and create your services with this broker instance
. start the broker

Additionally `broker-autobot` can subscribe to a customizable event (`broker-autobot` setting) through its own service in order to be aware of a configuration change.
While receiving this event it will:

. decide whether this event applies to its configuration through a customizable predicate (`broker-autobot` setting)
. retrieve the new common settings and parse them
. **TODO**: decide for a restart order between the broker instances impacted by the modification and wait for one's turn
. stop the current broker instance
. create a new broker with these settings and create your services with this broker instance
. start the broker
. **TODO**: tell other brokers that we are finished with the update

[WARNING]
====
Some broker configuration settings **must not** be retrieved/updated by the `broker-autobot`, this is by design.
Namely:

* `namespace`
* `transporter`
* `serializer`

Basically all settings related to the communication between brokers cannot be changed dynamically.
This due to the fact that these settings are needed by the temporary broker used to retrieve the common settings.
====

== Usage

Get the `broker-autobot` module:

[source,bash]
----
npm install @r2d2bzh/broker-autobot
----

In your main module:

.Base usage
====
.index.js
[source,javascript]
----
const brokerAutobot = require('@r2d2bzh/broker-autobot');

const schema1Factory = () => {
  // return your service schema 1 here
};
const schema2Factory = () => {
  // return your service schema 2 here
};

const autobot = brokerAutobot({
  initialSettings: {
    // initial broker settings, typically settings related to the communication between brokers
    transporter: process.env.TRANSPORTER
  },
  schemaFactories: [schema1Factory, schema2Factory],
});

autobot.start();
----
NOTE: Here there is neither an action nor an event to manage the broker configuration, the broker will start with an empty configuration.
====

== Retrieving the common broker configuration

To register an action to retrieve the common broker configuration:

.Retrieving the common broker settings
====
.index.js
[source,javascript]
----
const autobot = brokerAutobot({
  settingsRetrievalAction: {
    serviceName: 'configuration-holder-service',
    actionName: 'get-broker-config',
    params: {
      // action parameters
    },
    parser: (retrieveActionResult) => {
      // parse retrieveActionResult and return the broker configuration object
    },
  },
});
----
NOTE: If no parser is provided, the `retrieveActionResult` is directly used as the broker configuration object.
====

== Overloading the common configuration

If some specific settings need to be applied to this particular broker instance, the common configuration can be overloaded:

.Broker settings settingsOverload
====
.index.js
[source,javascript]
----
const autobot = brokerAutobot({
  settingsRetrievalAction: {
    // ...
  },
  settingsOverload: {
    logLevel: { '**': 'warn' },
  },
});
----
NOTE: {lodash-merge}[Lodash merge] is used to merge `settingsOverload` over the common broker settings.
====

== Broker configuration update event

In order for the `broker-autobot` to be aware of a broker configuration update, a {moleculer-event}[Moleculer event] can be used to trigger its restart.

name:: The name of the moleculer update event name to listen for (mandatory).
throttling:: Minimum time to wait until taking into account any new update event (optional, default: `() => true`).
predicate:: Decides whether or not the update event will trigger a broker restart (optional, default: `30e3` milliseconds).

[NOTE]
====
The throttling parameter is also used to create an update window size.
This is to avoid all autobots to restart at the same time which would disrupt services resilience.

Currently the update window size is set to `¾ * throttling` which means that any autobot must start before `¼ * throttling`.
If an autobot takes longer to restart, a new throttled update event might not be taken into account.
====

.Handling configuration updates
====
.index.js
[source,javascript]
----
const autobot = brokerAutobot({
  settingsUpdateEvent: {
    name: 'configuration-holder-service.configurationUpdated',
    throttling: 20e3, // 20s
    predicate: (ctx) => {
      // returns true or false depending on the event handler context
    },
  },
});
----
NOTE: If no predicate is provided, the configuration will always be updated when the event is received.
====