fix: recover CMPP slots after gateway restart

This commit is contained in:
hectorzhao
2026-07-16 18:02:25 +08:00
parent faa716b8d0
commit 23a1f6fa15
3 changed files with 8 additions and 0 deletions
@@ -541,6 +541,9 @@ describe('SmsConfigService', () => {
remoteIp: '127.0.0.1', remoteIp: '127.0.0.1',
}), }),
}); });
expect(prisma.cmppDownstreamConnection.deleteMany).toHaveBeenCalledWith({
where: { status: 'connected', lastHeartbeatAt: { lt: new Date('2026-07-11T10:58:30.000Z') } },
});
expect(prisma.operationLog.create).toHaveBeenCalledWith({ expect(prisma.operationLog.create).toHaveBeenCalledWith({
data: expect.objectContaining({ data: expect.objectContaining({
action: 'cmpp_downstream_connection.connected', action: 'cmpp_downstream_connection.connected',
+4
View File
@@ -681,6 +681,10 @@ export class SmsConfigService {
if (data.remoteIp && !isIpAllowed(data.remoteIp, application.ipAllowlist.map((item) => item.ipCidr))) { if (data.remoteIp && !isIpAllowed(data.remoteIp, application.ipAllowlist.map((item) => item.ipCidr))) {
throw new ForbiddenException('CMPP source IP is not in application allowlist'); throw new ForbiddenException('CMPP source IP is not in application allowlist');
} }
// A Gateway process can disappear before it reports disconnect. Prune its
// expired rows here so a restarted process can reclaim connection slots
// without waiting for an operator to open the connection-list page.
await this.markTimedOutDownstreamConnections(observedAt);
const activeConnections = await this.prisma.cmppDownstreamConnection.findMany({ const activeConnections = await this.prisma.cmppDownstreamConnection.findMany({
where: { applicationId: application.id, status: 'connected' }, where: { applicationId: application.id, status: 'connected' },
select: { connectionId: true }, select: { connectionId: true },
+1
View File
@@ -2004,3 +2004,4 @@ git diff --check
- Gateway 登录响应接入真实 `cmppMaxConnections`,按应用活动 TCP 会话计数并拒绝超限 bind;底层连接关闭回调会清理会话和回写断开。API 连接事件再次校验应用状态、接口开关、IP/CIDR 白名单和连接数,存量连接在下一次心跳不再符合配置时由 Gateway 主动关闭。 - Gateway 登录响应接入真实 `cmppMaxConnections`,按应用活动 TCP 会话计数并拒绝超限 bind;底层连接关闭回调会清理会话和回写断开。API 连接事件再次校验应用状态、接口开关、IP/CIDR 白名单和连接数,存量连接在下一次心跳不再符合配置时由 Gateway 主动关闭。
- 生产只读核查发现应用 `715011` 最大连接数为 1,当前来源 IP 为 `183.194.97.158`,白名单后来改为 `2.2.2.2`;连接建立早于白名单修改,印证旧实现不会主动清理存量连接。核查期间未修改生产配置、数据库或进程。 - 生产只读核查发现应用 `715011` 最大连接数为 1,当前来源 IP 为 `183.194.97.158`,白名单后来改为 `2.2.2.2`;连接建立早于白名单修改,印证旧实现不会主动清理存量连接。核查期间未修改生产配置、数据库或进程。
- API 目标测试 2 个套件 97 条以及全量 20 个套件 213 条断言通过,API TypeScript build、Gateway `go test ./...`、前端 TypeScript/Vite build 和部署脚本语法检查通过;全量 Jest 完成后仍提示既有异步句柄未关闭,断言结果不受影响,测试进程已单独结束。使用真实本地 NestJS API、PostgreSQL 和 Playwright/Chromium 验证运营端两类参数复制、客户端未开通 CMPP 的页面禁用与 API 403,以及 HTTP 页面复制降级;临时数据已清理。按要求暂不提交、不 push、不部署。 - API 目标测试 2 个套件 97 条以及全量 20 个套件 213 条断言通过,API TypeScript build、Gateway `go test ./...`、前端 TypeScript/Vite build 和部署脚本语法检查通过;全量 Jest 完成后仍提示既有异步句柄未关闭,断言结果不受影响,测试进程已单独结束。使用真实本地 NestJS API、PostgreSQL 和 Playwright/Chromium 验证运营端两类参数复制、客户端未开通 CMPP 的页面禁用与 API 403,以及 HTTP 页面复制降级;临时数据已清理。按要求暂不提交、不 push、不部署。
- 生产首轮发布复核发现 Gateway 进程重启时无法保证回写旧 TCP 会话断开,数据库陈旧连接可能占用应用连接名额,而原超时清理只在查询连接列表时触发。现将 90 秒陈旧连接清理前置到每次非断开连接事件校验,确保新 Gateway 无需等待运营人员打开页面即可自动恢复连接名额。