当前位置: 首页 > news >正文

羽球搭子 HarmonyOS 实战(21):云同步失败记录与重试队列

一、网络失败不能把球场上的比分一起回滚

实时计分发生在球场,网络却可能随时切换、变弱或完全中断。点击保存后,如果应用先等云端成功再写本地,失败会让用户怀疑本次保存的比分是否丢失;如果失败后无条件重建云端对局,又可能出现重复房间和重复事件。

羽球搭子的顺序是本地优先:比分变更先进入SessionStore并持久化,随后尝试提交云端事件。提交失败时,不回滚本地结果,而是记录一条“哪个对局的哪个场次同步失败”。页面据此显示明确提示,用户恢复网络后从云端协作入口执行一次完整同步。

这里的“重试队列”是轻量失败清单,不是常驻后台任务调度器。它保存可观察的失败项并去重,真正的重试由用户入口触发。这样的边界与现有实现一致,也避免在没有后台任务、退避算法和持久化任务协议时夸大能力。

二、本地保存与云端提交分成两个结果

计分页先得到本地变更对象,其中包含对局 ID、场次 ID、双方比分和结束时间。只有本地保存成功,才尝试生成云端事件。网络失败改变的是“云端是否收敛”,而不是“本机是否记住比分”。

interface ScoreChange { sessionId: string matchId: string scoreA: number scoreB: number finishedAt: number } function saveScoreLocally( sessionId: string, matchId: string, scoreA: number, scoreB: number ): ScoreChange | undefined { const detail = SessionStore.getDetail(sessionId) if (detail === undefined) { return undefined } const next = updateMatchScore(detail, matchId, scoreA, scoreB) SessionStore.saveDetail(next.detail) return next.change }

这段逻辑把本地数据当作现场操作的第一真相源。云端同步仍然重要,因为另一台设备需要看到最新比分;但它不应该让单机核心流程依赖瞬时网络。

阶段成功结果失败结果用户是否能继续计分
本地校验得到规范比分变更拒绝不存在的场次否,先修正输入
本地持久化摘要和详情同时更新保留旧值并提示否,避免假成功
云端事件提交版本推进并清除失败项记录失败项
主动重试云端与本地场次收敛保留失败提示

三、失败项使用复合键去重

失败记录至少需要sessionIdmatchId、提示信息和更新时间。添加记录时先解析本地 ID 与云端 ID 的别名,再用“规范对局 ID + 场次 ID”查找旧项。存在就覆盖时间和信息,不存在才追加。

interface SyncFailure { sessionId: string matchId: string message: string updatedAt: number } function markFailure( sessionId: string, matchId: string, message: string ): void { const resolvedId = CloudStateStore.resolveSessionId(sessionId) const failures = listFailures().slice() const index = failures.findIndex((item) => CloudStateStore.resolveSessionId(item.sessionId) === resolvedId && item.matchId === matchId ) const next = { sessionId: resolvedId, matchId, message, updatedAt: Date.now() } if (index >= 0) failures[index] = next else failures.push(next) AppStorage.setOrCreate<SyncFailure[]>('cloud_score_sync_failures', failures) }

复合键避免同一个失败因连续保存而堆出多条提示。更新时间仍会刷新,让页面知道最近一次同步尝试发生在何时。失败数组存在 AppStorage 中,进程重启后不会自动恢复,因此它代表运行期提示,不应当被描述成可靠持久化消息队列。

四、提交成功只清理对应场次

同步结果必须精确清理。如果对局中有两场比赛同时失败,A 场重新提交成功不能把 B 场提示一起消掉。成功时按复合键删除;整场对局完成完整同步或被归档后,才按 sessionId 清理全部关联项。

