306 lines
11 KiB
TypeScript
Raw Permalink Normal View History

import * as fs from 'node:fs';
import * as path from 'node:path';
import {
decideMatrixPlacementBind,
type AuthorityCoordinatorLease,
type MatrixMountCardinality,
type MatrixPlacementBindDecision,
type MatrixPlacementBindRequest,
type MatrixPlacementPolicy,
type RuntimeInventoryPlacementMetadata,
} from '@open-matrix/contracts/placement';
import type { MatrixActor } from '@open-matrix/core/core/MatrixActor.js';
import { MatrixRuntime } from '@open-matrix/core/runtime/MatrixRuntime.js';
import type { ILoadedMatrixPackageEnvironment } from './package-environment.js';
import type { ILoadedMatrixServiceManifest } from './service-manifest.js';
export const MATRIX_PLACEMENT_POLICY_SERVICE = 'matrix-placement-policy';
export const MATRIX_PLACEMENT_STANDBY_RESULTS_SERVICE = 'matrix-placement-standby-results';
export interface RunnerPlacementStandbyResult {
readonly mount: string;
readonly runtimeId: string;
readonly packageName: string;
readonly placement: RuntimeInventoryPlacementMetadata;
}
interface PlacementDeclaration {
readonly cardinality: MatrixMountCardinality;
readonly physicalMount?: string;
}
interface NodeTopologySnapshot {
readonly kind?: string;
readonly nodeId?: string;
readonly authorityRoot: string;
readonly authorityCoordinatorActive: boolean;
readonly authorityLease?: AuthorityCoordinatorLease | null;
}
type SupervisedCreate = MatrixRuntime['createSupervised'];
export function installRunnerPlacementPolicy(options: {
readonly runtime: MatrixRuntime;
readonly manifest: ILoadedMatrixServiceManifest;
readonly environment: ILoadedMatrixPackageEnvironment | undefined;
readonly runtimeId: string;
readonly authorityRoot: string | undefined;
}): void {
const declarations = readPlacementDeclarations(options.manifest);
const topology = resolveTopologySnapshot(options.runtime, options.environment, options.authorityRoot);
const standbyResults: RunnerPlacementStandbyResult[] = [];
const policy: MatrixPlacementPolicy = {
canBind(input: MatrixPlacementBindRequest): MatrixPlacementBindDecision {
return decideMatrixPlacementBind(input);
},
};
options.runtime.registerService(MATRIX_PLACEMENT_POLICY_SERVICE, policy);
options.runtime.registerService(MATRIX_PLACEMENT_STANDBY_RESULTS_SERVICE, standbyResults);
const originalCreateSupervised = options.runtime.createSupervised.bind(options.runtime) as SupervisedCreate;
options.runtime.createSupervised = (async <T extends MatrixActor>(
ComponentClass: new () => T,
mount: string,
props?: Record<string, unknown>,
): Promise<T> => {
const trimmedMount = mount.trim();
const declaration = declarations.get(trimmedMount);
if (!declaration) {
return await originalCreateSupervised(ComponentClass, mount, props);
}
const physicalMount = declaration.physicalMount ?? trimmedMount;
const request: MatrixPlacementBindRequest = {
authorityRoot: topology.authorityRoot,
runtimeId: options.runtimeId,
packageName: options.manifest.packageName,
mount: trimmedMount,
cardinality: declaration.cardinality,
...(topology.kind ? { topologyKind: topology.kind } : {}),
authorityCoordinatorActive: topology.authorityCoordinatorActive,
...(topology.nodeId ? { ownerNodeId: topology.nodeId } : {}),
...(topology.authorityLease !== undefined ? { authorityLease: topology.authorityLease } : {}),
};
const decision = policy.canBind(request);
if (decision.allowed) {
return await originalCreateSupervised(ComponentClass, mount, props);
}
standbyResults.push({
mount: trimmedMount,
runtimeId: options.runtimeId,
packageName: options.manifest.packageName,
placement: {
cardinality: declaration.cardinality,
claimMode: claimModeForCardinality(declaration.cardinality),
logicalMount: trimmedMount,
physicalMount,
callable: decision.callable,
...(decision.reason ? { standbyReason: decision.reason } : {}),
},
});
throw new Error(`Placement denied for ${trimmedMount}: ${decision.reason ?? 'UNKNOWN_PLACEMENT'}`);
}) as SupervisedCreate;
}
export function readRunnerPlacementStandbyResults(runtime: MatrixRuntime): readonly RunnerPlacementStandbyResult[] {
return runtime.getRootContext().getService<readonly RunnerPlacementStandbyResult[]>(
MATRIX_PLACEMENT_STANDBY_RESULTS_SERVICE,
) ?? [];
}
function resolveTopologySnapshot(
runtime: MatrixRuntime,
environment: ILoadedMatrixPackageEnvironment | undefined,
authorityRoot: string | undefined,
): NodeTopologySnapshot {
const currentRoot = authorityRoot?.trim()
|| environment?.environment.runtime.root?.trim()
|| runtime.transport.root.trim();
const topology = readTopologyRecord(runtime.getRootContext().getService<unknown>('matrix-node-topology'))
?? readTopologyRecord(environment?.environment.topology);
const topologyAuthorityRoot = readAuthorityRoot(topology) ?? currentRoot;
const active = readAuthorityCoordinatorActive(topology);
const authorityLease = readAuthorityLease(runtime, environment, topology);
return {
...(readString(topology?.kind) ? { kind: readString(topology?.kind) } : {}),
...(readString(topology?.nodeId) ? { nodeId: readString(topology?.nodeId) } : {}),
authorityRoot: topologyAuthorityRoot,
authorityCoordinatorActive: active ?? true,
...(authorityLease !== undefined ? { authorityLease } : {}),
};
}
function readAuthorityLease(
runtime: MatrixRuntime,
environment: ILoadedMatrixPackageEnvironment | undefined,
topology: Record<string, unknown> | undefined,
): AuthorityCoordinatorLease | null | undefined {
const serviceLease = readAuthorityLeaseRecord(runtime.getRootContext().getService<unknown>('matrix-authority-coordinator-lease'));
if (serviceLease !== undefined) {
return serviceLease;
}
const topologyLease = readAuthorityLeaseRecord(topology?.authorityLease);
if (topologyLease !== undefined) {
return topologyLease;
}
return readAuthorityLeaseRecord(environment?.environment.authorityLease);
}
function readPlacementDeclarations(manifest: ILoadedMatrixServiceManifest): ReadonlyMap<string, PlacementDeclaration> {
const declarations = new Map<string, PlacementDeclaration>();
const manifestRecord = readJsonObjectIfPresent(path.join(manifest.packageDir, 'matrix.json'));
if (!manifestRecord) {
return declarations;
}
addPlacementDeclaration(declarations, manifestRecord.root);
const components = manifestRecord.components;
if (Array.isArray(components)) {
for (const component of components) {
addPlacementDeclaration(declarations, component);
}
}
return declarations;
}
function addPlacementDeclaration(declarations: Map<string, PlacementDeclaration>, value: unknown): void {
if (!isRecord(value)) {
return;
}
const mount = readString(value.mount);
const placement = isRecord(value.placement) ? value.placement : undefined;
const cardinality = readMountCardinality(placement?.cardinality);
if (!mount || !cardinality) {
return;
}
const physicalMount = readString(placement?.physicalMount);
declarations.set(mount, {
cardinality,
...(physicalMount ? { physicalMount } : {}),
});
}
function readTopologyRecord(value: unknown): Record<string, unknown> | undefined {
return isRecord(value) ? value : undefined;
}
function readAuthorityRoot(topology: Record<string, unknown> | undefined): string | undefined {
const authority = isRecord(topology?.authority) ? topology.authority : undefined;
return readString(authority?.authorityRoot);
}
function readAuthorityCoordinatorActive(topology: Record<string, unknown> | undefined): boolean | undefined {
const roles = isRecord(topology?.roles) ? topology.roles : undefined;
const authorityCoordinator = isRecord(roles?.authorityCoordinator) ? roles.authorityCoordinator : undefined;
return typeof authorityCoordinator?.active === 'boolean' ? authorityCoordinator.active : undefined;
}
function readAuthorityLeaseRecord(value: unknown): AuthorityCoordinatorLease | null | undefined {
if (value === null) {
return null;
}
if (!isRecord(value)) {
return undefined;
}
if (
value.version !== 1
|| typeof value.authorityRoot !== 'string'
|| typeof value.leaseId !== 'string'
|| typeof value.ownerNodeId !== 'string'
|| typeof value.placementEpoch !== 'number'
|| typeof value.acquiredAt !== 'string'
|| typeof value.heartbeatAt !== 'string'
|| typeof value.expiresAt !== 'string'
) {
return undefined;
}
if (
value.status !== 'active'
&& value.status !== 'released'
&& value.status !== 'expired'
&& value.status !== 'fenced'
) {
return undefined;
}
if (
value.scope !== 'local-home'
&& value.scope !== 'hub-authority'
&& value.scope !== 'remote-projection'
) {
return undefined;
}
if (
value.leaseAuthority !== 'local'
&& value.leaseAuthority !== 'hub'
) {
return undefined;
}
if (
value.source !== 'local-file'
&& value.source !== 'hub-projection'
) {
return undefined;
}
return {
version: 1,
authorityRoot: value.authorityRoot,
leaseId: value.leaseId,
ownerNodeId: value.ownerNodeId,
...(readString(value.ownerHostId) ? { ownerHostId: readString(value.ownerHostId) } : {}),
...(readString(value.ownerInstallId) ? { ownerInstallId: readString(value.ownerInstallId) } : {}),
...(readString(value.ownerHostLinkId) ? { ownerHostLinkId: readString(value.ownerHostLinkId) } : {}),
placementEpoch: value.placementEpoch,
acquiredAt: value.acquiredAt,
heartbeatAt: value.heartbeatAt,
expiresAt: value.expiresAt,
status: value.status,
scope: value.scope,
leaseAuthority: value.leaseAuthority,
source: value.source,
...(readString(value.reason) ? { reason: readString(value.reason) } : {}),
};
}
function claimModeForCardinality(cardinality: MatrixMountCardinality): RuntimeInventoryPlacementMetadata['claimMode'] {
if (cardinality === 'authority-singleton') {
return 'authoritative';
}
if (cardinality === 'queue-service') {
return 'pool-member';
}
if (cardinality === 'replicated-read') {
return 'mirror';
}
return 'local-only';
}
function readMountCardinality(value: unknown): MatrixMountCardinality | undefined {
return value === 'authority-singleton'
|| value === 'device-singleton'
|| value === 'device-local-control'
|| value === 'queue-service'
|| value === 'named-instance'
|| value === 'replicated-read'
? value
: undefined;
}
function readJsonObjectIfPresent(filePath: string): Record<string, unknown> | undefined {
if (!fs.existsSync(filePath)) {
return undefined;
}
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '')) as unknown;
if (!isRecord(parsed)) {
throw new Error(`Expected JSON object at ${filePath}`);
}
return parsed;
}
function readString(value: unknown): string {
return typeof value === 'string' ? value.trim() : '';
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}