Skip to content

Commit

Permalink
feat(dynamo): add ability to alarm on min TTL deletions
Browse files Browse the repository at this point in the history
  • Loading branch information
echeung-amzn committed May 30, 2024
1 parent d1fda6f commit 2444cf6
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 24 deletions.
51 changes: 51 additions & 0 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 12 additions & 6 deletions lib/monitoring/aws-dynamo/DynamoTableMetricFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ import { BillingMode, ITable, Operation } from "aws-cdk-lib/aws-dynamodb";
import { MetricFactory, MetricStatistic } from "../../common";

const DynamoDbNamespace = "AWS/DynamoDB";
const ProvisionedReadCapacityUnitsMetric = "ProvisionedReadCapacityUnits";
const ProvisionedWriteCapacityUnitsMetric = "ProvisionedWriteCapacityUnits";
const ProvisionedLabel = "Provisioned";
const ConsumedLabel = "Consumed";
const ReadThrottleEventsLabel = "Read";
const WriteThrottleEventsLabel = "Write";
const SystemErrorsLabel = "System Errors";

export interface DynamoTableMetricFactoryProps {
/**
Expand Down Expand Up @@ -39,7 +36,7 @@ export class DynamoTableMetricFactory {

metricProvisionedReadCapacityUnits() {
return this.metricFactory.adaptMetric(
this.table.metric(ProvisionedReadCapacityUnitsMetric, {
this.table.metric("ProvisionedReadCapacityUnits", {
label: ProvisionedLabel,
statistic: MetricStatistic.AVERAGE,
})
Expand All @@ -48,7 +45,7 @@ export class DynamoTableMetricFactory {

metricProvisionedWriteCapacityUnits() {
return this.metricFactory.adaptMetric(
this.table.metric(ProvisionedWriteCapacityUnitsMetric, {
this.table.metric("ProvisionedWriteCapacityUnits", {
label: ProvisionedLabel,
statistic: MetricStatistic.AVERAGE,
})
Expand Down Expand Up @@ -195,7 +192,16 @@ export class DynamoTableMetricFactory {
// the metric is not emitted until error happens
Object.keys(usingMetrics).join("+"),
usingMetrics,
SystemErrorsLabel
"System Errors"
);
}

metricTimeToLiveDeletedItemCount() {
return this.metricFactory.adaptMetric(
this.table.metric("TimeToLiveDeletedItemCount", {
label: "TTL Deleted Item Count",
statistic: MetricStatistic.MAX,
})
);
}
}
26 changes: 26 additions & 0 deletions lib/monitoring/aws-dynamo/DynamoTableMonitoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
IMetric,
IWidget,
LegendPosition,
TreatMissingData,
} from "aws-cdk-lib/aws-cloudwatch";
import {
BillingMode,
Expand Down Expand Up @@ -36,12 +37,14 @@ import {
LatencyThreshold,
LatencyType,
MetricWithAlarmSupport,
MinUsageCountThreshold,
Monitoring,
MonitoringScope,
PercentageAxisFromZeroToHundred,
QuarterWidth,
ThrottledEventsThreshold,
TimeAxisMillisFromZero,
UsageAlarmFactory,
} from "../../common";
import {
MonitoringHeaderWidget,
Expand All @@ -68,6 +71,10 @@ export interface DynamoTableMonitoringOptions extends BaseMonitoringProps {
>;

readonly addSystemErrorCountAlarm?: Record<string, ErrorCountThreshold>;
readonly addMinTimeToLiveDeletedItemCountAlarm?: Record<
string,
MinUsageCountThreshold
>;

readonly addAverageSuccessfulGetRecordsLatencyAlarm?: Record<
string,
Expand Down Expand Up @@ -120,6 +127,7 @@ export class DynamoTableMonitoring extends Monitoring {
readonly errorAlarmFactory: ErrorAlarmFactory;
readonly latencyAlarmFactory: LatencyAlarmFactory;
readonly dynamoCapacityAlarmFactory: DynamoAlarmFactory;
readonly usageAlarmFactory: UsageAlarmFactory;

readonly latencyAnnotations: HorizontalAnnotation[];
readonly errorCountAnnotations: HorizontalAnnotation[];
Expand All @@ -134,6 +142,7 @@ export class DynamoTableMonitoring extends Monitoring {
readonly readThrottleCountMetric: MetricWithAlarmSupport;
readonly writeThrottleCountMetric: MetricWithAlarmSupport;
readonly systemErrorMetric: MetricWithAlarmSupport;
readonly timeToLiveDeletedItemCountMetric: MetricWithAlarmSupport;
readonly latencyAverageSearchMetrics: IMetric;
// keys are Operation, but JSII doesn't like non-string types
readonly averagePerOperationLatencyMetrics: Record<
Expand Down Expand Up @@ -166,6 +175,8 @@ export class DynamoTableMonitoring extends Monitoring {
this.errorAlarmFactory = new ErrorAlarmFactory(this.alarmFactory);
this.latencyAlarmFactory = new LatencyAlarmFactory(this.alarmFactory);
this.dynamoCapacityAlarmFactory = new DynamoAlarmFactory(this.alarmFactory);
this.usageAlarmFactory = new UsageAlarmFactory(this.alarmFactory);

this.errorCountAnnotations = [];
this.latencyAnnotations = [];
this.dynamoReadCapacityAnnotations = [];
Expand All @@ -189,6 +200,8 @@ export class DynamoTableMonitoring extends Monitoring {
this.writeThrottleCountMetric =
metricFactory.metricThrottledWriteRequestCount();
this.systemErrorMetric = metricFactory.metricSystemErrorsCount();
this.timeToLiveDeletedItemCountMetric =
metricFactory.metricTimeToLiveDeletedItemCount();
this.latencyAverageSearchMetrics =
metricFactory.metricSearchAverageSuccessfulRequestLatencyInMillis();
this.averagePerOperationLatencyMetrics = {
Expand Down Expand Up @@ -293,6 +306,19 @@ export class DynamoTableMonitoring extends Monitoring {
this.errorCountAnnotations.push(createdAlarm.annotation);
this.addAlarm(createdAlarm);
}
for (const disambiguator in props.addMinTimeToLiveDeletedItemCountAlarm) {
const alarmProps = {
// Missing data is bad if we expect TTL to be enabled
treatMissingDataOverride: TreatMissingData.BREACHING,
...props.addMinTimeToLiveDeletedItemCountAlarm[disambiguator],
};
const createdAlarm = this.usageAlarmFactory.addMinCountAlarm(
this.timeToLiveDeletedItemCountMetric,
alarmProps,
disambiguator
);
this.addAlarm(createdAlarm);
}
this.forEachOperationLatencyAlarmDefinition(
Operation.GET_RECORDS,
props.addAverageSuccessfulGetRecordsLatencyAlarm
Expand Down
16 changes: 14 additions & 2 deletions test/monitoring/aws-dynamo/DynamoTableMonitoring.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Duration, Stack } from "aws-cdk-lib";
import { Template } from "aws-cdk-lib/assertions";
import { TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
import { AttributeType, BillingMode, Table } from "aws-cdk-lib/aws-dynamodb";

import { AlarmWithAnnotation, DynamoTableMonitoring } from "../../../lib";
Expand Down Expand Up @@ -62,6 +63,11 @@ test("snapshot test: all alarms", () => {
evaluationPeriods: 8,
},
},
addMinTimeToLiveDeletedItemCountAlarm: {
Warning: {
minCount: 5,
},
},
addReadThrottledEventsCountAlarm: {
Warning: {
maxThrottledEventsThreshold: 5,
Expand Down Expand Up @@ -125,7 +131,7 @@ test("snapshot test: all alarms", () => {
});

addMonitoringDashboardsToStack(stack, monitoring);
expect(numAlarmsCreated).toStrictEqual(14);
expect(numAlarmsCreated).toStrictEqual(15);
expect(Template.fromStack(stack)).toMatchSnapshot();
});

Expand Down Expand Up @@ -187,6 +193,12 @@ test("snapshot test: pay-per-request, all alarms", () => {
evaluationPeriods: 8,
},
},
addMinTimeToLiveDeletedItemCountAlarm: {
Warning: {
minCount: 5,
treatMissingDataOverride: TreatMissingData.MISSING,
},
},
addReadThrottledEventsCountAlarm: {
Warning: {
maxThrottledEventsThreshold: 5,
Expand Down Expand Up @@ -250,6 +262,6 @@ test("snapshot test: pay-per-request, all alarms", () => {
});

addMonitoringDashboardsToStack(stack, monitoring);
expect(numAlarmsCreated).toStrictEqual(14);
expect(numAlarmsCreated).toStrictEqual(15);
expect(Template.fromStack(stack)).toMatchSnapshot();
});
Loading

0 comments on commit 2444cf6

Please sign in to comment.