适用范围: NoSQL
本文介绍在使用适用于 Azure Cosmos DB for NoSQL 的 Azure Functions 触发器时出现的常见问题及其解决方法和诊断步骤。
适用于 Azure Cosmos DB for NoSQL 的 Azure Functions 触发器和绑定依赖于基于基础 Azure Functions 运行时的扩展包 Microsoft.Azure.WebJobs.Extensions.CosmosDB。 请始终保持这些包的更新状态,因为它们可能包含用于解决你所遇到的任何潜在问题的修复程序和新功能。
该扩展包的关键功能是为适用于 Azure Cosmos DB 的 Azure Functions 触发器和绑定提供支持。 该包还包含 Azure Cosmos DB.NET SDK,用于帮助你以编程方式来与 Azure Cosmos DB 交互,而无需使用触发器和绑定。
若要使用 Azure Cosmos DB SDK,请务必不要将项目添加到另一个 NuGet 包引用。 而是让 SDK 引用通过 Azure Functions 的扩展包进行解析。 独立于触发器和绑定使用 Azure Cosmos DB SDK。
此外,如果你手动创建自己的 Azure Cosmos DB SDK 客户端实例,应遵循以下模式:只提供一个使用单一实例模式方法的客户端实例。 此过程可避免操作中出现潜在的套接字问题。
Azure 函数失败并出现以下错误消息:“源集合 collection-name
(在数据库 database-name
中)或租约集合 collection2-name
(在数据库 database2-name
中)不存在。 在侦听器启动之前,这两个集合必须存在。 若要自动创建租约集合,请将 CreateLeaseCollectionIfNotExists
设置为 true
。
这表示运行触发器所需的一个或两个 Azure Cosmos DB 容器处于以下任一状态:
- 不存在
- 无法由 Azure 函数访问
错误文本本身告知触发器正在根据配置查找的 Azure Cosmos DB 数据库和容器。
若要解决此问题,请执行下列操作:
验证
Connection
属性,以及它是否引用了 Azure 函数应用中存在的设置。- 此属性中的值不应是连接字符串本身,而是配置设置的名称。
验证
databaseName
和containerName
值是否在你的 Azure Cosmos DB 帐户中存在。- 如果使用自动值替换(使用
%settingName%
模式),请确保该设置的名称在 Azure 函数应用中存在。
- 如果使用自动值替换(使用
如果未指定
LeaseContainerName/leaseContainerName
值,则默认值为leases
。 验证此类容器是否存在。- (可选)可将触发器中的
CreateLeaseContainerIfNotExists
属性设置为true
,以自动创建该容器。
- (可选)可将触发器中的
为了确保 Azure Cosmos DB 帐户没有阻止 Azure 函数,请验证 Azure Cosmos DB 帐户的防火墙配置。
旧版 Azure Cosmos DB 扩展不支持使用在共享吞吐量数据库中创建的租约容器。
若要解决此问题,请执行下列操作:
- 更新 Microsoft.Azure.WebJobs.Extensions.CosmosDB 扩展以获取最新版本。
此错误表示正在使用带有旧版扩展依赖项的分区租约集合。
若要解决此问题,请执行下列操作:
- 升级到最新的可用版本。
Azure 函数无法启动,错误消息为“Forbidden (403); Substatus: 5300... The given request [POST ...] can't be authorized by Microsoft Entra token in data plane
”
此错误意味着函数尝试使用 Microsoft Entra 标识执行非数据操作。 使用 Microsoft Entra 标识时无法使用 CreateLeaseContainerIfNotExists = true
。
此错误表示当前租约容器已分区,但分区键路径不是 /id
。
若要解决此问题,请执行下列操作:
- 使用
/id
作为分区键来重新创建租约容器。
如果使用 Azure 门户,并在检查使用触发器的 Azure 函数时选择“运行”按钮,则可能会出现此问题。 触发器不需要你选择“运行”即可启动。 部署 Azure 函数时它会自动启动。
若要解决此问题,请执行下列操作:
- 若要在 Azure 门户上检查函数的日志流,请转到受监视的容器并插入一些新项。 触发器自动运行。
这种情况可能是多种原因造成的。 请考虑尝试以下任何或所有解决方法:
你的 Azure 函数是否与 Azure Cosmos DB 帐户部署在不同的区域? 为了优化网络延迟,应将 Azure 函数和 Azure Cosmos DB 帐户共置在同一个 Azure 区域。
Azure Cosmos DB 容器中发生的更改是连续性的还是偶发性的?
- 如果它们是偶发性的,则原因可能是存储更改的时间与 Azure Functions 拾取更改的时间有一段延迟。 当触发器在内部检查 Azure Cosmos DB 容器中的更改并且发现没有等待读取的更改时,就会发生此行为。 然后,触发器会休眠一段可配置的时间(默认为 5 秒),然后再检查是否有新更改。 触发器利用此行为来避免高请求单位 (RU) 消耗。 可以通过触发器的配置中的
FeedPollDelay/feedPollDelay
设置来配置休眠时间。 该值应以毫秒为单位。
- 如果它们是偶发性的,则原因可能是存储更改的时间与 Azure Functions 拾取更改的时间有一段延迟。 当触发器在内部检查 Azure Cosmos DB 容器中的更改并且发现没有等待读取的更改时,就会发生此行为。 然后,触发器会休眠一段可配置的时间(默认为 5 秒),然后再检查是否有新更改。 触发器利用此行为来避免高请求单位 (RU) 消耗。 可以通过触发器的配置中的
Azure Cosmos DB 容器可能受到速率限制。
可以使用触发器中的
PreferredLocations
属性来指定 Azure 区域的逗号分隔列表,以定义自定义的首选连接顺序。处理新更改的速度决定了触发器接收这些更改的速度。 验证函数的执行时间或持续时间。 如果函数速度缓慢,则会增加触发器获取新更改所需的时间。 如果你发现持续时间最近有所增加,原因可能是最近的代码影响了函数。 如果在 Azure Cosmos DB 容器上接收操作的速度快于触发器的速度,那么你将始终保持滞后状态。 建议调查函数的代码,以确定最耗时的操作以及如何优化。
可以使用调试日志检查诊断并验证是否存在网络延迟。
更改这一概念指的是对某项执行的操作。 收到同一项目的事件的最常见情况包括:
函数在执行期间失败。 如果函数已启用重试策略,或者如果函数执行超出了允许的执行时间,可以再次将同一批更改传递到函数。 此失败是意料之中的,也是设计使然,请查看函数日志以了解故障的指示,并确保已启用触发器日志以获取更多详细信息。
实例之间存在租用时间的负载均衡。 当实例增加或减少时,负载均衡可能会导致将同一批更改传递到多个函数实例。 此负载平衡是意料之中的,也是设计使然,并且应该是暂时的。 触发器日志包括实例获取和释放租用时间时的事件。
正在更新该项。 更改源可能包含对同一项的多个操作。 如果该项正在接收更新,则它可能会选取多个事件(每个更新一个事件)。 若要区分对同一项的不同操作,一个简单方法是跟踪
_lsn
每个更改的 属性。 如果属性不匹配,则更改将不同。如果只通过
id
来标识项,请记住,项的唯一标识符是id
以及其分区键。 (两个项可以具有相同的id
,但分区键不同)。
你可能会发现,函数没有获取 Azure Cosmos DB for NoSQL 容器中发生的某些更改。 或者在复制更改时,目标位置缺少一些更改。 如果是这样,请尝试本部分所述的解决方法。
确保已启用日志。 验证在处理过程中没有发生错误。
当 Azure 函数收到更改时,它通常会处理这些更改,并可能会选择性地将结果发送到另一个目标。 调查丢失更改的问题时,请确保衡量在引入时间点收到的更改。 在 Azure 函数启动时进行衡量,而不是在目的地进行衡量。
如果目标中缺少某些更改,则此行为可能表示在收到更改后执行 Azure 函数期间发生了某种错误。
在这种情况下,最佳措施是在代码中以及在可能正在处理更改的循环中添加
try/catch
块。 添加它有助于检测特定的项子集发生的任何失败,并相应地对其进行处理(将这些项发送到另一个存储以做进一步的分析或重试)。 此外,可以配置 Azure Functions 重试策略。备注
默认情况下,如果在代码执行期间发生未经处理的异常,则适用于 Azure Cosmos DB for NOSQL 的 Azure Functions 触发器不会重试一批更改。 这意味着,更改未抵达目标的原因可能是无法处理它们。
如果目标是另一个 Azure Cosmos DB 容器,并且你正在执行更新插入操作来复制项,请验证两个容器上的分区键定义。 受监视容器和目标容器上的分区键必须相同。 由于此配置差异,更新插入操作可能会将多个源项保存为目标上的一个项。
如果你发现触发器未收到某些更改,则最常见的原因是有另一个 Azure 函数正在运行。 另一个函数可能部署在 Azure 中,或者是在开发人员计算机本地运行的、采用完全相同配置的函数。 如果是这样,此函数可能正在窃取你的 Azure 函数预期要处理的更改子集。
此外,如果你知道正在运行多少个 Azure 函数应用实例,则也可以验证这种情况。 如果检查租约容器并统计其中包含的租约项数,这些项中的非重复 Owner
属性值应等于函数应用的实例数。 如果所有者数目超过已知的 Azure 函数应用实例数,则表示这些多出的所有者正在“窃取”更改。
若要解决此问题,一个简单的方法是将采用新值或不同值的 LeaseCollectionPrefix/leaseCollectionPrefix
应用到你的函数,或使用新的租约容器进行测试。
若要从头开始重新处理容器中的所有项,请执行以下操作:
如果 Azure 函数当前正在运行,请将其停止。
删除租约集合中的文档(或删除租约集合并重新创建一个空集合)。
将函数中的 StartFromBeginning CosmosDBTrigger 属性设置为
true
。重启 Azure 函数。 该函数现在从头开始读取并处理所有更改。
如果将 StartFromBeginning 设置为 true
,则会告知 Azure 函数要从头开始读取集合历史记录的更改,而不是从当前时间开始读取。
此解决方法仅适用于尚未创建租约(即租约集合中的文档)的情况。
如果已创建租约,则将此属性设置为 true
不起作用。 在这种情况下,当某个函数停止并重启时,它将从租约集合中定义的最后一个检查点开始读取。
如果 Azure Functions 项目包含对 Azure Cosmos DB SDK 的手动 NuGet 包引用且存在版本冲突,则会发生此错误。 发生此错误通常是因为包的版本与 Azure Functions 的 Azure Cosmos DB 扩展提供的版本不同。
若要解决此问题,请删除已添加的手动 NuGet 引用,并让 Azure Cosmos DB SDK 引用通过 Azure Functions 的 Azure Cosmos DB 扩展包进行解析。
如前面的接收更改花费了过长的时间部分中所述,Azure 函数将休眠一定的时间(可配置,默认为 5 秒),然后检查新的更改(以避免 RU 消耗量过高)。 可以通过触发器配置中的 FeedPollDelay/feedPollDelay
设置来配置此休眠时间(该值应以毫秒为单位)。