{"record":{"id":"acc9f5fb526417c8","repo":"apache/seatunnel","slug":"metrics-snapshots-are-updated-through-merge-semant","errorCode":null,"errorMessage":"Metrics snapshots are updated through merge semantics rather than putIfAbsent.","messagePattern":"Metrics snapshots are updated through merge semantics rather than putIfAbsent\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/common/statestore/metrics/MetricsSnapshotStateStore.java","lineNumber":85,"sourceCode":"     * @return {@code true} if a snapshot exists for the task\n     */\n    @Override\n    default boolean containsKey(TaskLocation taskLocation) {\n        return get(taskLocation) != null;\n    }\n\n    /**\n     * Conditional insertion is intentionally not exposed for metrics snapshots because the current\n     * engine model treats them as latest-snapshot overwrites.\n     *\n     * @param taskLocation task location to store\n     * @param metricsContext metrics snapshot to store\n     * @return never returns normally\n     */\n    @Override\n    default SeaTunnelMetricsContext putIfAbsent(\n            TaskLocation taskLocation, SeaTunnelMetricsContext metricsContext) {\n        throw new UnsupportedOperationException(\n                \"Metrics snapshots are updated through merge semantics rather than putIfAbsent.\");\n    }\n\n    /**\n     * Removes all task metrics belonging to a specific pipeline.\n     *\n     * @param pipelineLocation pipeline location to remove\n     */\n    void removePipeline(PipelineLocation pipelineLocation);\n\n    /**\n     * Checks whether any task snapshot exists for a specific pipeline.\n     *\n     * @param pipelineLocation pipeline location to check\n     * @return {@code true} if any snapshot exists for the pipeline\n     */\n    boolean containsPipeline(PipelineLocation pipelineLocation);\n","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/common/statestore/metrics/MetricsSnapshotStateStore.java#L67-L103","documentation":"MetricsSnapshotStateStore.putIfAbsent is explicitly unsupported: metrics snapshots are maintained exclusively through merge semantics, so calling putIfAbsent throws UnsupportedOperationException. The interface contract forces callers to use the merge-based update path so concurrent task metric contexts combine correctly instead of first-writer-wins.","triggerScenarios":"Calling putIfAbsent(taskLocation, metricsContext) on the metrics snapshot state store — typically code written against the generic StateStore interface assuming putIfAbsent semantics apply to metrics contexts.","commonSituations":"Custom metrics reporting or plugin code reusing the generic state-store API for metrics; porting code from other state stores where putIfAbsent is valid; refactoring that routed metric updates through the wrong method.","solutions":["Replace putIfAbsent calls with the merge-based update method (merge) provided by MetricsSnapshotStateStore","If you need create-or-get semantics, perform the merge with your initial context — merge handles first-write and updates uniformly","Audit code paths that handle SeaTunnelMetricsContext and ensure none call the generic putIfAbsent","If you control the interface, consider whether putIfAbsent should delegate to merge to avoid foot-guns"],"exampleFix":"// before\nstore.putIfAbsent(taskLocation, metricsContext);\n\n// after\nstore.merge(taskLocation, metricsContext); // merge semantics combine snapshots","handlingStrategy":"validation","validationCode":"if (store instanceof MetricsSnapshotStateStore) {\n    // must use merge, not putIfAbsent\n}","typeGuard":"void safeMetricsUpdate(StateStore store, TaskLocation loc, SeaTunnelMetricsContext ctx) {\n    if (store instanceof MetricsSnapshotStateStore) {\n        ((MetricsSnapshotStateStore) store).merge(loc, ctx);\n    }\n}","tryCatchPattern":"try {\n    store.putIfAbsent(loc, ctx);\n} catch (UnsupportedOperationException e) {\n    store.merge(loc, ctx); // fall back to required merge semantics\n}","preventionTips":["Always update SeaTunnelMetricsContext via merge","Don't reuse generic state-store mutation helpers for metrics","Read the MetricsSnapshotStateStore javadoc before extending callers"],"tags":["zeta-engine","metrics","state-store","unsupported-operation","api-misuse"],"backgroundTag":"unsupported-operation","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}