同步引擎的工作原理
本页描述的是 WCPOS v1.10.0 中引入的同步引擎。更早的版本使用另一种按屏幕划分的复制模型——请参见本页末尾的 v1.10.0 有哪些变化。
WCPOS 是本地优先的:每个屏幕都读写设备上的一个数据库,而 同步引擎 在后台让该数据库与您的 WooCommerce 商店保持收敛。本页解释引擎如何决定获取什么、何时获取,以及您的销售数据如何回到服务器——这一层次的细节对开发者、集成商以及希望了解 POS 对其主机做了什么的店主很有用。
每个商店和收银员各一个引擎
在每台设备上,POS 为 每个站点 + 商店 + 收银员的组合运行一个同步引擎。该引擎拥有自己的本地数据库,因此切换商店或收银员切换的是整个数据平面,而不是在同一堆共享记录上做筛选。按收银员隔离是有意为之:同一台设备上的两名收银员绝不会共享本地数据。
升级时,本地数据库绝不会就地迁移——应用会启动一个全新的数据库并从服务器重新下载,服务器始终持有权威副本(参见 从 v1.9 升级)。
在 Pro 多商店安装中,每个同步请求都会标明自己所属的商店,因此在收银台修改的价格更新的是该商店的价格,而不是网店的价格。
后台运行的内容
引擎按计划执行的每一项工作都是一个 通道——一个有名称、有边界的后台工作单元。通道分为三组:
| 分组 | 通道 | 默认节奏 |
|---|---|---|
| 拉取 | 变更检查(变更信号) | 10 秒 – 5 分钟,由您的 同步预设 决定 |
| 最近订单、产品目录预填充、参考数据预填充 | 约 5 分钟 | |
| 客户涓流下载(仅在空闲时) | 约 5 分钟 | |
| 推送 | 写入排空——发送已排队的本地更改 | 约 10 秒 |
| 维护 | 完整性与删除审计、服务器总数刷新 | 每隔几分钟到约 17 分钟 |
每个通道都满足两条特性:
- 每个通道都声明单次运行的请求上限。 任何通道都不得在一次运行中发出数量不受限的请求;较大的任务使用带可恢复游标的有界批次。这是一项核心设计不变式,同步性能 中有深入介绍。
- 维护工作让位于收银员。 审计和后台预热在让收银台可以开始销售的交互性工作 之后 运行,绝不会抢在前面;而且一旦您的服务器出现压力迹象,它们是最先被暂停的。
上述节奏都是默认值。检查间隔和每次请求的记录数可由商家在 POS 的 商店健康 → 性能 中按设备调整——请参见 商店健康。
POS 如何得知发生了变更
引擎不会为了弄清数据是否变化而重新下载数据。服务器维护一份变更日志,POS 轮询一个轻量的 变更检查,它只回答一个问题:自我上次所处的位置以来,有任何东西变动过吗?
- 一次检查覆盖八个集合——产品、变体、税率、客户、优惠券、分类、品牌和标签。订单被有意排除在变更检查之外;订单的时效性由它自己的最近订单通道负责,这也是产品更新和订单更新可能以不同节奏到达的原因。
- 空闲的收银台几乎不产生开销。 引擎发送条件请求:当没有任何变化时,服务器只回应一个无正文的
304 Not Modified。安静的商店会稳定在每次检查一个极小响应的水平。 - 检查带有 ±20% 的抖动,这样同一站点上的多台收银机会彼此错开,而不是同步地对服务器发起突发请求。
- 空闲衰减: 在 10 分钟无交互之后,检查会被拉长(下限为 60 秒)。任何真实活动——触摸、按键、一次被接受的条形码扫描——都会立即恢复到完整节奏,并触发一次即时的追赶检查。衰减只会拉长间隔,绝不会比所配置的设置轮询得更快。
- 停用数天的收银台会重新建立基线,而不是回放历史。 如果变更日志相对收银台上次所处的位置已经推进太远,逐行回放会耗费数百个请求。引擎会改为把游标跳到最新位置,并重新检查设备已持有内容在服务器上的当前状态——因此开销取决于本地副本的规模,而不是收银台离开了多久。
屏幕如何获得数据
在 v1.10.0 中,屏幕不再运行自己的同步。屏幕只声明它正在显示什么——搜索词、筛选条件、排序、页码——由引擎决定这是否需要发起请求。 每次声明会以三种方式之一得到解决:
- 已获取——引擎在网络上做了工作来满足它。
- 本地满足——设备已经拥有答案,或者一次相同的近期获取已经覆盖了它。
- 被取代——屏幕已经变化(您滚动了页面、更换了筛选条件),一个更新的声明取代了它。这是常规现象,不是错误。
只能在本地回答的筛选条件永远不会发送到服务器。而且,一次成功的声明并不意味着某个集合已完整下载——完整性是单独跟踪的(见下一节)。
并非所有内容都会被急切地下载,启动时设备上有什么内容也因集合而异:
- 预填充——产品通过有界的目录预填充逐步填满;税率在启动时拉取(没有税率,POS 无法完成购物车计算)。
- 按需下载,外加空闲涓流——客户没有急切的预填充。客户涓流下载在每个空闲间隔下载一小批,只要收银员处于活动状态就完全跳过;新增或变更的客户则通过变更检查到达。
- 首次打开时获取——分类、标签、品牌和优惠券在收银员首次打开它们时拉取。没有人打开的集合永远不会产生任何请求。
此外,变体选择器每次打开时会刷新一次价格和库存,因此已在本地的变体可以立即渲染,但绝不会显示数天前的过期库存。
“是否全部下载完了?”——诚实的覆盖率
像 “5,000 件产品中的 1,240 件” 这样的读数,其分母需要一个 服务器端 总数。引擎为每个集合维护一个这样的总数(大约每 15 分钟刷新一次,通常没有额外开销——真实的同步响应本身就带有总数,因此很少需要专门发一个请求),并遵守严格的诚实约定:
- 过期或缺失的服务器总数会显示为 正在检查…——绝不会悄悄用本地计数代替。用本地数字作分母永远会显示 100%,恰好掩盖了这个数字本该揭示的差距。
- 当引擎无法为完整性背书时,结论就是 未知,并且界面会把该数字标注为本地计数。
- 订单覆盖率条是相对于 整个 服务器订单历史来衡量的,而收银台有意只保留未结和最近的订单——所以健康的收银台在那里显示为部分覆盖,是设计使然。
这些数字会呈现在 商店健康 → 数据库 中,同时还有一个 可开始销售 里程碑:只要设备上有了第一件产品,它就会翻转为已达成——离线并不会阻止它,因为离线销售正是重点所在。请参见 商店健康。
更改如何回到您的商店
每一次本地写入——一笔销售、一次产品编辑、一次客户更新——都会先落入设备上的一个 持久化出站队列,然后由排空通道每隔几秒把队列推送到 WooCommerce。这正是离线销售安全的原因:在无连接状态下开出的销售会留在队列中,并在连接恢复时排空发出。
值得关注的细节:
- 购物车写入按订单串行化。 扫描枪连续快速扫描时会逐条应用,重复添加会合并到它所重复的那一行,而不是排队生成第二行。
- 订单确认会采纳服务器的副本。 WooCommerce 在创建时会为订单行项目分配 ID;引擎会采纳已确认的订单,使后续更新对应到这些行,而不是追加重复项。采纳是保守的——它绝不会覆盖服务器尚未见过的本地编辑,并且只适用于订单。
- 服务器永久拒绝的写入绝不会被无声重试。 它会连同服务器给出的原因一起被搁置,并呈现在 商店健康 → 数据库 的 “从未送达服务器的更改” 中,附带两个明确的操作:重新发送(根据记录当前的状态重新构建请求,因此之后所做的修正会生效)和 放弃。这里没有自动重试循环——恢复始终是一次可见且刻意的操作。面向商家的操作说明请参见 商店健康。
多个浏览器标签页
支持在同一商店的多个标签页中运行 Web 版 POS。每个标签页都可以开单——写入会追加到共享队列——但对于每个商店 + 收银员作用域,由一个被选举出来的标签页负责发送。如果该标签页关闭,浏览器会自动把下一个提升上来。以不同收银员身份登录的两个标签页属于不同作用域,各自管理自己的队列。
本地存储
Web 版和桌面版应用通过 OPFS(源私有文件系统)工作线程 存储数据;iOS 和 Android 应用通过文件系统引擎使用相同的磁盘格式。四个平台共用一种存储格式和同一套损坏恢复工具。(更早的版本在 Web 上使用 IndexedDB,在原生端使用 SQLite。)
查询在数据库层内部执行——选择器、排序和分页——因此只有可见的那一页数据会跨入应用。在一个包含 10,000 笔订单的合成测试数据上,这种下推把实时订阅下的单次写入更新开销从约 27 毫秒降到了约 0.06 毫秒。
从 v1.9 升级
v1.10.0 不迁移本地数据库——它执行一次 冷重新同步:
- 首次启动时,应用会打开一个全新的本地数据库并从您的商店重新下载。每台设备都会经历一次性的完整重新下载。
- 不会丢失任何东西:您的 WooCommerce 服务器是所有已同步数据的权威副本。待发送的未提交更改会在旧数据被清理 之前 排空或呈现出来——升级不可能毁掉一笔未发送的销售。
- 应用与插件同步发布。 v1.10.0 客户端使用插件的 v2 同步 API。如果只升级了其中一侧,请求会以
rest_no_route错误失败——这个特征意味着“请更新另一半”,而不是安装损坏。
v1.10.0 有哪些变化
| v1.9.x | v1.10.0 | |
|---|---|---|
| 同步单位 | 每个挂载的屏幕查询一条复制流 | 每个站点 + 商店 + 收银员一个引擎 |
| 什么驱动获取 | 屏幕挂载 | 屏幕声明它需要什么 |
| 变更检测 | 定期轮询 + 每小时全量审计 | 变更日志游标配合条件式 304 响应 |
| 服务器总数 | 不跟踪 | 各集合的总数,并带有诚实的 正在检查… 状态 |
| 失败的写入 | 不透明地重试 | 持久化队列、可见的恢复面板、无无声重试 |
| 多标签页 Web | 不协调 | 每个作用域一个被选举的发送方 |
| 本地搜索 | 按词首匹配 | 子串匹配(最少 3 个字符) |
| 存储 | IndexedDB(Web)、SQLite(原生) | 所有平台均采用 OPFS 格式存储 |
| 同步调优 | 固定 | 商店健康中按设备提供的预设与调节项 |
有关引擎背后的性能理念——请求上限、服务器压力下的退避以及实测数据——请参见 同步性能。