Files
twenty/packages/twenty-server
Paul Rastoin dc8c62a7a9 Add OpenTelemetry metrics to workspace migration build and run (#23797)
## Context

## AI generated graph goal example
<img width="2078" height="914" alt="image"
src="https://github.com/user-attachments/assets/14c760df-5ac8-4526-a5e0-40b57344be90"
/>
<img width="2158" height="1850" alt="image"
src="https://github.com/user-attachments/assets/2324779e-9ab8-47fc-a014-78269a78823e"
/>


The workspace migration builder and runner already log phase timings via
`logger.perfTime`, but those logs are only useful for local debugging:
they're gated behind the `performance` log level and never leave the
process. This PR routes the same timings into the existing
`MetricsService` (OpenTelemetry) so migration performance can be tracked
over time and regressions detected.

## What's recorded

All metrics are histograms recorded via
`MetricsService.recordHistogram`, which is a synchronous in-memory
aggregation - export happens on the background OTel readers (Prometheus
scrape or periodic OTLP push), so nothing is added to the migration hot
path. When `METER_DRIVER` is unset, records are no-ops.

| Metric | Where | Attributes |
|---|---|---|
| `workspace-migration/build-duration-ms` |
`WorkspaceMigrationValidateBuildAndRunService` | `status`: `success`,
`fail` (validation errors), `error` (builder threw) |
| `workspace-migration/build-entity-duration-ms` |
`WorkspaceEntityMigrationBuilderService.validateAndBuild` |
`metadataName`, `status` |
| `workspace-migration/build-entity-phase-duration-ms` | entity builder
| `metadataName`, `phase`: `matrix-computation`, `deletion-validation`,
`creation-validation`, `update-validation` |
| `workspace-migration/run-duration-ms` |
`WorkspaceMigrationRunnerService.run` | `status`: `success`, `fail` -
recorded on every exit path, including pre-transaction failures (DDL
locked, cache retrieval, application not found) |
| `workspace-migration/run-phase-duration-ms` | runner | `phase`:
`initial-cache-retrieval`, `action-execution`, `commit`,
`cache-invalidation`; `status` - on failure the elapsed transaction time
is recorded as `action-execution` with `status: 'fail'` (the
action/commit split is unknowable mid-failure) |
| `workspace-migration/action-duration-ms` | base action handler wrapper
| `actionType`, `metadataName`, `step`, `status` - failed steps are
recorded via try/finally |
| `workspace-migration/action-count` | validate-build-and-run service |
- |

Phase notes: phases are disjoint (commit is subtracted from transaction
time) but not exhaustive - connection setup, the application-map fetch,
rollback, and after-commit side effects sit outside them, so the stack
approximates rather than equals the run total. The `cache-invalidation`
phase is recorded only at the run's post-commit call site, not inside
the public `invalidateCache`, so standalone callers (upgrade backfill
commands, `FlatCacheInvalidateCommand`) don't pollute the series.

## Implementation notes

- Durations are measured with local `performance.now()` instead of
reusing `perfTime`/`perfTimeEnd`, so metrics are recorded regardless of
the `performance` log level and are immune to key collisions in the
logger's shared timer map under concurrent migrations. Existing perf
logs are untouched.
- `WorkspaceMigrationRunnerService.run` is now a thin wrapper around the
previous body (`executeRun`) so the total run duration and its
success/fail status are recorded in one place for every throw path.
- No `workspaceId` in attributes to keep cardinality bounded.
- Two new bucket-boundary constants follow the existing
`AI_LATENCY_MS_BUCKET_BOUNDARIES` pattern: durations 5ms-120s, action
counts 1-5000.
- `MetricsModule` imported into `WorkspaceMigrationModule`,
`WorkspaceMigrationRunnerModule`, `WorkspaceMigrationBuilderModule`, and
the action-handlers module.

## Test

- `nx typecheck twenty-server` passes
- oxlint + oxfmt clean on changed files
- `workspace.service.spec.ts` passes
2026-08-05 16:40:32 +00:00
..