function clearFailure(sessionId: string, matchId: string): void { const resolvedId = CloudStateStore.resolveSessionId(sessionId) const next = listFailures().filter((item) => { const sameSession = CloudStateStore.resolveSessionId(item.sessionId) === resolvedId return !(sameSession && item.matchId === matchId) }) AppStorage.setOrCreate<SyncFailure[]>('cloud_score_sync_failures', next) } function clearSessionFailures(sessionId: string): void { const resolvedId = CloudStateStore.resolveSessionId(sessionId) const next = listFailures().filter((item) => CloudStateStore.resolveSessionId(item.sessionId) !== resolvedId ) AppStorage.setOrCreate<SyncFailure[]>('cloud_score_sync_failures', next) }

清理动作本身也应当是幂等的。找不到目标项时返回同一个数组语义,不抛出异常,不把一次成功同步变成新的 UI 错误。

五、409 冲突先拉取版本,再重放事件

网络恢复后不代表原事件仍能直接提交。另一台设备可能已经推进服务端版本,旧请求携带的baseVersion会得到 409。仓库层读取服务端版本,更新本地云状态,再用新的客户端事件 ID 重放一次。

async function submitWithConflictRetry( change: ScoreChange, retryCount: number = 0 ): Promise<boolean> { try { const result = await CloudRepository.recordScore(change) CloudStateStore.updateVersion(change.sessionId, result.version) clearFailure(change.sessionId, change.matchId) return true } catch (error) { const serverVersion = readConflictVersion(error) if (serverVersion <= 0 || retryCount >= 1) { markFailure(change.sessionId, change.matchId, friendlyMessage(error)) return false } CloudStateStore.updateVersion(change.sessionId, serverVersion) return CloudRepository.recordScore({ ...change, clientEventId: createRetryEventId(change), baseVersion: serverVersion }).then(() => true).catch(() => false) } }

冲突重试只能有限次数。持续 409 可能意味着本地快照已经严重落后,正确做法是拉取服务端详情并重新合并,而不是无限循环发送。

六、手动重试选择“完整场次同步”

运行期失败项只记录了需要提示的比分事件,并没有保存一份可跨重启执行的命令对象。因此“我的 > 云端协作”中的重试入口选择同步当前完整对局:若本地场次尚未建立云端映射,先创建;已存在则比较版本、提交差异或拉取最新详情。

async function retryActiveCloudSync(): Promise<void> { if (cloudBusy || !AuthSessionStore.isSignedIn()) { return } const sessionId = pickActiveSessionId() if (sessionId.length === 0) { showMessage('暂无需要同步的对局') return } cloudBusy = true try { const synced = await CloudRepository.syncLocalSession(sessionId) if (!synced) throw new Error('sync failed') clearSessionFailures(sessionId) reloadCloudState() showMessage('云端同步已重试') } finally { cloudBusy = false } }

完整同步比盲目重发旧事件更符合当前数据模型:本地对局详情仍然存在,仓库层可以重新计算需要提交的内容。未来若要实现自动后台重试,才需要持久化命令、退避时间、最大次数、网络约束和幂等键。

七、提示要告诉用户“本地仍然安全”

错误文案应明确两件事:云端同步失败,但比分已经保存在本地;用户可以稍后重新保存,或到云端协作入口主动重试。只写“请求失败”会让用户不敢离开页面,也无法判断是否需要重新计分。

页面状态文案重点可操作入口禁止行为
单场同步失败本地已保存、云端未同步重新保存或稍后重试自动回滚比分
当前对局存在失败项展示“重试云端同步”完整同步当前对局重复创建本地对局
重试进行中防止连续点击按钮禁用并发发起两次同步
重试成功清除失败提示返回正常状态保留陈旧红色告警
仍然失败保持本地数据和提示稍后再试无限快速重试

页面通过失败数组派生按钮是否显示,而不是维护另一枚容易漂移的布尔值。只要数组中还有当前对局的项目,入口就保持可见。

八、断网、冲突和冷启动分别验收

第一组测试在实时计分页断网后修改比分,确认本地页面立即更新、重进页面仍能看到比分,并出现同步失败提示。恢复网络后点击主动重试,服务端详情与本地一致,提示消失。

第二组测试使用两台设备制造版本冲突:设备 A、B 同时进入同一对局,A 先改分,B 基于旧版本提交。仓库层应读取 409 中的服务端版本并执行有限重试;若仍不能收敛,保留失败项而不是覆盖对方结果。

