You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

依赖注入中多次创建令牌导致GitHub Runner并行测试超时

Dataverse ServiceClient并行测试挂起问题

依赖注入配置

通过tokenProviderFunction注入Microsoft.PowerPlatform.Dataverse.Client的DI配置代码:

sc.AddSingleton<IOrganizationServiceAsync2, ServiceClient>(provider =>
{
    var config = ...;
    var factory = ...;
    var client = new ServiceClient(
        new Uri(config....),
        factory.GetTokenAsync, true, provider.GetService<ILogger<ServiceClient>>())
    {
        MaxRetryCount = 5
    };
    return client;
});

令牌获取逻辑

令牌工厂基于多个ConfidentialClientApplications(每个用户对应一个)实现连接池,令牌获取代码如下:

return (await _generator
                .AcquireTokenForClient(new[] { $"{resource}/.default" })
                .ExecuteAsync(cancellationToken)).AccessToken;

问题现象

  • 本地虚拟机、本地测试及开发环境运行正常,但在GitHub Runner上执行xUnit集成测试时,测试并行运行且创建多个Fixture或测试类的场景下,会在_generator.AcquireTokenForClient(...).ExecuteAsync步骤挂起
  • GitHub Runner上顺序运行测试时无异常
  • 使用类Fixture或在测试类构造函数中初始化DI服务时会触发问题
  • 添加详细日志后,执行generator相关操作后无后续日志输出
  • 添加.ConfigureAwait(false)后能输出下一行日志"done",但整体仍会挂起
  • 新建测试项目可复现:单个测试类正常,多个则失败;但将令牌操作放在每个测试用例中时可正常运行
  • 尝试ubuntu-latest和windows-latest的GitHub Action,问题表现一致
  • GitHub Action工作流已设置超时,问题仍存在

解决方案

1. 避免同步上下文执行异步操作

xUnit的测试类构造函数、Fixture初始化是同步执行上下文,若在此阶段触发异步令牌获取(比如DI初始化ServiceClient时自动触发),极易因线程上下文死锁导致挂起。建议:

  • 用Lazy<T>包装ServiceClient,延迟到测试用例内部再初始化
  • 不在构造函数/Fixture中直接获取需要异步初始化的服务,改为在每个测试用例中显式获取并触发初始化

2. 隔离ConfidentialClientApplication实例

并行测试场景下,共享ConfidentialClientApplications可能引发资源竞争或线程池耗尽:

  • 为每个测试用例创建独立的ConfidentialClientApplication实例,避免跨测试共享状态
  • 在xUnit配置文件中设置maxParallelThreads,限制并行测试的线程数,适配GitHub Runner的资源限制

3. 确保CancellationToken正确传递

检查GetTokenAsync方法中是否正确传递测试框架提供的CancellationToken,避免因取消令牌未正确处理导致的无响应:

// 在测试用例中传递测试的CancellationToken
var token = cancellationToken;
var accessToken = await factory.GetTokenAsync(resource, token);

4. 调整ServiceClient初始化参数

初始化ServiceClient时,将自动刷新令牌的参数设为false(原代码第三个参数为true),减少后台异步操作带来的线程冲突:

var client = new ServiceClient(
    new Uri(config....),
    factory.GetTokenAsync, false, // 禁用自动令牌刷新
    provider.GetService<ILogger<ServiceClient>>())
{
    MaxRetryCount = 5
};

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 06:16:30