Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/__tests__/__snapshots__/construct-hub.test.ts.snap

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

1 change: 1 addition & 0 deletions src/__tests__/devapp/__snapshots__/snapshot.test.ts.snap

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

28 changes: 28 additions & 0 deletions src/__tests__/package-sources/npmjs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,34 @@ test('retries laggy packuments through a queue that moves give-ups to a dead-let
});
});

test('every queue the packument processor reads hides messages for longer than the function runs', () => {
const template = synth(new NpmJs());

const [processorId, processor] = Object.entries(
template.findResources('AWS::Lambda::Function', {
Properties: {
Description: Match.stringLikeRegexp(
'Processes packages from the npm registry'
),
},
})
)[0] as [string, any];
const queues = template.findResources('AWS::SQS::Queue');
const mappings = Object.values(
template.findResources('AWS::Lambda::EventSourceMapping', {
Properties: { FunctionName: { Ref: processorId } },
})
) as any[];

expect(mappings).toHaveLength(2);
for (const mapping of mappings) {
const queueId = mapping.Properties.EventSourceArn['Fn::GetAtt'][0];
// Unset means the SQS default of 30 seconds.
const visibility = queues[queueId].Properties.VisibilityTimeout ?? 30;
expect(visibility).toBeGreaterThanOrEqual(processor.Properties.Timeout);
}
});

test('the billing mode of the known versions table is configurable', () => {
const template = synth(
new NpmJs({
Expand Down
18 changes: 15 additions & 3 deletions src/package-sources/npmjs/packument-queue-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ import { PackumentProcessor } from './packument-processor';
import { IDenyList } from '../../backend/deny-list/api';
import { ILicenseList } from '../../backend/license-list/api';

/** The timeout of the packument processor function. */
const FUNCTION_TIMEOUT = Duration.minutes(5);

/**
* The visibility timeout of both queues, which the function reads. Lambda
* requires at least the function timeout; AWS recommends 6x.
*/
const QUEUE_VISIBILITY_TIMEOUT = Duration.minutes(
6 * FUNCTION_TIMEOUT.toMinutes()
);

export interface PackumentQueueProcessorProps {
/** The table recording the package versions that were processed. */
readonly knownVersions: ITableV2;
Expand Down Expand Up @@ -54,13 +65,14 @@ export class PackumentQueueProcessor extends Construct {
this.deadLetterQueue = new Queue(this, 'DLQ', {
encryption: QueueEncryption.KMS_MANAGED,
retentionPeriod: Duration.days(14),
visibilityTimeout: QUEUE_VISIBILITY_TIMEOUT,
});
this.queue = new Queue(this, 'Queue', {
encryption: QueueEncryption.KMS_MANAGED,
retentionPeriod: Duration.days(14),
// Only used when the processor cannot schedule the next attempt itself,
// for example on a timeout. AWS recommends 6x the function timeout.
visibilityTimeout: Duration.minutes(30),
// for example on a timeout.
visibilityTimeout: QUEUE_VISIBILITY_TIMEOUT,
deadLetterQueue: {
queue: this.deadLetterQueue,
maxReceiveCount: PACKUMENT_MAX_ATTEMPTS,
Expand All @@ -77,7 +89,7 @@ export class PackumentQueueProcessor extends Construct {
[ENV_PACKUMENT_DEAD_LETTER_QUEUE_ARN]: this.deadLetterQueue.queueArn,
},
memorySize: 2_048,
timeout: Duration.minutes(5),
timeout: FUNCTION_TIMEOUT,
tracing: Tracing.ACTIVE,
});
this.function.addEventSource(
Expand Down
Loading