Skip to content

Fix WeChat CP MsgAudit SDK being destroyed and re-initialized on every API call#3934

Open
Copilot wants to merge 2 commits intodevelopfrom
copilot/fix-jvm-crash-issue
Open

Fix WeChat CP MsgAudit SDK being destroyed and re-initialized on every API call#3934
Copilot wants to merge 2 commits intodevelopfrom
copilot/fix-jvm-crash-issue

Conversation

Copy link
Contributor

Copilot AI commented Mar 17, 2026

The ref-counted SDK lifecycle methods (releaseMsgAuditSdk / decrementMsgAuditSdkRefCount) were unconditionally calling Finance.DestroySdk() whenever the reference count hit zero — regardless of whether the SDK had actually expired. Since each high-level method (e.g. getChatRecordPlainText) acquires and releases exactly one ref, every single call resulted in a full SDK teardown + re-init cycle.

Root cause

// Before: destroys SDK on every last-ref-release
if (newCount == 0 && this.msgAuditSdk == sdk) {
    Finance.DestroySdk(sdk);   // ← fires after every single API call
    this.msgAuditSdk = 0;
}

Fix

Only destroy the SDK when ref count hits zero and the SDK has already expired. While valid, the SDK is kept cached so subsequent calls reuse it without re-initialization.

// After: only destroy when expired
if (newCount == 0 && this.msgAuditSdk == sdk && isMsgAuditSdkExpired()) {
    Finance.DestroySdk(sdk);
    this.msgAuditSdk = 0;
}

Expiry-time cleanup still happens naturally: acquireMsgAuditSdk() returns 0 for expired SDKs, triggering initSdk() which calls updateMsgAuditSdk() — which destroys the old handle before caching the new one.

Changes

  • WxCpDefaultConfigImpl — guard Finance.DestroySdk in both releaseMsgAuditSdk and decrementMsgAuditSdkRefCount with isMsgAuditSdkExpired()
  • WxCpRedisConfigImpl — same fix
  • WxCpConfigStorage — updated Javadoc to reflect the corrected lifecycle contract
  • WxCpDefaultConfigImplMsgAuditSdkTest — new unit tests covering: SDK preserved after last-ref release when not expired; SDK reuse on next acquireMsgAuditSdk call; concurrent ref-count correctness
Original prompt

This section details on the original issue you should resolve

<issue_title>【企业微信】最新的会话存档相关API解决JVM崩溃问题的commit,是否存在频繁初始化及频繁销毁sdk的问题</issue_title>
<issue_description>如上,我看每个方法都执行完就消费,是否有影响?

Image</issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: 全部用中文
需要用中文,包括PR标题和分析总结过程


📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix JVM crash issue related to session archiving API Fix WeChat CP MsgAudit SDK being destroyed and re-initialized on every API call Mar 17, 2026
Copilot AI requested a review from binarywang March 17, 2026 07:32
@binarywang binarywang marked this pull request as ready for review March 17, 2026 07:33
Copilot AI review requested due to automatic review settings March 17, 2026 07:33
@binarywang binarywang removed their request for review March 17, 2026 07:34
@augmentcode
Copy link

augmentcode bot commented Mar 17, 2026

🤖 Augment PR Summary

总结:本 PR 修复企业微信会话存档(MsgAudit)SDK 在每次 API 调用后被反复销毁并重新初始化的问题,通过引用计数 + 过期判断实现 SDK 复用。

主要变更:

  • WxCpDefaultConfigImplWxCpRedisConfigImpl 中,releaseMsgAuditSdk/decrementMsgAuditSdkRefCount 仅在“引用计数归零且 SDK 已过期”时才调用 Finance.DestroySdk()
  • 当 SDK 未过期且引用计数降到 0 时,保留 SDK 句柄与过期时间,供后续调用直接复用,避免频繁 init/destroy 循环。
  • 更新 WxCpConfigStorage 的 Javadoc,明确“归零不一定销毁”的生命周期契约。
  • 新增 WxCpDefaultConfigImplMsgAuditSdkTest,覆盖:有效 SDK 的 acquire 行为、过期 SDK 的 acquire 返回值、release 后不销毁与再次复用等核心场景。
  • testng.xml 中加入上述新测试类。

技术要点:销毁动作由“最后一次释放”改为“最后一次释放且已过期”,而过期后的清理依旧会在后续重新初始化(updateMsgAuditSdk 更新句柄)过程中自然发生,从而降低 SDK 生命周期抖动。

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

