Cosmos DB单分区达50GB拆分后changeFeedWorker运行异常
Cosmos DB单分区自动拆分后Change Feed Worker运行异常排查
问题现象
- Cosmos DB单个分区达到50GB容量上限自动拆分为2个分区后,此前运行正常的changeFeedWorker出现异常
- 异常表现:数据库新增大量条目时changefeedworkers无法稳定触发,依赖变更流同步更新的视图仅能完成部分数据更新
- 租约容器观测结果:LeaseToken从拆分前的
0变为拆分后的1、2共2个 - 分区拆分前变更同步功能完全正常,以下为当前使用的worker启动代码:
async Task IChangeFeedWorker.StartAsync() { await _semaphoreSlim.WaitAsync(); string operationName = $"{nameof(CosmosChangeFeedWorker)}.{nameof(IChangeFeedWorker.StartAsync)}"; using var _ = BeginChangeFeedWorkerScope(_logger, operationName, _processorName, _instanceName); try { if (Active) { return; } Container eventContainer = _cosmosClient.GetContainer(_databaseId, _eventContainerId); Container leaseContainer = _cosmosClient.GetContainer(_databaseId, _leaseContainerId); _changeFeedProcessor = eventContainer .GetChangeFeedProcessorBuilder<CosmosEventChange>(_processorName, HandleChangesAsync) .WithInstanceName(_instanceName) .WithLeaseContainer(leaseContainer) .WithStartTime(_startTimeOfTrackingChanges) .Build(); await _changeFeedProcessor.StartAsync(); Active = true; _logger.LogInformation( "Change feed processor instance has been started.", _processorName, _instanceName); } catch (Exception e) { _logger.LogError(e, "Starting of change feed processor instance has failed.", _processorName, _instanceName); _changeFeedProcessor = null; throw; } finally { _semaphoreSlim.Release(); } }
排查方向与解决方案
- 优先检查Cosmos .NET SDK版本,这是分区拆分场景下的最高发问题。3.27.0之前的版本对自动分区拆分的适配存在已知缺陷:拆分完成后旧的租约记录无法正确映射到新生成的两个物理分区,部分新分区的消费任务不会被现有worker正常拾取,直接表现为新写入数据只有部分能被消费、租约Token更新后部分分区的消费位点长期停滞。直接将
Microsoft.Azure.Cosmos升级到3.27.0及以上的稳定版本,全量重启所有worker实例,观察10-15分钟看消费延迟是否回落至正常水平。 - 核对代码中的
.WithStartTime(_startTimeOfTrackingChanges)配置。该配置仅在消费组首次初始化、不存在任何历史租约记录时生效,分区拆分后旧租约仍然存在,此时SDK处理新拆分出的分区时,会强制从指定的历史时间点全量拉取历史数据,数据量较大时worker会长时间卡在历史数据回放阶段,新写入的增量数据会被阻塞无法及时处理,表现为触发不稳定、数据同步不全。如果业务不需要每次重启都重放历史数据,直接删除该行配置即可——Change Feed Processor默认会自动持久化每个分区的消费检查点,重启后会自动从上次中断的位置继续消费,不会丢失数据。如果确实需要保留固定起始时间的逻辑,升级SDK后先删除lease容器中拆分前生成的旧租约文档,再启动worker。 - 检查worker实例数与消费并发配置。分区拆分后物理分区数变为2个,如果仅部署1个worker实例,该实例需要同时承担两个分区的消费任务,若
HandleChangesAsync处理逻辑耗时较长、未配置合理的并发参数,很容易出现单个分区消费任务被饿死的情况。可以临时将worker实例数扩容到2个及以上,同时通过.WithMaxItems(100)、.WithPollInterval(TimeSpan.FromSeconds(1))调整拉取批次大小和轮询间隔,观察两个分区的消费进度是否都能正常推进。 - 排查消费处理逻辑的隐式报错。如果
HandleChangesAsync中存在未捕获的异常、或是对拆分后新分区键范围的数据存在逻辑bug(比如分区键格式校验失败、特殊值处理抛错),worker会持续重试失败的批次,不会向后消费新数据,也会表现为部分数据无法同步。将Cosmos客户端的日志级别调整为Debug,重点排查是否存在消费批次处理失败、租约接管失败的报错日志。 - 兜底修复方案:如果以上步骤排查完成后异常仍存在,可以直接清空lease容器内的所有文档,重启worker实例重新初始化消费位点。注意该操作会触发一次全量变更重放,需要提前确保下游消费逻辑做了幂等处理,避免重复更新引发数据问题。
内容的提问来源于stack exchange,提问作者Denfer
相关产品推荐
相关产品推荐

