{"record":{"id":"63c1d176cdea9531","repo":"thanos-io/thanos","slug":"shuffle-sharding-requires-ketama-hashring-as-base","errorCode":null,"errorMessage":"shuffle sharding requires ketama hashring as base ring","messagePattern":"shuffle sharding requires ketama hashring as base ring","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/receive/hashring.go","lineNumber":603,"sourceCode":"\t}\n\n\th, err := s.getTenantShard(tenant)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts.metrics.numItems.Inc()\n\ts.cache.Add(tenant, h)\n\n\treturn h, nil\n}\n\n// getTenantShard returns a consistent subset of nodes for a tenant using\n// Cortex-style consistent hashing.\nfunc (s *shuffleShardHashring) getTenantShard(tenant string) (*ketamaHashring, error) {\n\tbaseRing, ok := s.baseRing.(*ketamaHashring)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"shuffle sharding requires ketama hashring as base ring\")\n\t}\n\n\tnodes := s.Nodes()\n\tnodesByAZ := make(map[string][]Endpoint)\n\tfor _, node := range nodes {\n\t\tvar az = node.AZ\n\t\tif s.shuffleShardingConfig.ZoneAwarenessDisabled {\n\t\t\taz = \"\"\n\t\t}\n\t\tnodesByAZ[az] = append(nodesByAZ[az], node)\n\t}\n\n\tsectionsByAZ := make(map[string]sections)\n\tfor _, sec := range baseRing.sections {\n\t\tendpoint := baseRing.endpoints[sec.endpointIndex]\n\t\tvar az = endpoint.AZ\n\t\tif s.shuffleShardingConfig.ZoneAwarenessDisabled {\n\t\t\taz = \"\"","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/receive/hashring.go#L585-L621","documentation":"shuffleShardHashring.getTenantShard asserts its base ring is a *ketamaHashring; when the underlying implementation is anything else (e.g. a simple hashring) this error is returned. Shuffle sharding is only implemented on top of the ketama consistent-hashing ring, so this is a programming/configuration invariant violation.","triggerScenarios":"Constructing a shuffle-shard hashring with a base ring that is not a ketama hashring — e.g. wiring a simple hashring as the base, or a code path that built the ring with the wrong algorithm before wrapping it with newShuffleShardHashring.","commonSituations":"Custom code embedding the hashring package passes a newSimpleHashring where ketama is required; refactors change the ring type but not the shuffle-shard wrapper.","solutions":["Build the base ring with newKetamaHashring before wrapping it with newShuffleShardHashring","Use the Ketama algorithm in config when shuffleSharding.shardSize > 0 (the hashmod path already rejects it)","Audit custom code that constructs hashrings directly and fix the type passed as baseRing"],"exampleFix":"// before\nbase := newSimpleHashring(endpoints)\nshard := newShuffleShardHashring(base, cfg, rf, reg, name)\n// after\nbase := newKetamaHashring(endpoints, SectionsPerNode, replicationFactor)\nshard := newShuffleShardHashring(base, cfg, rf, reg, name)","handlingStrategy":"type-guard","validationCode":"if _, ok := baseRing.(*ketamaHashring); !ok {\n    return fmt.Errorf(\"shuffle sharding requires a ketama base ring\")\n}","typeGuard":"base, ok := ring.(*ketamaHashring)\nif !ok {\n    return nil, errors.New(\"shuffle sharding requires ketama hashring as base ring\")\n}","tryCatchPattern":"shard, err := ssRing.GetTenantShard(tenant)\nif err != nil {\n    return Endpoint{}, fmt.Errorf(\"tenant shard unavailable: %w\", err)\n}","preventionTips":["Only wrap ketama rings with newShuffleShardHashring","Route through newHashringConfig instead of constructing rings manually"],"tags":["hashring","shuffle-sharding","internal"],"backgroundTag":"incompatible-source-type","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}