Fix concurrent checklist sync and consistent actions
This commit is contained in:
parent
a33f7a297d
commit
8d5a94d7fa
10 changed files with 124 additions and 20 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import tempfile
|
||||
import unittest
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
import app
|
||||
|
|
@ -30,6 +31,21 @@ class StoreTests(unittest.TestCase):
|
|||
self.assertFalse(snapshot["checks"]["crop-parsnip"])
|
||||
self.assertEqual(snapshot["revision"], 2)
|
||||
|
||||
def test_two_clients_keep_distinct_concurrent_changes(self):
|
||||
second_client = app.Store(self.store.path)
|
||||
|
||||
def write(index):
|
||||
store = self.store if index % 2 else second_client
|
||||
store.set_check(f"concurrent-item-{index}", True, f"player-{index % 2}")
|
||||
|
||||
with ThreadPoolExecutor(max_workers=2) as pool:
|
||||
list(pool.map(write, range(20)))
|
||||
|
||||
snapshot = self.store.snapshot()
|
||||
self.assertEqual(snapshot["revision"], 20)
|
||||
self.assertEqual(len(snapshot["checks"]), 20)
|
||||
self.assertTrue(all(snapshot["checks"].values()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
30
tests/test_sync.js
Normal file
30
tests/test_sync.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const assert = require("node:assert/strict");
|
||||
const sync = require("../static/sync.js");
|
||||
|
||||
// A second client advances the shared revision while this client is saving.
|
||||
// The in-flight local value must not be replaced by the older server value.
|
||||
const local = { "crop-parsnip": true, "fish-eel": false };
|
||||
const remoteWhileSaving = {
|
||||
revision: 6,
|
||||
checks: { "crop-parsnip": false, "fish-eel": true }
|
||||
};
|
||||
const merged = sync.mergeSnapshot(local, new Set(["crop-parsnip"]), 5, remoteWhileSaving);
|
||||
assert.equal(merged.applied, true);
|
||||
assert.equal(merged.checks["crop-parsnip"], true);
|
||||
assert.equal(merged.checks["fish-eel"], true);
|
||||
|
||||
// A response already superseded by a later write must never roll the client back.
|
||||
const stale = sync.mergeSnapshot(merged.checks, new Set(), 7, remoteWhileSaving);
|
||||
assert.equal(stale.applied, false);
|
||||
assert.equal(stale.revision, 7);
|
||||
|
||||
// The write response is authoritative for its own item even after an interleaved poll.
|
||||
const confirmed = sync.confirmWrite(merged.checks, 6, {
|
||||
itemId: "crop-parsnip",
|
||||
checked: true,
|
||||
revision: 7
|
||||
});
|
||||
assert.equal(confirmed.checks["crop-parsnip"], true);
|
||||
assert.equal(confirmed.revision, 7);
|
||||
|
||||
console.log("sync race tests passed");
|
||||
Loading…
Add table
Add a link
Reference in a new issue