两个渠道之间的缺口不是靠速度填补的。真正填补它的,是让所有渠道读取同一个库存池,而不是各自持有互相信任的多份副本。
几乎没有一次超售是因为系统太慢造成的。真正的原因是同一间房在多个地方被同时记录,而每个地方都只相信自己手上的那份数据。只要真相存在多个版本,两个版本之间迟早会出现矛盾的那一刻——速度只能缩短这个瞬间,永远无法让它消失。
想象周六晚上最后一间房,同时挂在两个渠道上。两位客人几乎同时下单。每个渠道各自检查自己那份库存数据,看到房间还空着,于是都接受了预订。等到关房的指令传到第二个渠道时,第二笔预订已经成立了。
没有人犯错。两个渠道都完全按照设计运作。问题出在架构上:两个系统被允许同时认为自己拥有同一间房。
用 iCal 串联日历通常是大多数人迈出的第一步,这也合情合理。但 iCal 是一种“拉取式”格式:对方会按照自己的节奏去抓取你的日历文件,通常间隔是几十分钟,平台繁忙时甚至更久。它从来不是即时的,也从来没有任何保证。
从 iCal 换成真正的实时连接,能把这个窗口从几分钟大幅缩短到几秒钟。这确实是实实在在的进步,也能消除大部分事故。但它消除不了这个问题的根源,因为推送式的设计本质上仍然意味着存在多份副本,只是事后再去协调。
还有一种更隐蔽的失败,是速度解决不了的。有些渠道会对库存做自己的自动调整——比如取消预订到达时自动把房间加回去。如果你的系统在渠道正在应用自己的增减值的同一时刻,写入一个绝对数值,两个操作的先后顺序可能颠倒,结果有时会比真实情况更高。
结构性的解决办法是停止保留多份副本。库存数量由单一的真实房间池计算得出,每个渠道只是这个池子的一个视图,而不是其中一部分的所有者。无论预订从哪里进来,这个池子都会相应减少,每个渠道都会被修正到与新数量一致。
最后一点比看起来更重要。一个读不到渠道当前真实状态的系统,只有两个选择:猜,或者承认读不到。往“大概还有空房”的方向猜测,正是房间被卖两次的典型原因。拒绝瞎猜,才是一个“通常是对的”系统和一个“客满时真正靠得住”系统之间的差别。