两周前那块叫号屏停在 41。按钮叫的是工具名,而那个工具并不存在。
这次接上。不过在接之前有一件事要先定 —— 那个数字是谁拿着的。
这为什么是个问题
屏幕拿着,代码最短。按下按钮,屏幕里的值加一,屏幕重画。服务端不用知道。
而且 加第二块面板那天就完了。 候诊室门口一块,柜台旁边一块。各数各的,两块显示不同的号。客人看眼前的,员工看旁边的。
号不是屏幕的。是柜台的。
柜台拿着
所以状态放在服务端。
var _now = 41;
var _issued = 41;
now 是正在叫的号,issued 是最后发出去的号。
两个工具动这两个值。
server.addTool(
name: 'queue.next',
description: 'Call the next number',
inputSchema: const {'type': 'object', 'properties': {}},
handler: (args) async {
// The counter refuses to pass the last ticket it issued. Without this
// the display runs ahead of the room and calls numbers nobody holds.
if (_now >= _issued) return _state(notice: 'nobody is waiting');
_now++;
return _state(notice: 'called $_now');
},
);
先验拒绝
接上之后第一个验的,不是能成的情况,而是不该成的情况。
没人在等,按下「Call next」。
pressed Call next -> now 41 ("nobody is waiting")
the counter refused: it will not call past the last ticket issued
停在 41 没往上走。 没有这道检查,屏幕就会跑到房间前面去——叫一个没人拿着的 42,再叫 43,等真拿着 42 的人过来时早就过去了。
现在会动了
取三张票。
three tickets taken -> issued 44, 3 waiting

叫两次。
called twice -> now 43, 1 waiting

43,还剩一个。 41 + 2 = 43,44 − 43 = 1。不是屏幕算的,是柜台算好送过来的。
屏幕那边的代码里既没有 41 也没有 +1。只有 {{now}} 和 {{waiting}} 两个位置。
拔掉,再插上
这是这一篇最重要的部分。
叫号屏是会被拔的。断电、换面板、打扫时碰到线。那一刻会发生什么,不试就不知道。
所以把连接断掉,换了一块显示装置接上。
display disconnected — the server process ends with it
a different display connects
it reads 43/44 before, 41/41 after

回到了 41。 等待人数也是 0。
这不是缺陷,而是这个样例实际就是这么做的这一事实。状态在 服务端进程的内存里,显示装置是用 stdio 接的,所以显示装置一死,服务端进程跟着一起死。队列跟着进程一起没了。
the number did NOT survive: this counter keeps its queue in memory,
so the process ending took the queue with it