diff --git a/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts b/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts index 81b58ca9..40ce6a99 100644 --- a/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts +++ b/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts @@ -436,13 +436,54 @@ export class GrpcClient extends EventEmitter { } /** - * 加入会话 + * 加入会话(带重试逻辑) + * + * 支持重试以处理乐观锁冲突等瞬态错误 */ async joinSession(sessionId: string, partyId: string, joinToken: string): Promise { if (!this.client) { throw new Error('Not connected'); } + const maxRetries = 3; + const baseDelayMs = 500; + let lastError: Error | null = null; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + const result = await this.doJoinSession(sessionId, partyId, joinToken); + return result; + } catch (err) { + lastError = err as Error; + const errorMessage = lastError.message || ''; + + // 判断是否为可重试的错误 + const isRetryable = + errorMessage.includes('optimistic lock') || + errorMessage.includes('modified by another transaction') || + errorMessage.includes('UNAVAILABLE') || + errorMessage.includes('DEADLINE_EXCEEDED'); + + if (!isRetryable || attempt >= maxRetries) { + console.error(`[gRPC] joinSession failed after ${attempt} attempt(s):`, errorMessage); + throw lastError; + } + + // 计算退避延迟(指数退避 + 随机抖动) + const delay = baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 100; + console.log(`[gRPC] joinSession attempt ${attempt}/${maxRetries} failed (${errorMessage}), retrying in ${Math.round(delay)}ms...`); + + await this.sleep(delay); + } + } + + throw lastError || new Error('joinSession failed after retries'); + } + + /** + * 内部方法:执行单次 joinSession 调用 + */ + private doJoinSession(sessionId: string, partyId: string, joinToken: string): Promise { return new Promise((resolve, reject) => { (this.client as grpc.Client & { joinSession: (req: unknown, callback: (err: Error | null, res: JoinSessionResponse) => void) => void }) .joinSession( @@ -462,6 +503,13 @@ export class GrpcClient extends EventEmitter { }); } + /** + * 辅助方法:异步等待 + */ + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + /** * 订阅会话事件(带自动重连) */