{"record":{"id":"b38065b2da14933e","repo":"apache/pulsar","slug":"synthetic-layout-missing-segment-id-partition","errorCode":null,"errorMessage":"Synthetic layout missing segment_id= + partition +  (N= + n + )","messagePattern":"Synthetic layout missing segment_id= \\+ partition \\+  \\(N= \\+ n \\+ \\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/SegmentRouter.java","lineNumber":103,"sourceCode":"            }\n        }\n        return true;\n    }\n\n    /**\n     * Mod-N routing over {@code segment_id}, matching v4 partitioned-topic routing\n     * ({@code signSafeMod(murmurHash3_32(key), N)}).\n     */\n    private static long routeModN(String key, List<ActiveSegment> activeSegments) {\n        int hash32 = Murmur3_32Hash.getInstance().makeHash(key.getBytes(StandardCharsets.UTF_8));\n        int n = activeSegments.size();\n        int partition = signSafeMod(hash32, n);\n        for (var segment : activeSegments) {\n            if (segment.segmentId() == partition) {\n                return segment.segmentId();\n            }\n        }\n        throw new IllegalStateException(\n                \"Synthetic layout missing segment_id=\" + partition + \" (N=\" + n + \")\");\n    }\n\n    private static int signSafeMod(int dividend, int divisor) {\n        int mod = dividend % divisor;\n        return mod < 0 ? mod + divisor : mod;\n    }\n\n    /**\n     * The raw 32-bit {@code Murmur3_32} hash of a key. Its two 16-bit halves drive the two independent\n     * rings — high half → segment routing ({@link #segmentHash}), low half → entry-bucketing\n     * ({@link #entryBucketHash}, PIP-486). Compute this <b>once</b> per key and split it, rather than\n     * hashing the key twice.\n     *\n     * <p>The <i>raw</i> (unmasked) hash is required so the high half is full-range: {@code makeHash}\n     * clears bit 31, which would confine the high half to {@code [0, 0x7FFF]}.\n     */\n    static int murmur(byte[] keyBytes) {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/SegmentRouter.java#L85-L121","documentation":"SegmentRouter.routeModN maps a message key hash onto an active segment via a sign-safe modulo of the hash by the number of segments (N). This IllegalStateException is thrown when the computed partition index does not match any currently active segment, meaning the in-memory synthetic layout has drifted from the expected 0..N-1 segment id range. The library throws it rather than silently dropping or misrouting the message.","triggerScenarios":"Calling route() (which delegates to routeModN) when activeSegments does not contain a segment whose segmentId() equals signSafeMod(hash32, n) — e.g. segments were closed/removed, reordered, or the active segment list is sparse while n still counts all segments.","commonSituations":"A segment was closed or failed while routing continued with a stale count of active segments; concurrent layout changes (segment add/remove) racing with route; constructing the router with non-contiguous segment ids; rebalancing/reshuffling the synthetic layout mid-flight.","solutions":["Rebuild or refresh the router's activeSegments list so it is contiguous and consistent with the value of n used for the modulo.","Route under the same lock/consistency point that mutates activeSegments so segment removal cannot race with routing.","Verify segment ids cover 0..N-1 at construction; fail fast or recompute n from activeSegments.size() instead of a stale count.","Catch IllegalStateException in route() callers and retry against a refreshed router."],"exampleFix":"// before\nint n = configuredSegmentCount;\nint partition = signSafeMod(hash32, n);\nfor (var segment : activeSegments) {\n    if (segment.segmentId() == partition) {\n        return segment.segmentId();\n    }\n}\nthrow new IllegalStateException(\"Synthetic layout missing segment_id=\" + partition + \" (N=\" + n + \")\");\n// after\nvar snapshot = new ArrayList<>(activeSegments); // consistent view, taken under lock\nint n = snapshot.size();\nint partition = signSafeMod(hash32, n);\nreturn snapshot.stream()\n    .filter(s -> s.segmentId() == partition)\n    .findFirst()\n    .map(Segment::segmentId)\n    .orElseGet(() -> snapshot.get(Math.floorMod(partition, snapshot.size())).segmentId());","handlingStrategy":"validation","validationCode":"if (activeSegments.size() != n || activeSegments.stream().mapToInt(Segment::segmentId).distinct().count() != n) {\n    throw new IllegalStateException(\"segment layout not contiguous for N=\" + n);\n}","typeGuard":null,"tryCatchPattern":"try { return router.route(key); } catch (IllegalStateException e) { router.refreshLayout(); return router.route(key); }","preventionTips":["Derive n from activeSegments.size() rather than a separate configured count","Mutate activeSegments and route under the same lock or use an immutable snapshot reference","Assert segment ids are exactly 0..N-1 when building the router","Add a self-check at router construction that maps sample hashes to valid segments"],"tags":["routing","illegal-state","segment-layout","concurrency"],"backgroundTag":"partition-not-found","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}