{"record":{"id":"5986ee1385a87230","repo":"apache/druid","slug":"segments-to-drop-must-all-be-part-of-the-same-data","errorCode":null,"errorMessage":"Segments to drop must all be part of the same datasource","messagePattern":"Segments to drop must all be part of the same datasource","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java","lineNumber":785,"sourceCode":"  {\n    return markSegments(segmentIds, false, updateTime);\n  }\n\n  /**\n   * Marks the given segments as either used or unused.\n   *\n   * @return the number of segments actually modified.\n   */\n  private int markSegments(final Set<SegmentId> segmentIds, final boolean used, DateTime updateTime)\n  {\n    final String dataSource;\n\n    if (segmentIds.isEmpty()) {\n      return 0;\n    } else {\n      dataSource = segmentIds.iterator().next().getDataSource();\n      if (segmentIds.stream().anyMatch(segment -> !dataSource.equals(segment.getDataSource()))) {\n        throw new IAE(\"Segments to drop must all be part of the same datasource\");\n      }\n    }\n\n    final PreparedBatch batch =\n        handle.prepareBatch(\n            StringUtils.format(\n                \"UPDATE %s SET used = ?, used_status_last_updated = ? WHERE datasource = ? AND id = ?\",\n                dbTables.getSegmentsTable()\n            )\n        );\n\n    for (SegmentId segmentId : segmentIds) {\n      batch.add(used, updateTime.toString(), dataSource, segmentId.toString());\n    }\n\n    final int[] segmentChanges = batch.execute();\n    return computeNumChangedSegments(\n        segmentIds.stream().map(SegmentId::toString).collect(Collectors.toList()),","sourceCodeStart":767,"sourceCodeEnd":803,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java#L767-L803","documentation":"SqlSegmentsMetadataQuery.markSegments() validates that every SegmentId in the passed collection belongs to the same datasource before building the batch SQL for mark-as-used/mark-as-unused. If any segment's datasource differs from the first element's datasource, it throws IllegalArgumentException, because the generated UPDATE statement is scoped to a single datasource.","triggerScenarios":"Passing a mixed collection of SegmentIds from two or more datasources to markSegmentsAsUsed(), markSegmentsAsUnused(), markSegmentAsUsed()/markSegmentsUnused batching paths that route through markSegments().","commonSituations":"Bulk unused/used operations built from a segment list spanning multiple datasources; scripts or tooling that combine segment sets across datasources; a bug in calling code that forgot to group by dataSource before calling the metadata manager.","solutions":["Partition your SegmentIds by dataSource and call markSegments once per datasource.","Filter the input list to a single datasource before invoking markSegmentsAsUsed/markSegmentsAsUnused.","Add an assertion/grouping step in the caller (e.g., Collectors.groupingBy(SegmentId::getDataSource))."],"exampleFix":"// before\nsegmentsMetadataManager.markSegmentsAsUnused(mixedSegments);\n// after\nmixedSegments.stream()\n    .collect(Collectors.groupingBy(SegmentId::getDataSource))\n    .forEach((ds, segs) -> segmentsMetadataManager.markSegmentsAsUnused(segs));","handlingStrategy":"validation","validationCode":"Set<String> datasources = segmentIds.stream()\n    .map(SegmentId::getDataSource)\n    .collect(Collectors.toSet());\nif (datasources.size() > 1) {\n  throw new IllegalArgumentException(\"mixed datasources: \" + datasources);\n}","typeGuard":"boolean sameDatasource(List<SegmentId> ids) {\n  return ids.isEmpty() || ids.stream().map(SegmentId::getDataSource).distinct().count() <= 1;\n}","tryCatchPattern":"try {\n  manager.markSegmentsAsUnused(segmentIds);\n} catch (IllegalArgumentException e) {\n  // group by datasource and retry per datasource\n}","preventionTips":["Group segment IDs by dataSource before any bulk mark operation","Never merge segment lists from different datasources","Add pre-call assertions in batch scripts"],"tags":["validation","metadata","segments"],"backgroundTag":"invalid-argument-value","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"}