in notificationworkerlambda/cdk/lib/senderworker.ts [37:220]
constructor(scope: GuStack, id: string, props: SenderWorkerOpts) {
super(scope, id)
cdk.Tags.of(this).add("App", id)
const snsTopicAction = new SnsAction(props.alarmTopic)
const senderDlq = new sqs.Queue(this, 'SenderDlq')
this.senderSqs = new sqs.Queue(this, 'SenderSqs', {
visibilityTimeout: props.isBatchingSqsMessages ? cdk.Duration.seconds(190) : cdk.Duration.seconds(100),
retentionPeriod: cdk.Duration.hours(1),
deadLetterQueue: {
queue: senderDlq,
maxReceiveCount: 5
}
})
const executionRole = new iam.Role(this, 'ExecutionRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
path: "/",
inlinePolicies: {
logs: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: [ 'logs:CreateLogGroup' ],
resources: [ `arn:aws:logs:eu-west-1:${scope.account}:*` ]
}),
new iam.PolicyStatement({
actions: [ 'logs:CreateLogStream', 'logs:PutLogEvents' ],
resources: [ `arn:aws:logs:eu-west-1:${scope.account}:log-group:/aws/lambda/*:*` ]
})
] }),
SQSOutput: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: [ 'sqs:*' ],
resources: [ this.senderSqs.queueArn ]
})
] }),
SQSInput: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: [ 'sqs:SendMessage' ],
resources: [ props.cleanerQueueArn ]
})
] }),
Conf: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: [ 'ssm:GetParametersByPath' ],
resources: [ `arn:aws:ssm:${scope.region}:${scope.account}:parameter/notifications/${scope.stage}/workers/${props.platform}` ]
})
] }),
Cloudwatch: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: [ 'cloudwatch:PutMetricData' ],
resources: [ '*' ]
})
] }),
}
})
const codeImage = lambda.DockerImageCode.fromEcr(props.imageRepo, {
cmd: [ props.handler ],
tag: props.buildId
})
const senderLambdaCtr = new lambda.DockerImageFunction(this, 'SenderLambdaCtr', {
functionName: `${scope.stack}-${id}-sender-ctr-${scope.stage}`,
code: codeImage,
environment: {
Stage: scope.stage,
Stack: scope.stack,
App: id,
Platform: props.platform,
},
memorySize: 10240,
description: `sends notifications for ${id}`,
role: executionRole,
timeout: props.isBatchingSqsMessages ? cdk.Duration.seconds(180) : cdk.Duration.seconds(90),
reservedConcurrentExecutions: props.reservedConcurrency
})
const senderSqsEventSourceMapping = new lambda.EventSourceMapping(this, "SenderSqsEventSourceMapping", {
batchSize: props.isBatchingSqsMessages ? 20 : 1,
maxBatchingWindow: props.isBatchingSqsMessages ? cdk.Duration.seconds(1) : cdk.Duration.seconds(0),
enabled: true,
eventSourceArn: this.senderSqs.queueArn,
target: senderLambdaCtr
})
senderSqsEventSourceMapping.node.addDependency(this.senderSqs)
senderSqsEventSourceMapping.node.addDependency(senderLambdaCtr)
const senderThrottleAlarm = new cloudwatch.Alarm(this, 'SenderThrottleAlarm', {
alarmDescription: `Triggers if the ${id} sender lambda is throttled in ${scope.stage}.`,
comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
evaluationPeriods: 1,
threshold: 0,
metric: senderLambdaCtr.metricThrottles({period: cdk.Duration.seconds(360), statistic: "Sum"}),
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING
})
senderThrottleAlarm.addAlarmAction(snsTopicAction)
senderThrottleAlarm.addOkAction(snsTopicAction)
const senderErrorAlarm = new cloudwatch.Alarm(this, 'SenderErrorAlarm', {
alarmDescription: `Triggers if the ${id} sender lambda errors in ${scope.stage}.`,
comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
evaluationPeriods: 1,
threshold: 0,
metric: senderLambdaCtr.metricErrors({period: cdk.Duration.seconds(360), statistic: "Sum"}),
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING
})
senderErrorAlarm.addAlarmAction(snsTopicAction)
senderErrorAlarm.addOkAction(snsTopicAction)
const senderTooFewInvocationsAlarm = new cloudwatch.Alarm(this, 'SenderTooFewInvocationsAlarm', {
alarmDescription: `Triggers if the ${id} sender lambda is not frequently invoked in ${scope.stage}.`,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
evaluationPeriods: 1,
threshold: 0,
// whole day for editions, 60 minutes for others
metric: senderLambdaCtr.metricInvocations({period: cdk.Duration.seconds(props.dailyAlarmPeriod ? 60 * 60 * 24 : 60 * 60), statistic: "Sum"}),
treatMissingData: cloudwatch.TreatMissingData.BREACHING,
actionsEnabled: true // isEnabled
})
senderTooFewInvocationsAlarm.addAlarmAction(snsTopicAction)
senderTooFewInvocationsAlarm.addOkAction(snsTopicAction)
if (props.tooFewNotificationByTypeAlarms) {
const nonBreakingCountMetric = new Metric({
namespace: `Notifications/${scope.stage}/workers`,
metricName: "worker.notificationProcessingTime",
period: Duration.minutes(15),
statistic: "SampleCount",
dimensionsMap: {
platform: id,
type: "other",
},
});
const senderTooFewNonBreakingAlarm = new cloudwatch.Alarm(this, 'SenderTooFewNonBreakingAlarm', {
alarmDescription: `Triggers if the ${id} sender lambda is not frequently invoked for non-breaking news notification in ${scope.stage}.`,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
evaluationPeriods: 4,
threshold: 0,
metric: nonBreakingCountMetric,
treatMissingData: cloudwatch.TreatMissingData.BREACHING,
actionsEnabled: (scope.stage === 'PROD'),
})
senderTooFewNonBreakingAlarm.addAlarmAction(snsTopicAction)
senderTooFewNonBreakingAlarm.addOkAction(snsTopicAction)
const breakingNewsCountMetric = new Metric({
namespace: `Notifications/${scope.stage}/workers`,
metricName: "worker.notificationProcessingTime",
period: Duration.minutes(15),
statistic: "SampleCount",
dimensionsMap: {
platform: id,
type: "breakingNews",
},
});
const senderTooFewBreakingNewsAlarm = new cloudwatch.Alarm(this, 'SenderTooFewBreakingNewsAlarm', {
alarmDescription: `Triggers if the ${id} sender lambda is not frequently invoked for breaking news notification in ${scope.stage}.`,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
evaluationPeriods: 24,
threshold: 0,
metric: breakingNewsCountMetric,
treatMissingData: cloudwatch.TreatMissingData.BREACHING,
actionsEnabled: (scope.stage === 'PROD'),
})
senderTooFewBreakingNewsAlarm.addAlarmAction(snsTopicAction)
senderTooFewBreakingNewsAlarm.addOkAction(snsTopicAction)
}
// this advertises the name of the sender queue to the harvester app
new ssm.StringParameter(this, 'SenderQueueSSMParameter', {
parameterName: `/notifications/${scope.stage}/workers/harvester/${props.paramPrefix}SqsCdkUrl`,
simpleName: false,
stringValue: this.senderSqs.queueUrl,
tier: ssm.ParameterTier.STANDARD,
dataType: ssm.ParameterDataType.TEXT
})
}