| 1 | import type * as aas from "@distilled.cloud/aws/application-auto-scaling"; |
| 2 | import type * as Effect from "effect/Effect"; |
| 3 | import type * as Stream from "effect/Stream"; |
| 4 | import { |
| 5 | consumeBusEvents, |
| 6 | type EventRecord, |
| 7 | } from "../EventBridge/EventSource.ts"; |
| 8 | import type { ScalableTarget } from "./ScalableTarget.ts"; |
| 9 | |
| 10 | /** |
| 11 | * The `detail` payload Application Auto Scaling delivers to EventBridge for a |
| 12 | * `Application Auto Scaling Scaling Activity State Change` event. Emitted |
| 13 | * when a scale-out reaches the scalable target's maximum capacity |
| 14 | * (`scaledToMax: true`) — further demand cannot be absorbed until the |
| 15 | * target's `maxCapacity` is raised. |
| 16 | */ |
| 17 | export interface ScalingActivityEventDetail { |
| 18 | /** |
| 19 | * ISO-8601 time the scaling activity started. |
| 20 | */ |
| 21 | startTime: string; |
| 22 | /** |
| 23 | * ISO-8601 time the scaling activity ended. |
| 24 | */ |
| 25 | endTime: string; |
| 26 | /** |
| 27 | * Desired capacity after the scaling activity. |
| 28 | */ |
| 29 | newDesiredCapacity: number; |
| 30 | /** |
| 31 | * Desired capacity before the scaling activity. |
| 32 | */ |
| 33 | oldDesiredCapacity: number; |
| 34 | /** |
| 35 | * Minimum capacity of the scalable target. |
| 36 | */ |
| 37 | minCapacity: number; |
| 38 | /** |
| 39 | * Maximum capacity of the scalable target. |
| 40 | */ |
| 41 | maxCapacity: number; |
| 42 | /** |
| 43 | * Identifier of the scaled resource, e.g. `table/my-table`. |
| 44 | */ |
| 45 | resourceId: string; |
| 46 | /** |
| 47 | * Scalable dimension of the target, e.g. |
| 48 | * `dynamodb:table:WriteCapacityUnits`. |
| 49 | */ |
| 50 | scalableDimension: aas.ScalableDimension; |
| 51 | /** |
| 52 | * Namespace of the AWS service that provides the resource, e.g. `dynamodb`. |
| 53 | */ |
| 54 | serviceNamespace: aas.ServiceNamespace; |
| 55 | /** |
| 56 | * Final status of the scaling activity (e.g. `Successful`). |
| 57 | */ |
| 58 | statusCode: aas.ScalingActivityStatusCode; |
| 59 | /** |
| 60 | * Whether the activity scaled the target out to its maximum size limit. |
| 61 | */ |
| 62 | scaledToMax: boolean; |
| 63 | /** |
| 64 | * Direction of the scaling activity (`scale-out` or `scale-in`). |
| 65 | */ |
| 66 | direction?: string; |
| 67 | } |
| 68 | |
| 69 | /** A scaling-activity EventBridge event delivered to the handler. */ |
| 70 | export type ScalingActivityEvent = EventRecord<ScalingActivityEventDetail>; |
| 71 | |
| 72 | export interface ScalingActivityEventSourceProps { |
| 73 | /** |
| 74 | * Logical id prefix for the EventBridge rule. Defaults to the scalable |
| 75 | * target's logical id. |
| 76 | */ |
| 77 | id?: string; |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * Deliver Application Auto Scaling scaling-activity state-change events to |
| 82 | * the host Function via EventBridge. Application Auto Scaling emits these |
| 83 | * events when a scale-out pins a scalable target at its maximum capacity |
| 84 | * (`detail.scaledToMax`), e.g. to alert that demand exceeds the configured |
| 85 | * ceiling or to raise `maxCapacity` automatically. |
| 86 | * |
| 87 | * The EventBridge pattern matches every scaling-activity event in the |
| 88 | * account; inspect `event.detail.resourceId` / `event.detail.serviceNamespace` |
| 89 | * in the handler if multiple targets share the Function. |
| 90 | * |
| 91 | * ### Reacting to Scaling Activity |
| 92 | * **Example:** Alert When a Target Is Pinned at Max Capacity |
| 93 | * ```typescript |
| 94 | * yield* consumeScalingActivityEvents(target, {}, (events) => |
| 95 | * Stream.runForEach(events, (event) => |
| 96 | * event.detail.scaledToMax |
| 97 | * ? Effect.log( |
| 98 | * `${event.detail.resourceId} is pinned at max capacity ${event.detail.maxCapacity}`, |
| 99 | * ) |
| 100 | * : Effect.void, |
| 101 | * ), |
| 102 | * ); |
| 103 | * ``` |
| 104 | */ |
| 105 | export const consumeScalingActivityEvents = <StreamReq = never, Req = never>( |
| 106 | target: ScalableTarget, |
| 107 | props: ScalingActivityEventSourceProps, |
| 108 | process: ( |
| 109 | events: Stream.Stream<ScalingActivityEvent, never, StreamReq>, |
| 110 | ) => Effect.Effect<void, never, Req>, |
| 111 | ) => |
| 112 | // The pattern uses only literal `source` + `detail-type` so it round-trips |
| 113 | // through both the deploy-time rule and the runtime matcher (Output values |
| 114 | // cannot appear in the pattern — they don't resolve inside the deployed |
| 115 | // bundle). |
| 116 | consumeBusEvents( |
| 117 | `${props.id ?? target.LogicalId}-ScalingActivityEvents`, |
| 118 | { |
| 119 | source: ["aws.application-autoscaling"], |
| 120 | "detail-type": ["Application Auto Scaling Scaling Activity State Change"], |
| 121 | }, |
| 122 | process, |
| 123 | ); |