Supervision
This commit is contained in:
parent
04bbcd25ab
commit
2940b80563
|
@ -16,6 +16,7 @@ export * as QuasiValue from './runtime/quasivalue.js';
|
||||||
export * from './runtime/randomid.js';
|
export * from './runtime/randomid.js';
|
||||||
export * as Rewrite from './runtime/rewrite.js';
|
export * as Rewrite from './runtime/rewrite.js';
|
||||||
export * as Skeleton from './runtime/skeleton.js';
|
export * as Skeleton from './runtime/skeleton.js';
|
||||||
|
export * from './runtime/supervise.js';
|
||||||
export * as Task from './runtime/task.js';
|
export * as Task from './runtime/task.js';
|
||||||
|
|
||||||
export * as Cryptography from './transport/cryptography.js';
|
export * as Cryptography from './transport/cryptography.js';
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/// SPDX-FileCopyrightText: Copyright © 2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
|
||||||
|
import { State } from '../gen/service';
|
||||||
|
import { Actor, AnyValue, LocalAction, Turn } from './actor';
|
||||||
|
import { Field } from './dataflow';
|
||||||
|
|
||||||
|
type StateName = State['_variant'];
|
||||||
|
|
||||||
|
export enum SupervisorRestartPolicy {
|
||||||
|
ALWAYS,
|
||||||
|
ON_ERROR_ONLY,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SupervisorConfiguration = {
|
||||||
|
intensity: number,
|
||||||
|
period: number, /* seconds */
|
||||||
|
pauseTime: number, /* seconds */
|
||||||
|
sleepTime: number, /* seconds */
|
||||||
|
restartPolicy: SupervisorRestartPolicy,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_CONFIG: SupervisorConfiguration = {
|
||||||
|
intensity: 1,
|
||||||
|
period: 5,
|
||||||
|
pauseTime: 0.2,
|
||||||
|
sleepTime: 10,
|
||||||
|
restartPolicy: SupervisorRestartPolicy.ON_ERROR_ONLY,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Supervisor {
|
||||||
|
readonly config: SupervisorConfiguration;
|
||||||
|
readonly nameFunction: () => AnyValue;
|
||||||
|
readonly bootFunction: LocalAction;
|
||||||
|
readonly restarts: number[] = []; /* timestamps */
|
||||||
|
readonly state: Field<StateName> = Turn.active.field('started');
|
||||||
|
supervisee: Actor | null = null;
|
||||||
|
|
||||||
|
constructor(config: Partial<SupervisorConfiguration>,
|
||||||
|
nameFunction: () => AnyValue,
|
||||||
|
bootFunction: LocalAction)
|
||||||
|
{
|
||||||
|
this.config = Object.assign({}, DEFAULT_CONFIG, config);
|
||||||
|
this.nameFunction = nameFunction;
|
||||||
|
this.bootFunction = bootFunction;
|
||||||
|
this.startSupervisee();
|
||||||
|
}
|
||||||
|
|
||||||
|
startSupervisee() {
|
||||||
|
Turn.active.facet(() => {
|
||||||
|
this.state.value = 'started';
|
||||||
|
this.supervisee = Turn.active.spawnLink(this.bootFunction);
|
||||||
|
if (this.supervisee) this.supervisee.name = this.nameFunction();
|
||||||
|
Turn.activeFacet.onStop(() => {
|
||||||
|
const exitReason = this.supervisee?.exitReason;
|
||||||
|
if (!exitReason) {
|
||||||
|
throw new Error("Expected supervisee to have terminated");
|
||||||
|
}
|
||||||
|
if (exitReason.ok && (this.config.restartPolicy === SupervisorRestartPolicy.ON_ERROR_ONLY)) {
|
||||||
|
this.state.value = 'complete';
|
||||||
|
} else {
|
||||||
|
this.state.value = exitReason.ok ? 'complete' : 'failed';
|
||||||
|
const now = +(new Date());
|
||||||
|
const oldestToKeep = now - this.config.period * 1000.0;
|
||||||
|
this.restarts.push(now);
|
||||||
|
while (this.restarts.length && (this.restarts[0] < oldestToKeep)) {
|
||||||
|
this.restarts.shift();
|
||||||
|
}
|
||||||
|
const waitTime = (this.restarts.length > this.config.intensity)
|
||||||
|
? this.config.sleepTime
|
||||||
|
: this.config.pauseTime;
|
||||||
|
Turn.active.after(waitTime * 1000.0, () => this.startSupervisee());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue