{"record":{"id":"65a38bab060db4ad","repo":"apache/druid","slug":"total-merging-levels-already-defined-for-the-merge","errorCode":null,"errorMessage":"Total merging levels already defined for the merge sort.","messagePattern":"Total merging levels already defined for the merge sort\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/SuperSorterProgressTracker.java","lineNumber":91,"sourceCode":"      Collections.emptyMap(),\n      Collections.emptyMap(),\n      SuperSorter.UNKNOWN_TOTAL,\n      true\n  );\n\n  public SuperSorterProgressTracker()\n  {\n    this.levelToMergedBatches = new HashMap<>();\n    this.levelToTotalBatches = new HashMap<>();\n  }\n\n  /**\n   * Set total merging levels for the SuperSorter it is tracking. Can be set only once\n   */\n  public synchronized void setTotalMergingLevels(final int totalMergingLevels)\n  {\n    if (this.totalMergingLevels != SuperSorter.UNKNOWN_LEVEL) {\n      throw new ISE(\"Total merging levels already defined for the merge sort.\");\n    }\n    levelToMergedBatches.keySet().stream().max(Ordering.natural()).ifPresent(max -> {\n      if (max >= totalMergingLevels) {\n        throw new ISE(\n            \"Max level found in levelToMergedBatches is %d (0-indexed). Cannot set totalMergingLevels to %d\",\n            max,\n            totalMergingLevels\n        );\n      }\n    });\n    levelToTotalBatches.keySet().stream().max(Ordering.natural()).ifPresent(max -> {\n      if (max >= totalMergingLevels) {\n        throw new ISE(\n            \"Max level found in levelToTotalBatches is %d (0-indexed). Cannot set totalMergingLevels to %d\",\n            max,\n            totalMergingLevels\n        );\n      }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/SuperSorterProgressTracker.java#L73-L109","documentation":"SuperSorterProgressTracker.setTotalMergingLevels throws this when totalMergingLevels is already set and someone tries to set it again. The tracker treats the total merging level count as a write-once property of the sort, so a second call indicates the sorter is being driven twice or by two different components. It is a synchronization/initialization-order bug indicator.","triggerScenarios":"Calling setTotalMergingLevels twice on the same tracker instance — e.g., both a direct-merger setup and a middle-merger setup path compute and set the level count, or a sorter is reinitialized reusing the old tracker.","commonSituations":"Developers wiring SuperSorter with a shared tracker across two sorting phases; retry logic that re-runs the initialization block; unit tests reusing a tracker fixture without resetting it.","solutions":["Set totalMergingLevels exactly once, before any merge activity; make the setting call idempotent on the caller side (check SuperSorter.UNKNOWN_LEVEL first).","Ensure each SuperSorter instance has its own dedicated SuperSorterProgressTracker; don't share trackers across sorters or retries.","If re-running a sort, construct a fresh tracker instead of reusing the previous instance.","Verify no code path (e.g., a partition-count update) recomputes and re-sets levels mid-sort."],"exampleFix":"// before\ntracker.setTotalMergingLevels(levels); // may run twice\n// after\nif (tracker.getTotalMergingLevels() == SuperSorter.UNKNOWN_LEVEL) {\n  tracker.setTotalMergingLevels(levels);\n}","handlingStrategy":"validation","validationCode":"if (tracker.getTotalMergingLevels() == SuperSorter.UNKNOWN_LEVEL) {\n  tracker.setTotalMergingLevels(levels);\n}","typeGuard":null,"tryCatchPattern":"try {\n  tracker.setTotalMergingLevels(levels);\n} catch (IllegalStateException e) {\n  if (!e.getMessage().contains(\"already defined\")) {\n    throw e;\n  } // already set — acceptable if the value matches\n}","preventionTips":["Give every SuperSorter its own tracker instance.","Create a fresh tracker for each retry or re-run.","Set the level count exactly once, at sort initialization, before any merge activity."],"tags":["internal-state","initialization","write-once"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}