{"record":{"id":"43bd17ade33e7460","repo":"microsoft/FASTER","slug":"cannot-use-growindex-when-using-non-async-sessions","errorCode":null,"errorMessage":"Cannot use GrowIndex when using non-async sessions","messagePattern":"Cannot use GrowIndex when using non-async sessions","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Index/FASTER/FASTER.cs","lineNumber":727,"sourceCode":"                internalStatus = InternalDelete(ref key, keyHash, ref context, ref pcontext, fasterSession, serialNo);\n            while (HandleImmediateRetryStatus(internalStatus, fasterSession, ref pcontext));\n\n            var status = HandleOperationStatus(fasterSession.Ctx, ref pcontext, internalStatus);\n\n            Debug.Assert(serialNo >= fasterSession.Ctx.serialNum, \"Operation serial numbers must be non-decreasing\");\n            fasterSession.Ctx.serialNum = serialNo;\n            return status;\n        }\n\n        /// <summary>\n        /// Grow the hash index by a factor of two. Make sure to take a full checkpoint\n        /// after growth, for persistence.\n        /// </summary>\n        /// <returns>Whether the grow completed</returns>\n        public bool GrowIndex()\n        {\n            if (epoch.ThisInstanceProtected())\n                throw new FasterException(\"Cannot use GrowIndex when using non-async sessions\");\n\n            if (!StartStateMachine(new IndexResizeStateMachine())) return false;\n\n            epoch.Resume();\n\n            try\n            {\n                while (true)\n                {\n                    SystemState _systemState = SystemState.Copy(ref systemState);\n                    if (_systemState.Phase == Phase.PREPARE_GROW)\n                        ThreadStateMachineStep<Empty, Empty, Empty, NullFasterSession>(null, NullFasterSession.Instance, default);\n                    else if (_systemState.Phase == Phase.IN_PROGRESS_GROW)\n                        SplitBuckets(0);\n                    else if (_systemState.Phase == Phase.REST)\n                        break;\n                    epoch.ProtectAndDrain();\n                    Thread.Yield();","sourceCodeStart":709,"sourceCodeEnd":745,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Index/FASTER/FASTER.cs#L709-L745","documentation":"GrowIndex resizes the hash index via a state machine that must not run on an epoch-protected thread; otherwise the epoch system cannot advance threads during the resize. If the current thread holds epoch protection (non-async/blocking session usage), the call throws instead of proceeding.","triggerScenarios":"Calling GrowIndex from a thread that currently has epoch protection active (e.g., inside a synchronous session operation, within epoch-protected regions, or a caller that manually entered epoch protection).","commonSituations":"Synchronous (non-async) sessions trying to grow the index inline; invoking GrowIndex from inside upsert/read callbacks or epoch-protected test harness code.","solutions":["Call GrowIndex from a context without active epoch protection (e.g., main async session loop, separate task)","Migrate to async sessions and check GrowIndexAsync, which manages epoch protection correctly","Restructure so index growth is triggered by background maintenance code rather than inline request handling"],"exampleFix":"// before: called while epoch protected\nfasterKV.epoch.Resume(); // user-managed protection\nfasterKV.GrowIndex(); // throws if ThisInstanceProtected\n// after: call from unprotected context\nbool grew = fasterKV.GrowIndex(); // ensure caller thread is NOT epoch-protected","handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"try { fasterKV.GrowIndex(); }\ncatch (FasterException ex) when (ex.Message.Contains(\"GrowIndex when using non-async sessions\"))\n{\n    // defer growth to a background task without epoch protection\n    await Task.Run(() => fasterKV.GrowIndex());\n}","preventionTips":["Trigger index growth from background maintenance code, not from inside request/session callbacks","Avoid manually managing epoch protection around index operations","Prefer async sessions and GrowIndexAsync where available"],"tags":["epoch","index-resize","api-misuse","async"],"backgroundTag":"invalid-state-transition","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}