Skip to content

fix: collect metrics to help analyze performance problems#1124

Merged
rix0rrr merged 24 commits intomainfrom
huijbers/measure-more-perf
Mar 17, 2026
Merged

fix: collect metrics to help analyze performance problems#1124
rix0rrr merged 24 commits intomainfrom
huijbers/measure-more-perf

Conversation

@rix0rrr
Copy link
Contributor

@rix0rrr rix0rrr commented Feb 9, 2026

This PR changes telemetry to help diagnose and address performance problems.

The effective changes are as follows:

  • We are seeing slow synth times.
    • Log the total number of assemblies and stacks to correlate and segment synth times and stack counts (is the relationship linear/nonlinear, are slow synth times due to huge applications, ...)
    • Log the (guessed) implementation language, to see if synth times behave differently between languages, if startup times make a lot of difference, etc.
  • We are concerned about the time needed for agents to produce a successful CDK application.
    • Try to determine whether we are currently being run by an agent so we can segment on that.
    • Log the counts of warnings and errors, to see if deployment failure is or can be preceded/prevented by better error reporting at synth time.
  • The library version we were collecting was a rough guess, do a better one once we have accurate information.
  • Count the number of resources in a stack to correlate and segment deployment times by resource count.

For consistency, the same counters changes are being made to toolkit-lib, even though those counters aren't being consumed by anything.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

In multiple places the `SYNTH` span doesn't measure the time to
synthesize the assembly, but rather the time to select stacks from an
already-synthesized assembly.

Presumably this was done in order to avoid duplicating this line:

```ts
    const selectStacks = options.stacks ?? ALL_STACKS;
```

Instead, introduce a helper function that gets called from all places,
and centralize the `selectStacks` logic so we don't fear duplication
anymore.
@rix0rrr rix0rrr force-pushed the huijbers/measure-more-perf branch from d141182 to 9fa8629 Compare February 9, 2026 09:57
@github-actions github-actions bot added the p2 label Feb 9, 2026
@aws-cdk-automation aws-cdk-automation requested a review from a team February 9, 2026 09:57
@rix0rrr rix0rrr changed the title fix: collect metrics to help analyze performance impact fix: collect metrics to help analyze performance problems Feb 9, 2026
@rix0rrr rix0rrr force-pushed the huijbers/measure-more-perf branch from 9fa8629 to 671ef9f Compare February 9, 2026 09:58
@rix0rrr rix0rrr force-pushed the huijbers/measure-more-perf branch from 671ef9f to 063862f Compare February 9, 2026 09:59
mrgrain pushed a commit to awsdocs/aws-cdk-guide that referenced this pull request Mar 6, 2026
Changes are proposed in aws/aws-cdk-cli#1124.

We could have sent this data as simple "counters" and not have had to
change this page, but instead we are more obvious about it and send them
as independent fields that we document.

cr: https://code.amazon.com/reviews/CR-252851509

function mutable<A extends object>(x: A): { -readonly [k in keyof A]: A[k] } {
return x;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we gasp playing god here and turning immutable objects mutable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only ever seen this as re-defining the entire variable

await this.notify(this.definition.end.msg(
endMsg, {
duration: duration.asMs,
...(Object.keys(this.counters).length > 0 ? { counters: this.counters } : {}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, doesn't matter: is there a reason you didn't want to send counters: {}?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I like sending as little as possible.

*/
type SpanEndArguments<T> = keyof T extends keyof SpanEnd
? (Pick<Partial<SpanEnd>, keyof T & keyof SpanEnd> | void)
: Optional<T, keyof T & keyof SpanEnd>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i read your comments but this was still greek to me. This is what Kiro said, if true, it was a lot simpler to understand:

In short: if the span type already covers everything SpanEnd needs, ending is 
no-arg. If the span type has extra fields beyond SpanEnd, you're 
forced to supply them when calling end().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kiro is not entirely correct. You can still, but don't have to override the implicit values with optionals.

Comment on lines +5 to +6
* It's hard for us to say `false` for sure, so we only respond
* with `yes` or `don't know`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard for us to say 'yes' for sure too, since anyone can add an env variable like this.

Copy link
Contributor Author

@rix0rrr rix0rrr Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. That's why the name is guess

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants