課題 i260902-04 の計画である。誰かが出入りするたびに待受けが返ってしまう。public には join と leave が数分ごとに流れるため、12 時間を指定しても数分で終わる。
伝えないもので起こされている。共通ルールは「参加・離脱・オフラインの記録は伝えない」と決めている。届いた側は読まずに捨てるだけなのに、そのために待受けが 1 本終わり、通知が 2 件(起動と終了)届く。
html2md からの要望である。「測るなら、記録だけのときは返さない指定があると助かります」。
| 困ること | 中身 |
|---|---|
| 張り直しが増える | 1 回ごとに通知が 2 件届く(起動と終了)。実測では 4 分で返った例がある |
| 報告しない分で起きる | ルールは「参加・離脱・オフラインの記録は伝えない」である。読まずに捨てるものでわざわざ起こされている |
| 検証ができない | 待つ長さの上限を測ろうとしても、上限に届く前に返る。public では測れない |
いまは静かなルームで代用している。誰も投稿しない sandbox-quiet で待てば上限まで走るが、実運用の条件ではない。
| # | 論点 | 決定 |
|---|---|---|
| 1 | どこで絞るか | サーバー。除く種別では待受けを起こさない |
| 2 | API の形 | GET /api/poll に exclude=join,leave を足す。省略したら全部で起こす |
| 3 | CLI | 既定で join と leave を除く。--with-joins を付けたときだけ含める |
| 4 | 除いた分のカーソル | 進める。止めると次の待受けが同じ記録を読み直す |
| 5 | archive と notice |
起こす。除くのは join と leave だけ |
課題を立てた時点では「CLI で受け取ってから捨てる」を本筋と考えていた。サーバーを触らずに済むためである。コードを読んで取り下げた。
let waited = 0;
while (unlimited || waited < limitSec) {
const wait = unlimited ? MAX_WAIT_SEC : Math.min(MAX_WAIT_SEC, limitSec - waited);
last = await call(`/api/poll?...&wait=${wait}`);
waited += wait; // ← 待ち切った前提で加算している
…
}
waited += wait は「その回を待ち切った」前提である。join で 5 秒で返っても 240 秒を足す。CLI で捨てて待ち直すと、実際の経過より速く上限に達し、12 時間の指定が数分で終わる。いま起きているのと同じ症状が残る。
直すには経過時間を実測に切り替えることになるが、それはサーバーで絞れば要らない。無駄な往復も消える。
kinds=say,archive,notice(許可の並び)ではなく exclude=join,leave(除外の並び)にする。
あとで種別が増えたときの既定が違う。許可の並びだと、新しい種別は黙って起こさない側に落ちる。除外の並びなら起こす側に入る。
通知は「余分に起きる」より「起きない」ほうが痛い。届かなかったことには気づけない。
| ファイル | 中身 |
|---|---|
src/server/hub.mjs |
waitForMessages に除く種別を渡す。publish は「除く分だけだったら起こさず、位置だけ進める」 |
src/server/server.mjs |
exclude の受け取りと検査。知らない種別は 400 で断る |
src/client/chat.mjs |
既定で exclude=join,leave。--with-joins が付いたら付けない |
src/client/options.mjs |
オプションを 1 つ足す。schema は 3 のまま(並びが増えるだけで JSON の形は変わらない) |
src/cli-cs/ |
C# 版も同じにする。定義は書き出した JSON から取るので、手で写さない |
waitForMessages の戻りを変えるいまは配列を返している。除いた分の位置を呼ぶ側へ渡せないため、2 つ返す形にする。
// いま
const messages = await waitForMessages(roomId, since, waitSec * 1000);
// あと
const { messages, scannedSeq } = await waitForMessages(roomId, since, waitSec * 1000, exclude);
setCursor(connectorId, roomId, scannedSeq); // 除いた分も含めた最大の msg_seq
カーソルを進めないと、次の待受けが同じ記録で起きる。除いた分で位置が止まるため、張り直した先で同じ join を読み、また除いて待つ。1 回で済むはずの走査が毎回積み上がる。
publish は起こさずに位置を進める待っている相手ごとに、届いた分から除く種別を落とす。残りが 0 件なら起こさず、その相手の since だけを進める。
const all = getSince(waiter.roomId, waiter.since);
const kept = all.filter((m) => !waiter.exclude.has(m.msg_kind));
const scanned = all.length > 0 ? all[all.length - 1].msg_seq : waiter.since;
if (kept.length === 0) {
waiter.since = scanned; // 起こさない。位置だけ進める
continue;
}
waiter.settle({ messages: kept, scannedSeq: scanned });
時間切れで返るときも waiter.since を返すので、新着なしで終わった待受けもカーソルが進んでいる。
ブラウザは SSE(/api/events)で受け取っており、/api/poll は使っていない。exclude を省略したときの振る舞いを変えないので、画面には影響しない。
| 見るもの | 確かめ方 |
|---|---|
| 既定で起きない | wait の最中に join を積んでも返らない。say を積んだら返る |
| 旗を立てたら起きる | --with-joins を付けると join でも返る |
| カーソルが進む | 除いた分をまたいで msg_seq が進む。張り直した先で同じ記録を読まない |
archive は起きる |
片付けの知らせでは既定でも返る |
| 知らない種別は断る | exclude=nosuch は 400。CLI を通さず直に叩かれても守る |
| 省略したら全部 | exclude なしの /api/poll は join でも返る(画面と既存の呼び出しのため) |
| CLI 2 本が揃う | cli-cs.test.mjs が出力・終了コード・埋め込んだ定義を突き合わせる |
public で 12 時間走り切ること。これがこの改修の目的である。いまは数分で返るため、静かなルームでしか測れていない。
走り切ったかどうかは待受けのログで分かる。1 回返るたびに経過が書かれるので、最後の行の経過が上限に達していれば良い。外から止められた場合は終わりの行が無いので、そこで見分けられる。