{"record":{"id":"438059155e2e5ca4","repo":"microsoft/FASTER","slug":"invalid-number-of-chunks","errorCode":null,"errorMessage":"Invalid number of chunks: ","messagePattern":"Invalid number of chunks: ","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Index/FASTER/Implementation/SplitIndex.cs","lineNumber":24,"sourceCode":"namespace FASTER.core\n{\n    public unsafe partial class FasterKV<Key, Value> : FasterBase, IFasterKV<Key, Value>\n    {\n        private void SplitBuckets(long hash)\n        {\n            long masked_bucket_index = hash & state[1 - resizeInfo.version].size_mask;\n            int offset = (int)(masked_bucket_index >> Constants.kSizeofChunkBits);\n            SplitBuckets(offset);\n        }\n\n        private void SplitBuckets(int offset)\n        {\n            int numChunks = (int)(state[1 - resizeInfo.version].size / Constants.kSizeofChunk);\n            if (numChunks == 0) numChunks = 1; // at least one chunk\n\n            if (!Utility.IsPowerOfTwo(numChunks))\n            {\n                throw new FasterException(\"Invalid number of chunks: \" + numChunks);\n            }\n            for (int i = offset; i < offset + numChunks; i++)\n            {\n                if (0 == Interlocked.CompareExchange(ref splitStatus[i & (numChunks - 1)], 1, 0))\n                {\n                    long chunkSize = state[1 - resizeInfo.version].size / numChunks;\n                    long ptr = chunkSize * (i & (numChunks - 1));\n\n                    HashBucket* src_start = state[1 - resizeInfo.version].tableAligned + ptr;\n                    HashBucket* dest_start0 = state[resizeInfo.version].tableAligned + ptr;\n                    HashBucket* dest_start1 = state[resizeInfo.version].tableAligned + state[1 - resizeInfo.version].size + ptr;\n\n                    SplitChunk(src_start, dest_start0, dest_start1, chunkSize);\n\n                    // split for chunk is done\n                    splitStatus[i & (numChunks - 1)] = 2;\n\n                    if (Interlocked.Decrement(ref numPendingChunksToBeSplit) == 0)","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Index/FASTER/Implementation/SplitIndex.cs#L6-L42","documentation":"During a checkpoint-driven resize, SplitIndex.SplitBuckets divides the new table size into chunks (Constants.kSizeofChunk each); the resulting chunk count must be a power of two so buckets map to chunks via masking. If numChunks is not a power of two, FASTER throws FasterException because the split would be unbalanced/incorrect. This is an invariant over the configured table size and chunk constant.","triggerScenarios":"Calling Checkpoint/FullCheckpoint/StartCheckpoint (which triggers SplitBuckets) with an index size such that (newSize / kSizeofChunk) is not a power of two — e.g. odd or non-power-of-2 chunk counts produced by unusual index sizes interacting with the chunk constant.","commonSituations":"Configuring FasterKV with an index size that passes constructor checks but yields a non-power-of-2 chunk count at resize/checkpoint time; mismatched Constants.kSizeofChunk in custom builds.","solutions":["Use an index size where size / kSizeofChunk is a power of 2 (typically sizes of kSizeofChunk * 2^n); log the computed numChunks to verify.","Round the configured index size up to the nearest valid chunked size before constructing the store.","If using a modified Constants.kSizeofChunk, ensure it keeps size/numChunks a power of two, or file a FASTER bug with the size."],"exampleFix":"// before\nvar store = new FasterKV<long, long>(indexSize: 1 << 20 + 1); // arbitrary size\nawait store.TakeFullCheckpointAsync(token);\n// after\nlong indexSize = Utility.NextPowerOf2(1 << 21); // keep size/kSizeofChunk a power of 2\nvar store = new FasterKV<long, long>(indexSize);\nawait store.TakeFullCheckpointAsync(token);","handlingStrategy":"validation","validationCode":"long indexSize = /* configured */;\nlong numChunks = Math.Max(1, indexSize / Constants.kSizeofChunk);\nif (!Utility.IsPowerOfTwo(numChunks))\n    indexSize = (long)Constants.kSizeofChunk * Utility.NextPowerOf2(numChunks);\nvar store = new FasterKV<long, long>(indexSize);","typeGuard":"bool SupportsCheckpointSplit(long indexSize) => Utility.IsPowerOfTwo(Math.Max(1, indexSize / Constants.kSizeofChunk));","tryCatchPattern":"try { await store.TakeFullCheckpointAsync(token); }\ncatch (FasterException ex) when (ex.Message.StartsWith(\"Invalid number of chunks\"))\n{ logger.LogCritical(ex, \"Index size yields non-power-of-2 chunk count\"); throw; }","preventionTips":["Choose index sizes that are kSizeofChunk * 2^n in configuration templates.","Add a startup validation that checks numChunks is a power of two before the first checkpoint.","Avoid modifying Constants.kSizeofChunk in custom builds without re-validating sizes."],"tags":["faster","checkpoint","resize","configuration","csharp"],"backgroundTag":"invalid-config-value","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}