* 验证:多线程场景下,多个并发调用的引用计数正确性
*/
@Test
public void testConcurrentRefCounting() throws Exception {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里方法名/注释强调“多线程/并发”,但实际是单线程顺序调用 acquireMsgAuditSdk()/releaseMsgAuditSdk(),无法覆盖竞态条件下的正确性。可能会让读者误以为已有并发测试覆盖,建议确认是否需要补充真实并发场景或调整描述。

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

/**
* 减少会话存档SDK的引用计数
* 当引用计数降为0时,自动销毁SDK以释放资源
* 当引用计数降为0且SDK已过期时,才自动销毁SDK以释放资源
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本次语义变更后,仓库里其它外部文档/示例中“引用计数归零即释放 SDK”的表述(例如 docs/CP_MSG_AUDIT_SDK_SAFE_USAGE.md 的实现原理段落)可能会与这里不一致。建议检查并同步相关文档,避免用户误解 SDK 生命周期行为。

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 修复企业微信会话存档(MsgAudit)SDK 在每次 API 调用结束时因引用计数归零而被无条件销毁、导致频繁重新初始化的问题,从而避免不必要的 SDK teardown/init 循环,提升调用效率并降低抖动。

Changes:

  • WxCpDefaultConfigImpl / WxCpRedisConfigImpl 中,将 DestroySdk 的触发条件调整为“引用计数归零且 SDK 已过期”。
  • 更新 WxCpConfigStorage 中与引用计数/释放相关的 Javadoc,反映新的生命周期约定。
  • 新增 WxCpDefaultConfigImplMsgAuditSdkTest 并加入 TestNG suite,覆盖“未过期时不销毁、可复用”等场景。

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java 引用计数归零时仅在 SDK 过期场景才销毁,避免每次调用都 destroy/init
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java 同步修复 Redis 配置实现中的销毁条件,保持行为一致
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java 更新接口文档以匹配新的生命周期与返回值语义
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImplMsgAuditSdkTest.java 新增单测验证 SDK 在未过期且 refCount 归零时不会被销毁、可被复用
weixin-java-cp/src/test/resources/testng.xml 将新增测试类纳入 TestNG 执行列表

*
* @param sdk sdk id
* @return 减少后的引用计数,如果返回0表示SDK已被销毁,如果SDK不匹配返回-1
* @return 减少后的引用计数,如果SDK不匹配返回-1
Comment on lines +128 to +160
/**
* 验证:多线程场景下,多个并发调用的引用计数正确性
*/
@Test
public void testConcurrentRefCounting() throws Exception {
long fakeSdk = 77777L;
setField("msgAuditSdk", fakeSdk);
setField("msgAuditSdkExpiresTime", System.currentTimeMillis() + VALID_EXPIRATION_TIME_OFFSET);
setField("msgAuditSdkRefCount", 0);

// 模拟 3 个并发调用同时持有 SDK
long sdk1 = config.acquireMsgAuditSdk();
long sdk2 = config.acquireMsgAuditSdk();
long sdk3 = config.acquireMsgAuditSdk();

Assert.assertEquals(sdk1, fakeSdk);
Assert.assertEquals(sdk2, fakeSdk);
Assert.assertEquals(sdk3, fakeSdk);
Assert.assertEquals((int) getField("msgAuditSdkRefCount"), 3, "应有 3 个引用");

// 逐一释放
config.releaseMsgAuditSdk(fakeSdk);
Assert.assertEquals((int) getField("msgAuditSdkRefCount"), 2, "释放一个后应有 2 个引用");
Assert.assertEquals((long) getField("msgAuditSdk"), fakeSdk, "SDK 仍有引用,不应被销毁");

config.releaseMsgAuditSdk(fakeSdk);
Assert.assertEquals((int) getField("msgAuditSdkRefCount"), 1, "释放两个后应有 1 个引用");

config.releaseMsgAuditSdk(fakeSdk);
Assert.assertEquals((int) getField("msgAuditSdkRefCount"), 0, "全部释放后引用计数应为 0");
// SDK 未过期,不应被销毁
Assert.assertEquals((long) getField("msgAuditSdk"), fakeSdk, "SDK 未过期,全部引用释放后不应被销毁");
}
Assert.assertEquals(refCountAfterRelease, 0, "引用计数应为 0");
}

/**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

【企业微信】最新的会话存档相关API解决JVM崩溃问题的commit,是否存在频繁初始化及频繁销毁sdk的问题

3 participants