发布

  • [OPIK-5890] [BE][FE] feat: add optimization flow analytics events (#6356)

    frostbyte_neo 发布于 2026-04-20 13:41:28 +00:00

    • [OPIK-5890] [BE][FE] feat: add optimization flow analytics events

    Add PostHog instrumentation for the Optimization flow to track launch
    metrics defined in epic OPIK-5245.

    Backend (2 events via AnalyticsService in OptimizationService):

    • opik_optimization_created: fires on upsert, captures UI + SDK paths
    • opik_optimization_completed: fires on terminal status transition,
      includes num_trials, baseline/best scores for improvement delta

    Frontend (1 event via trackEvent):

    • opik_optimization_wizard_started: fires on wizard page mount (v2 only)

    Implements OPIK-5890: Optimization Flow Analytics Events

    • fix(analytics): guard created event for new optimizations only, add opik_ prefix
    • Guard trackOptimizationCreated with existingOpt.isEmpty() to prevent
      double-counting on re-saves/upserts of existing optimizations
    • Add explicit opik_ prefix to event names matching convention from
      sibling PRs (OPIK-5691, OPIK-5692)
    • fix(analytics): match new trackEvent signature, guard completed event on transition, thread user identity

    Addresses review on PR #6356:

    • Reorder trackOptimizationCreated args to (eventType, properties, identity) after #6362
    • Only emit opik_optimization_completed on the transition into a terminal state
    • Thread userName through trackOptimizationCompleted to preserve attribution on scheduler threads; fall back to resolveIdentity() on the internal cancelOptimization() path where USER_NAME is absent
    • fix(analytics): re-fetch optimization post-update so completed event carries fresh metrics

    Addresses review on PR #6356 — numTrials, baselineObjectiveScore, and bestObjectiveScore
    are computed by query-time aggregation over trials/scores; the pre-update snapshot can be
    stale versus the state observed right after the terminal status write. Re-read the row
    after the update and emit the event from the refreshed entity.

    • fix(analytics): offload optimization track calls to boundedElastic

    Per PR #6356 review — trackOptimizationCreated/Completed fire from reactive
    doOnSuccess/doOnNext callbacks. AnalyticsService.resolveIdentity() falls back to
    usageReportService.getAnonymousId(), a synchronous JDBC read, when identity is null
    (reachable via the internal cancelOptimization() path). Wrap with
    Schedulers.boundedElastic().schedule(...) so a blocking fallback can't stall the
    reactive thread.

    • refactor(analytics): inline track calls and drop post-update re-fetch

    Addresses andrescrz on PR #6356:

    • Drop the optimizationDAO.getById re-fetch after update — ClickHouse replication means the read can land on a replica that hasn't seen the write yet, producing stale data or a spurious NotFound. Use the pre-update snapshot; analytics metric drift here is acceptable and doesn't justify the extra latency or error surface on the request path.
    • Inline trackOptimizationCreated/trackOptimizationCompleted at the two call sites and remove the helpers — they no longer add value over a direct analyticsService.trackEvent call.
    • docs(skill): codify backend analytics patterns surfaced in PR #6356 review
    • Document both trackEvent overloads and when to pick the 3-arg identity variant
    • Add canonical reactive-chain recipe: Schedulers.boundedElastic() wrap + Mono.deferContextual identity capture, with the "why" for each
    • Call out the getOrDefault(USER_NAME, null) fallback for internal, system-triggered paths
    • Add a Don'ts list: no try/catch, no trivial helper delegates, no ClickHouse read-after-write for analytics, no emission unit tests, don't trust the non-blocking contract
    下载附件