{"record":{"id":"839cbd243c16dceb","repo":"neondatabase/neon","slug":"can-t-change-stripe-size-from-to","errorCode":null,"errorMessage":"can't change stripe size from {} to {}","messagePattern":"can't change stripe size from (.+?) to (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pageserver/client_grpc/src/client.rs","lineNumber":141,"sourceCode":"    ///\n    /// TODO: verify that in-flight requests are allowed to complete, and that the old pools are\n    /// properly spun down and dropped afterwards.\n    pub fn update_shards(&self, shard_spec: ShardSpec) -> anyhow::Result<()> {\n        // Validate the shard spec. We should really use `ArcSwap::rcu` for this, to avoid races\n        // with concurrent updates, but that involves creating a new `Shards` on every attempt,\n        // which spins up a bunch of Tokio tasks and such. These should already be checked elsewhere\n        // in the stack, and if they're violated then we already have problems elsewhere, so a\n        // best-effort but possibly-racy check is okay here.\n        let old = self.shards.load_full();\n        if shard_spec.count < old.count {\n            return Err(anyhow!(\n                \"can't reduce shard count from {} to {}\",\n                old.count,\n                shard_spec.count\n            ));\n        }\n        if !old.count.is_unsharded() && shard_spec.stripe_size != old.stripe_size {\n            return Err(anyhow!(\n                \"can't change stripe size from {} to {}\",\n                old.stripe_size.expect(\"always Some when sharded\"),\n                shard_spec.stripe_size.expect(\"always Some when sharded\")\n            ));\n        }\n\n        let shards = Shards::new(\n            self.tenant_id,\n            self.timeline_id,\n            shard_spec,\n            self.auth_token.clone(),\n            self.compression,\n        )?;\n        self.shards.store(Arc::new(shards));\n        Ok(())\n    }\n\n    /// Returns the total size of a database, as # of bytes.","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/client_grpc/src/client.rs#L123-L159","documentation":"In `update_shards`, once a tenant is sharded its stripe size must stay constant across updates: the stripe size determines which shard owns each key, so changing it would silently remap every key. The check applies only when the current count is sharded (unsharded tenants may gain a stripe size when first split).","triggerScenarios":"Calling `update_shards` on an already-sharded tenant with a `ShardSpec` whose `stripe_size` differs from the currently loaded one — e.g. the controller's shard map was regenerated with a different stripe_size setting.","commonSituations":"Changing the sharding stripe-size configuration after tenants were sharded; controller state drift between the spec used at split time and a later reconciliation; merging specs from different sources.","solutions":["Pass the same stripe_size the tenant was originally sharded with (read it from the current shard map)","If a different stripe size is truly required, that requires a new tenant/topology — not an in-place update","Audit where shard_spec.stripe_size is computed and pin it to the controller's canonical value","Add a precondition assertion in the caller comparing old and new specs before calling update_shards"],"exampleFix":"// before: sharded tenant (stripe 32768) updated with a different stripe\nclient.update_shards(ShardSpec::new(urls, Some(ShardStripeSize(16384)))?)?; // can't change stripe size\n\n// after: keep the original stripe size\nclient.update_shards(ShardSpec::new(urls, Some(current_stripe_size))?)?;","handlingStrategy":"validation","validationCode":"let old = client.shards();\nif !old.count.is_unsharded() && shard_spec.stripe_size != old.stripe_size {\n    anyhow::bail!(\n        \"stripe size is immutable once sharded (current {:?}, requested {:?})\",\n        old.stripe_size, shard_spec.stripe_size\n    );\n}\nclient.update_shards(shard_spec)?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Persist the stripe size used at split time and source all later specs from it","Treat stripe-size changes as a new-topology operation, never an in-place update","Assert old/new spec equality (except count growth) in controller reconciliation"],"tags":["rust","neon","sharding","pageserver","grpc-client"],"backgroundTag":"invalid-shard-configuration","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}