From 6534336ac009e11e423555cbb38647b475171f4e Mon Sep 17 00:00:00 2001 From: RazvanP Date: Wed, 11 Dec 2024 10:33:38 +0200 Subject: [PATCH 1/4] adds streaming-whisper type --- README.md | 4 ++-- src/app.ts | 4 +++- src/autoscaler.ts | 7 +++++++ src/group_report.ts | 13 +++++++++++++ src/instance_launcher.ts | 40 ++++++++++++++++++++++++++++++++++++++++ src/instance_store.ts | 7 +++++++ src/instance_tracker.ts | 17 ++++++++++++++++- src/validator.ts | 1 + 8 files changed, 89 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c807700..f04ee15 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ ### High Level -An autoscaler for Jitsi instances (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`), which are deployed in one of the following ways: +An autoscaler for Jitsi instances (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`, `streaming-whisper`), which are deployed in one of the following ways: * as a parameterized Nomad batch job * as an Instance in Oracle Cloud * as a Droplet in Digital Ocean * custom deployment model -The autoscaler manages multiple `groups` of instances, each having a `type` (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`) and being deployed in a specific `cloud` (`oracle`, `digitalocean`, `custom`). +The autoscaler manages multiple `groups` of instances, each having a `type` (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`, `streaming-whisper`) and being deployed in a specific `cloud` (`oracle`, `digitalocean`, `custom`). The autoscaler knows the Jitsi instances status and communicates with them via the [jitsi-autoscaler-sidecar](https://github.com/jitsi/jitsi-autoscaler-sidecar), which needs to be co-located on each Jitsi instance. The sidecar periodically checks in with the autoscaler via a REST call and sends its status. diff --git a/src/app.ts b/src/app.ts index 085d0ea..8111a61 100644 --- a/src/app.ts +++ b/src/app.ts @@ -613,7 +613,9 @@ app.put( body('options.scaleDownPeriodsCount').optional().isInt({ min: 0 }).withMessage('Value must be positive'), body('instanceType').custom(async (value) => { if (!(await validator.supportedInstanceType(value))) { - throw new Error('Instance type not supported. Use jvb, jigasi, nomad, jibri or sip-jibri instead'); + throw new Error( + 'Instance type not supported. Use jvb, jigasi, nomad, jibri, streaming-whisper or sip-jibri instead', + ); } return true; }), diff --git a/src/autoscaler.ts b/src/autoscaler.ts index d621913..8f22434 100644 --- a/src/autoscaler.ts +++ b/src/autoscaler.ts @@ -206,6 +206,11 @@ export default class AutoscaleProcessor { (count < group.scalingOptions.maxDesired && value >= group.scalingOptions.scaleUpThreshold) || count < group.scalingOptions.minDesired ); + case 'streaming-whisper': + return ( + (count < group.scalingOptions.maxDesired && value >= group.scalingOptions.scaleUpThreshold) || + count < group.scalingOptions.minDesired + ); } return false; } @@ -221,6 +226,8 @@ export default class AutoscaleProcessor { case 'JVB': // in the case of JVB scale down only if value (average stress level) is below threshhold return count > group.scalingOptions.minDesired && value < group.scalingOptions.scaleDownThreshold; + case 'streaming-whisper': + return count > group.scalingOptions.minDesired && value < group.scalingOptions.scaleDownThreshold; } return false; diff --git a/src/group_report.ts b/src/group_report.ts index 299e9f8..03b6826 100644 --- a/src/group_report.ts +++ b/src/group_report.ts @@ -159,6 +159,7 @@ export default class GroupReportGenerator { break; case 'jigasi': case 'nomad': + case 'streaming-whisper': case 'JVB': // @TODO: implement JVB instance counting break; @@ -247,6 +248,18 @@ export default class GroupReportGenerator { instanceReport.scaleStatus = 'GRACEFUL SHUTDOWN'; } break; + case 'streaming-whisper': + instanceReport.scaleStatus = 'ONLINE'; + if (instanceState.status.whisperStatus && instanceState.status.whisperStatus.connections) { + instanceReport.scaleStatus = 'IN USE'; + } + if ( + instanceState.status.whisperStatus && + instanceState.status.whisperStatus.graceful_shutdown + ) { + instanceReport.scaleStatus = 'GRACEFUL SHUTDOWN'; + } + break; } } if (instanceState.metadata.name) { diff --git a/src/instance_launcher.ts b/src/instance_launcher.ts index 5f0a7ba..56d7f74 100644 --- a/src/instance_launcher.ts +++ b/src/instance_launcher.ts @@ -277,6 +277,38 @@ export default class InstanceLauncher { return listOfInstancesForScaleDown.slice(0, actualScaleDownQuantity); } + getWhisperForScaleDown( + ctx: Context, + group: InstanceGroup, + unprotectedInstances: InstanceState[], + desiredScaleDownQuantity: number, + ): InstanceDetails[] { + // first sort by participant count + unprotectedInstances.sort((a, b) => { + const aConnections = a.status.whisperStatus ? a.status.whisperStatus.connections : 0; + const bConnections = b.status.whisperStatus ? b.status.whisperStatus.connections : 0; + return aConnections - bConnections; + }); + const actualScaleDownQuantity = Math.min(desiredScaleDownQuantity, unprotectedInstances.length); + if (actualScaleDownQuantity < desiredScaleDownQuantity) { + ctx.logger.error( + '[Launcher] Nr of streaming-whisper instances in group for scale down is less than desired scale down quantity', + { groupName: group.name, actualScaleDownQuantity, desiredScaleDownQuantity }, + ); + } + // Try to not scale down the running instances unless needed + // This is needed in case of scale up problems, when we should terminate the provisioning instances first + let listOfInstancesForScaleDown = this.getProvisioningOrWithoutStatusInstances(unprotectedInstances); + if (listOfInstancesForScaleDown.length < actualScaleDownQuantity) { + listOfInstancesForScaleDown = listOfInstancesForScaleDown.concat( + this.getRunningInstances(unprotectedInstances), + ); + } + + // now return first N instances, least loaded first + return listOfInstancesForScaleDown.slice(0, actualScaleDownQuantity); + } + getJibrisForScaleDown( ctx: Context, group: InstanceGroup, @@ -365,6 +397,14 @@ export default class InstanceLauncher { desiredScaleDownQuantity, ); break; + case 'streaming-whisper': + listOfInstancesForScaleDown = this.getWhisperForScaleDown( + ctx, + group, + unprotectedInstances, + desiredScaleDownQuantity, + ); + break; } return listOfInstancesForScaleDown; } diff --git a/src/instance_store.ts b/src/instance_store.ts index 0a04fca..a2f4345 100644 --- a/src/instance_store.ts +++ b/src/instance_store.ts @@ -110,12 +110,19 @@ export interface JVBStatus { graceful_shutdown: boolean; } +export interface whisperStatus { + stress_level: number; + connections: number; + graceful_shutdown: boolean; +} + export interface InstanceStatus { provisioning: boolean; jibriStatus?: JibriStatus; jvbStatus?: JVBStatus; jigasiStatus?: JigasiStatus; nomadStatus?: NomadStatus; + whisperStatus?: whisperStatus; } export interface InstanceState { diff --git a/src/instance_tracker.ts b/src/instance_tracker.ts index e041a3e..1e7c2fb 100644 --- a/src/instance_tracker.ts +++ b/src/instance_tracker.ts @@ -12,6 +12,7 @@ import InstanceStore, { JigasiStatus, JVBStatus, NomadStatus, + whisperStatus, } from './instance_store'; /* eslint-disable */ @@ -116,6 +117,9 @@ export class InstanceTracker { case 'JVB': instanceState.status.jvbStatus = report.stats; break; + case 'streaming-whisper': + instanceState.status.whisperStatus = report.stats; + break; } } ctx.logger.debug('Tracking instance state', { instanceState }); @@ -205,6 +209,14 @@ export class InstanceTracker { metricValue = state.status.jvbStatus.stress_level; } break; + case 'streaming-whisper': + if (!state.status.whisperStatus) { + // If streaming-whisper is not up or is in graceful shutdown, we should not use it to compute average stress level across the group + trackMetric = false; + } else if (state.status.whisperStatus.stress_level) { + metricValue = state.status.whisperStatus.stress_level; + } + break; } if (trackMetric) { @@ -236,6 +248,8 @@ export class InstanceTracker { case 'jigasi': case 'JVB': return this.getAverageMetricPerPeriod(ctx, metricInventoryPerPeriod, periodCount); + case 'streaming-whisper': + return this.getAverageMetricPerPeriod(ctx, metricInventoryPerPeriod, periodCount); } return; } @@ -435,11 +449,12 @@ export class InstanceTracker { let shutdownStatus = false; shutdownStatus = state.shutdownStatus; if (!shutdownStatus) { - // check whether jigasi or JVB reports graceful shutdown, treat as if sidecar has acknowledge shutdown command + // check whether jigasi, JVB or streaming-whisper reports graceful shutdown, treat as if sidecar has acknowledge shutdown command if ( state.status && ((state.status.jvbStatus && state.status.jvbStatus.graceful_shutdown) || (state.status.jigasiStatus && state.status.jigasiStatus.graceful_shutdown) || + (state.status.whisperStatus && state.status.whisperStatus.graceful_shutdown) || (state.status.nomadStatus && !state.status.nomadStatus.eligibleForScheduling)) ) { shutdownStatus = true; diff --git a/src/validator.ts b/src/validator.ts index 26b6780..cf220b4 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -94,6 +94,7 @@ export default class Validator { instanceType.toLowerCase() == 'sip-jibri' || instanceType.toLowerCase() == 'jigasi' || instanceType.toLowerCase() == 'nomad' || + instanceType.toLowerCase() == 'streaming-whisper' || instanceType.toLowerCase() == 'jvb') ); } From 3807e1df6d75688ef0a4d8b078f2ba85b6fb7aa0 Mon Sep 17 00:00:00 2001 From: RazvanP Date: Wed, 11 Dec 2024 15:27:30 +0200 Subject: [PATCH 2/4] Rename streaming-whisper to whisper --- README.md | 4 ++-- src/app.ts | 2 +- src/autoscaler.ts | 4 ++-- src/group_report.ts | 4 ++-- src/instance_launcher.ts | 4 ++-- src/instance_tracker.ts | 10 +++++----- src/validator.ts | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f04ee15..b3d2ded 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ ### High Level -An autoscaler for Jitsi instances (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`, `streaming-whisper`), which are deployed in one of the following ways: +An autoscaler for Jitsi instances (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`, `whisper`), which are deployed in one of the following ways: * as a parameterized Nomad batch job * as an Instance in Oracle Cloud * as a Droplet in Digital Ocean * custom deployment model -The autoscaler manages multiple `groups` of instances, each having a `type` (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`, `streaming-whisper`) and being deployed in a specific `cloud` (`oracle`, `digitalocean`, `custom`). +The autoscaler manages multiple `groups` of instances, each having a `type` (`jibri`, `sip-jibri`, `jigasi`, `JVB`, `nomad`, `whisper`) and being deployed in a specific `cloud` (`oracle`, `digitalocean`, `custom`). The autoscaler knows the Jitsi instances status and communicates with them via the [jitsi-autoscaler-sidecar](https://github.com/jitsi/jitsi-autoscaler-sidecar), which needs to be co-located on each Jitsi instance. The sidecar periodically checks in with the autoscaler via a REST call and sends its status. diff --git a/src/app.ts b/src/app.ts index 8111a61..d443baa 100644 --- a/src/app.ts +++ b/src/app.ts @@ -614,7 +614,7 @@ app.put( body('instanceType').custom(async (value) => { if (!(await validator.supportedInstanceType(value))) { throw new Error( - 'Instance type not supported. Use jvb, jigasi, nomad, jibri, streaming-whisper or sip-jibri instead', + 'Instance type not supported. Use jvb, jigasi, nomad, jibri, whisper or sip-jibri instead', ); } return true; diff --git a/src/autoscaler.ts b/src/autoscaler.ts index 8f22434..940517f 100644 --- a/src/autoscaler.ts +++ b/src/autoscaler.ts @@ -206,7 +206,7 @@ export default class AutoscaleProcessor { (count < group.scalingOptions.maxDesired && value >= group.scalingOptions.scaleUpThreshold) || count < group.scalingOptions.minDesired ); - case 'streaming-whisper': + case 'whisper': return ( (count < group.scalingOptions.maxDesired && value >= group.scalingOptions.scaleUpThreshold) || count < group.scalingOptions.minDesired @@ -226,7 +226,7 @@ export default class AutoscaleProcessor { case 'JVB': // in the case of JVB scale down only if value (average stress level) is below threshhold return count > group.scalingOptions.minDesired && value < group.scalingOptions.scaleDownThreshold; - case 'streaming-whisper': + case 'whisper': return count > group.scalingOptions.minDesired && value < group.scalingOptions.scaleDownThreshold; } diff --git a/src/group_report.ts b/src/group_report.ts index 03b6826..50b1ede 100644 --- a/src/group_report.ts +++ b/src/group_report.ts @@ -159,7 +159,7 @@ export default class GroupReportGenerator { break; case 'jigasi': case 'nomad': - case 'streaming-whisper': + case 'whisper': case 'JVB': // @TODO: implement JVB instance counting break; @@ -248,7 +248,7 @@ export default class GroupReportGenerator { instanceReport.scaleStatus = 'GRACEFUL SHUTDOWN'; } break; - case 'streaming-whisper': + case 'whisper': instanceReport.scaleStatus = 'ONLINE'; if (instanceState.status.whisperStatus && instanceState.status.whisperStatus.connections) { instanceReport.scaleStatus = 'IN USE'; diff --git a/src/instance_launcher.ts b/src/instance_launcher.ts index 56d7f74..a116a43 100644 --- a/src/instance_launcher.ts +++ b/src/instance_launcher.ts @@ -292,7 +292,7 @@ export default class InstanceLauncher { const actualScaleDownQuantity = Math.min(desiredScaleDownQuantity, unprotectedInstances.length); if (actualScaleDownQuantity < desiredScaleDownQuantity) { ctx.logger.error( - '[Launcher] Nr of streaming-whisper instances in group for scale down is less than desired scale down quantity', + '[Launcher] Nr of whisper instances in group for scale down is less than desired scale down quantity', { groupName: group.name, actualScaleDownQuantity, desiredScaleDownQuantity }, ); } @@ -397,7 +397,7 @@ export default class InstanceLauncher { desiredScaleDownQuantity, ); break; - case 'streaming-whisper': + case 'whisper': listOfInstancesForScaleDown = this.getWhisperForScaleDown( ctx, group, diff --git a/src/instance_tracker.ts b/src/instance_tracker.ts index 1e7c2fb..11247b1 100644 --- a/src/instance_tracker.ts +++ b/src/instance_tracker.ts @@ -117,7 +117,7 @@ export class InstanceTracker { case 'JVB': instanceState.status.jvbStatus = report.stats; break; - case 'streaming-whisper': + case 'whisper': instanceState.status.whisperStatus = report.stats; break; } @@ -209,9 +209,9 @@ export class InstanceTracker { metricValue = state.status.jvbStatus.stress_level; } break; - case 'streaming-whisper': + case 'whisper': if (!state.status.whisperStatus) { - // If streaming-whisper is not up or is in graceful shutdown, we should not use it to compute average stress level across the group + // If whisper is not up or is in graceful shutdown, we should not use it to compute average stress level across the group trackMetric = false; } else if (state.status.whisperStatus.stress_level) { metricValue = state.status.whisperStatus.stress_level; @@ -248,7 +248,7 @@ export class InstanceTracker { case 'jigasi': case 'JVB': return this.getAverageMetricPerPeriod(ctx, metricInventoryPerPeriod, periodCount); - case 'streaming-whisper': + case 'whisper': return this.getAverageMetricPerPeriod(ctx, metricInventoryPerPeriod, periodCount); } return; @@ -449,7 +449,7 @@ export class InstanceTracker { let shutdownStatus = false; shutdownStatus = state.shutdownStatus; if (!shutdownStatus) { - // check whether jigasi, JVB or streaming-whisper reports graceful shutdown, treat as if sidecar has acknowledge shutdown command + // check whether jigasi, JVB or whisper reports graceful shutdown, treat as if sidecar has acknowledge shutdown command if ( state.status && ((state.status.jvbStatus && state.status.jvbStatus.graceful_shutdown) || diff --git a/src/validator.ts b/src/validator.ts index cf220b4..ed03382 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -94,7 +94,7 @@ export default class Validator { instanceType.toLowerCase() == 'sip-jibri' || instanceType.toLowerCase() == 'jigasi' || instanceType.toLowerCase() == 'nomad' || - instanceType.toLowerCase() == 'streaming-whisper' || + instanceType.toLowerCase() == 'whisper' || instanceType.toLowerCase() == 'jvb') ); } From 2e26f6bdbf6bfc7bc2d8d1d16ead0b00c606f9bc Mon Sep 17 00:00:00 2001 From: RazvanP Date: Wed, 11 Dec 2024 16:24:00 +0200 Subject: [PATCH 3/4] renamed interface --- src/instance_store.ts | 4 ++-- src/instance_tracker.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/instance_store.ts b/src/instance_store.ts index a2f4345..68336f0 100644 --- a/src/instance_store.ts +++ b/src/instance_store.ts @@ -110,7 +110,7 @@ export interface JVBStatus { graceful_shutdown: boolean; } -export interface whisperStatus { +export interface WhisperStatus { stress_level: number; connections: number; graceful_shutdown: boolean; @@ -122,7 +122,7 @@ export interface InstanceStatus { jvbStatus?: JVBStatus; jigasiStatus?: JigasiStatus; nomadStatus?: NomadStatus; - whisperStatus?: whisperStatus; + whisperStatus?: WhisperStatus; } export interface InstanceState { diff --git a/src/instance_tracker.ts b/src/instance_tracker.ts index 11247b1..fcc5b46 100644 --- a/src/instance_tracker.ts +++ b/src/instance_tracker.ts @@ -12,7 +12,7 @@ import InstanceStore, { JigasiStatus, JVBStatus, NomadStatus, - whisperStatus, + WhisperStatus, } from './instance_store'; /* eslint-disable */ @@ -118,7 +118,7 @@ export class InstanceTracker { instanceState.status.jvbStatus = report.stats; break; case 'whisper': - instanceState.status.whisperStatus = report.stats; + instanceState.status.whisperStatus = report.stats; break; } } From 2cf15a02dbccf572131020b508eb9c4c34a41261 Mon Sep 17 00:00:00 2001 From: Aaron van Meerten Date: Wed, 11 Dec 2024 09:02:57 -0600 Subject: [PATCH 4/4] fix whitespace --- src/app.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app.ts b/src/app.ts index d443baa..5bfe4a2 100644 --- a/src/app.ts +++ b/src/app.ts @@ -613,9 +613,7 @@ app.put( body('options.scaleDownPeriodsCount').optional().isInt({ min: 0 }).withMessage('Value must be positive'), body('instanceType').custom(async (value) => { if (!(await validator.supportedInstanceType(value))) { - throw new Error( - 'Instance type not supported. Use jvb, jigasi, nomad, jibri, whisper or sip-jibri instead', - ); + throw new Error('Instance type not supported. Use jvb, jigasi, nomad, jibri, whisper or sip-jibri instead'); } return true; }),