第三组测试结束进程再启动。比赛数据应从 Preferences 恢复,而运行期失败提示可能为空;此时用户仍可从云端协作页主动同步当前场次。这个结果清楚反映当前实现边界,不把 AppStorage 失败清单误当持久任务。

网络请求与状态管理的实现细节应以 HarmonyOS Network Kit 官方指南 为准,同时结合服务端幂等和版本冲突协议设计。

九、总结

云同步失败处理的核心顺序是:本地先保存,云端后提交;失败项按对局和场次去重;成功只清理对应项;用户通过明确入口同步完整场次;版本冲突执行有限重试。这样弱网不会破坏现场计分,也不会因连续点击制造重复对局。

当前失败数组是运行期可观察清单,而不是后台持久化任务系统。把边界讲清楚,反而能让后续演进更稳:需要自动重试时,再补持久化命令、退避、网络约束和可审计的幂等键,而不是把 UI 提示数组直接扩成不可靠的调度器。

http://www.jsqmd.com/news/1248979/

相关文章:

  • Django毕设项目:基于 Django 的智慧宠物照料服务共享平台设计 宠物寄养订单运维与评价体系系统 (源码+文档,讲解、调试运行,定制等)
  • AI 原生知识库:一分钟生成专业文档初稿,告别零起点
  • 一个秒杀就把 MySQL 打挂了?我用 Redis + 异步削峰扛住了 100 倍流量
  • AI驱动的下一代药物研发技术解析
  • ARM Cortex-M微控制器Flash性能优化与安全保护机制深度解析
  • 基于YOLO与SpringBoot的智能杂草检测系统实践
  • 欧米茄北京售后维修服务中心|北京欧米茄手表表圈旋转齿感不均调校 + 售后电话 400-883-8097 (2026 年 7 月最新) - 欧米茄中国售后中心
  • 量化交易平台商业化困境与破局路径分析
  • Minidayz1.4.1网页版:生存游戏轻量化与跨平台优化
  • 文献阅读 260722-Nonlinear increase of compound drought-heatwave events since the early 2000s
  • AI时代技术写作者角色重塑:从文字匠到知识架构师,Baklib驱动高效知识管理与多站发布
  • 我的Linux学习方法:不会就问AI,动手熟能生巧
  • 羽球搭子 HarmonyOS 实战(22):备份恢复与长比赛容错
  • C++后端开发:基于libcpr实现HTTP断点续传的完整方案
  • 如何借助节气装置,实现氩弧焊40%-60%节气率?
  • 每日热门skill:12亿微信用户一夜打通AI!openclaw-weixin到底强在哪?卡兹克带你拆透官方插件
  • 基因编辑载体伯远生物基因编辑载体
  • 冲压自动化解决方案应用科普 智能制造落地优势与工艺适配
  • 连锁回收品牌实力PK|绍兴奢侈品回收优质门店盘点,6家持证机构回款速度与估价方式横评 - 商业资讯新知
  • Agent Skills技术:构建智能编程助手的技能包革命
  • Spring AI Alibaba Graph智能工作流-顺序执行
  • Unity射线检测与LineRenderer可视化实战:从原理到性能优化
  • PyTorch实战:从零构建与优化大语言模型
  • 视频去模糊技术:轻量化DSTNet的创新与实践
  • 前端工程师收藏!2026年大模型风口,你的进阶“逃生”指南
  • 数字人口型同步不是调参游戏!真正决定商业落地的3个硬件感知阈值与2个不可绕过的物理延迟硬边界
  • 深入解析Tiva TM4C123BH6ZRB设备能力寄存器:实现硬件自识别与驱动健壮性
  • CDN能力边界的技术探讨与实践
  • Webpack5 完整性能调优实战:分包、缓存、tree-shaking、压缩、CDN
  • 2026北京彩钢房回收 制冷设备回收 电线电缆回收处理指南 - LYL仔仔