Javier Mr
Javier Mr

Reputation: 2200

AWS-CDK get secondary indexes and metrics for those

I have a cdk stack with a DDB table created with some alarms over it. Now have successfully add a secondary index to it.

However, how can I programmatically list the secondary indexes that a table has and how can I get the metrics for it.

Background, we have a module which creates the alarms for our DDB tables. That module receives the cdk table object and creates some alarms over it using methods like metricConsumedWriteCapacityUnits.

I want to extend that alarm creator module to also create alarms for the indexes of the table, for such I need to read the secondary indexes (more concretely a global one) to check if the table has any; and if it does then create the alarms. Those alarms should be over capacity consumption and throttled requests (but might be extended to other metrics).

  1. Given a table cdk object, how can I list the secondary indexes it has?
  2. Having retrieved the secondary indexes, how can I now if they are local or global?
  3. Having retrieved a global secondary index, how can I get the metrics associated with it; capacity usage and throttled requests?

Upvotes: 3

Views: 1445

Answers (1)

Chuck Wilbur
Chuck Wilbur

Reputation: 2620

Given a table cdk object, how can I list the secondary indexes it has?

This is the tricky bit. The indexes are not available on the ITable, only the CfnTable. Getting from the ITable to the CfnTable can be done through table.node.defaultChild. Then the variables with the indexes have to be resolved in the CDK stack.

Having retrieved the secondary indexes, how can I [know] if they are local or global?

This is the easy bit. The indexes are in separate variables, globalSecondaryIndexes and localSecondaryIndexes.

Having retrieved a global secondary index, how can I get the metrics associated with it; capacity usage and throttled requests?

By passing { GlobalSecondaryIndexName: <indexName> } to the dimensions parameter of the metric call.

Below is a class, based on something I just built for work, that does all of the above. It compiles and should even run (not 100% sure it will run because I removed some intermediate scaffolding from our solution and haven't tested this exact code).

import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import * as dynamo from '@aws-cdk/aws-dynamodb';
import { Construct, Duration } from '@aws-cdk/core';

export class DynamoMonitor extends Construct {

  private static getIndexNames(dynamoTable: dynamo.ITable) {
    // pull the names of any indexes on the current table from construct
    const table = dynamoTable.node.defaultChild as dynamo.CfnTable;
    const indexes = dynamoTable.stack.resolve(table.globalSecondaryIndexes) as
        Array<dynamo.CfnTable.GlobalSecondaryIndexProperty> | undefined;
    const indexNames: string[] = [];
    if (indexes) {
      for (const index of indexes) {
        indexNames.push(index.indexName);
      }
    }
    return indexNames;
  }

  constructor(scope: Construct, id: string, dynamoTable: dynamo.ITable) {
    super(scope, id);

    const period = Duration.seconds(30);
    const threshold = 50;
    const evaluationPeriods = 5;

    const indexNames = DynamoMonitor.getIndexNames(dynamoTable);
    for (const indexName of indexNames) {
      const throttleEvents = dynamoTable.metric('WriteThrottleEvents', {
        period: period,
        dimensions: { GlobalSecondaryIndexName: indexName },
        statistic: cloudwatch.Statistic.SAMPLE_COUNT,
        unit: cloudwatch.Unit.COUNT,
      });

      const consumedWriteCapacityUnits = dynamoTable.metricConsumedWriteCapacityUnits({
        label: 'ConsumedWriteCapacityUnits',
        dimensions: { GlobalSecondaryIndexName: indexName },
        period: period,
        statistic: cloudwatch.Statistic.SAMPLE_COUNT,
        unit: cloudwatch.Unit.COUNT,
      });

      const throttleRate = new cloudwatch.MathExpression({
        expression: '(throttleEvents/consumedWriteCapacityUnits) * 100',
        label: 'WriteThrottleRate',
        usingMetrics: {
          throttleEvents: throttleEvents,
          consumedWriteCapacityUnits: consumedWriteCapacityUnits,
        },
      });

      throttleRate.createAlarm(this, `WriteIndex${indexName}ThrottleRateAlarm`, {
        threshold: threshold,
        evaluationPeriods: evaluationPeriods,
        comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
        alarmDescription: 'this.description',
        treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
      });
    }
  }
}

Upvotes: 1

Related Questions