Service Fabric滚动升级中cancellationToken.IsCancellationRequested未生效
解决Stateful Service滚动升级时循环卡住并持续抛"Primary State Manager is currently not readable"异常的问题
首先,咱们先拆解下你遇到的问题:滚动升级时,Service Fabric本应该触发RunAsync的取消令牌让循环退出,但你的代码因为没正确处理取消信号+异常捕获逻辑的问题,导致循环一直跑,反复抛出State Manager不可读的异常,同时Service Fabric Explorer里会显示类似**"Replica is undergoing reconfiguration, please wait for the reconfiguration to complete"**的提示(对应你提供的截图内容)。
问题根源分析
- 取消令牌未被及时检查:你的循环只在开头判断
!cancellationToken.IsCancellationRequested,但当升级触发取消后,若await this.cacheStore.GetAllCacheItemsWithExpiration()抛出异常,catch块仅记录异常就回到循环开头,此时如果取消令牌已经触发,却没再次确认状态,就会导致循环继续执行。 - 未将取消令牌传递给Reliable Collection操作:
GetAllCacheItemsWithExpiration方法内部可能没有接收cancellationToken,导致Service Fabric发送取消信号后,这个异步操作还在等待,无法及时终止。 - 未处理特定异常的重试/退出逻辑:"Primary State Manager is currently not readable"是副本重新配置时的临时状态,这时候应该重试,但重试前必须检查取消令牌,避免无限重试。
修复方案
修改你的RunAsync代码,做以下调整:
while (!cancellationToken.IsCancellationRequested) { try { // 把取消令牌传递给Reliable Collection操作,让它能响应取消 var cacheItemsWithExpiration = await this.cacheStore.GetAllCacheItemsWithExpiration(cancellationToken); // some processing on cacheItemsWithExpiration } catch (FabricTransientException ex) when (ex.Message.Contains("Primary State Manager is currently not readable")) { // 针对临时不可读的异常,添加短时间重试,同时检查取消令牌 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); this.telemetryHelper.TrackException(ex); } catch (Exception ex) { this.telemetryHelper.TrackException(ex); // 捕获其他异常后,再次检查取消令牌,避免循环卡住 if (cancellationToken.IsCancellationRequested) { break; } // 可选:添加其他异常的重试逻辑,根据业务需求调整 await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); } // 循环末尾也检查一次取消令牌,确保及时退出 if (cancellationToken.IsCancellationRequested) { break; } }
同时,需要修改GetAllCacheItemsWithExpiration方法,让它接收并使用cancellationToken,比如在内部调用Reliable Collection的方法时传入令牌:
public async Task<IEnumerable<CacheItem>> GetAllCacheItemsWithExpiration(CancellationToken cancellationToken) { var reliableDict = await this.StateManager.GetOrAddAsync<IReliableDictionary<string, CacheItem>>("CacheStore"); using (var tx = this.StateManager.CreateTransaction()) { // 传递取消令牌给异步枚举操作 var enumerator = await reliableDict.CreateEnumerableAsync(tx, cancellationToken); var items = new List<CacheItem>(); while (await enumerator.MoveNextAsync(cancellationToken)) { items.Add(enumerator.Current.Value); } return items.Where(item => item.ExpirationTime <= DateTime.UtcNow); } }
额外注意事项
- Service Fabric升级时的副本行为:滚动升级时,Service Fabric会先将旧版本的副本标记为要关闭,触发
RunAsync的取消令牌,然后启动新版本副本。如果旧副本的RunAsync不能及时退出,会导致升级卡住或超时。 - 避免无限制的异常循环:一定要在异常处理逻辑中检查取消令牌,确保在收到取消信号后能立即退出循环,不要因为异常捕获而忽略了取消请求。
内容的提问来源于stack exchange,提问作者Dadwals
相关产品推荐
相关产品推荐

