From e99452e00daa3136f8ac5ebf46e73cf4d39d175c Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:55:04 +0200 Subject: [PATCH] feat(server): attach app attributes to application rate-limited metric (#23500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What `MetricsKeys.CommonApiApplicationQueryRateLimited` (`common-api-query/application-rate-limited`) is emitted from the per-application throttler in `common-base-query-runner.service.ts` **without any attributes**. Downstream that means a single undifferentiated counter — there is no way to tell *which* application is hitting `APPLICATION_API_RATE_LIMITING_LIMIT`. This attaches the app dimension: ```ts await this.metricsService.incrementCounterForEvent({ key: MetricsKeys.CommonApiApplicationQueryRateLimited, shouldStoreInCache: false, attributes: { universal_identifier: authContext.application.universalIdentifier, app_name: authContext.application.name, source_type: authContext.application.sourceType, }, }); ``` ## Why these three attributes Same trio already emitted by `application-registration.service.ts`, `application-install.service.ts` and `application-gauge.service.ts`, so per-app API rate limiting joins cleanly against the existing app lifecycle metrics rather than introducing a second naming scheme. ## Notes - **No new data fetching.** `authContext` is already narrowed to `ApplicationWorkspaceAuthContext` by the `isApplicationAuthContext` guard at the top of the method, and the throttler key a few lines above already reads `authContext.application.universalIdentifier`. `name` and `sourceType` are plain columns on `FlatApplication`, present on the same in-memory object. - **Bounded cardinality.** `universalIdentifier` is stable for an application across workspaces (see the unique index on `(universalIdentifier, workspaceId)`), so the label set is bounded by the number of distinct applications, not by installs. - **`source_type` typing.** `ApplicationRegistrationSourceType` is a string enum, assignable to OTel's `AttributeValue`. ## Context The consuming dashboard panels are already merged-pending in `twentyhq/twenty-infra` ([PR #835](https://github.com/twentyhq/twenty-infra/pull/835)) and written with a `coalesce(nullIf(Attributes['app_name'], ''), nullIf(Attributes['universal_identifier'], ''), 'unknown')` fallback, so they render today as a single `unknown` series and start splitting per app automatically once this ships — no dashboard change needed on either side of the deploy. ## Test plan - Behaviour is unchanged: the counter still fires once per `ThrottlerException`, and the error is still rethrown. Only the attribute bag is new. - I did not run the twenty-server test suite or typecheck locally — this was authored against a shallow clone without a monorepo install, so I'm relying on CI for both. --- _Generated by [Claude Code](https://claude.ai/code/session_01VbFtaCS5RkxDhLJApNSteV)_ Review in cubic --- .../common-query-runners/common-base-query-runner.service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts index 0189c6c80f..6ba0750ce6 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts @@ -373,6 +373,11 @@ export abstract class CommonBaseQueryRunnerService< await this.metricsService.incrementCounterForEvent({ key: MetricsKeys.CommonApiApplicationQueryRateLimited, shouldStoreInCache: false, + attributes: { + universal_identifier: authContext.application.universalIdentifier, + app_name: authContext.application.name, + source_type: authContext.application.sourceType, + }, }); }