mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-15 23:24:52 +09:00
Tag prefetched copies with the controller's run token
If the controller crashes after sending generation N but before saving it, the destination keeps that prefetch while the controller restarts at N-1, and another instance can then upload different data as generation N. The controller now picks a random token per run, sends it with every prefetch and acquire reply, and the scenario applies a held copy only when its generation and token both match, and lets a new run's copy replace one with another token. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
ca7caedfbe
commit
5e4dcada50
6 files changed
+84
-18
No files matched your search
@@ -112,7 +112,10 @@ describe("player_sync controller", function() {
|
||||
await handler(msg.UploadRequest)(new msg.UploadRequest(instanceId, data.name, data));
|
||||
}
|
||||
async function acquireResponse(instanceId, playerName) {
|
||||
return await handler(msg.AcquireRequest)(new msg.AcquireRequest(instanceId, playerName));
|
||||
const response = await handler(msg.AcquireRequest)(new msg.AcquireRequest(instanceId, playerName));
|
||||
// Random per controller run, the run token tests cover it
|
||||
response.token = undefined;
|
||||
return response;
|
||||
}
|
||||
|
||||
async function loadWithInstances() {
|
||||
@@ -404,7 +407,8 @@ describe("player_sync controller", function() {
|
||||
}
|
||||
const data = generation => ({ name: "test", generation });
|
||||
async function stored(controller) {
|
||||
return (await mock.getHandler(controller, msg.DownloadRequest)(new msg.DownloadRequest(2, "test"))).playerData;
|
||||
const download = mock.getHandler(controller, msg.DownloadRequest);
|
||||
return (await download(new msg.DownloadRequest(2, "test"))).playerData;
|
||||
}
|
||||
|
||||
it("should send the stored data to a running destination with player_sync on", async function() {
|
||||
@@ -415,6 +419,24 @@ describe("player_sync controller", function() {
|
||||
assert.ok(sent[0].message instanceof msg.PrefetchEvent);
|
||||
assert.deepEqual(sent[0].message.playerData, data(1));
|
||||
});
|
||||
it("should tag the prefetch and acquire reply with one token per controller run", async function() {
|
||||
const { controller, sent, upload } = await setup();
|
||||
await upload(new msg.UploadRequest(1, "test", data(1), 2));
|
||||
const acquire = await mock.getHandler(controller, msg.AcquireRequest)(new msg.AcquireRequest(2, "test"));
|
||||
assert.equal(acquire.status, "acquired");
|
||||
assert.ok(sent[0].message.token);
|
||||
assert.equal(acquire.token, sent[0].message.token);
|
||||
|
||||
const again = await setup();
|
||||
await again.upload(new msg.UploadRequest(1, "test", data(1), 2));
|
||||
assert.notEqual(again.sent[0].message.token, sent[0].message.token);
|
||||
});
|
||||
it("should not need a token on a busy reply", async function() {
|
||||
const { controller } = await setup();
|
||||
const acquire = mock.getHandler(controller, msg.AcquireRequest);
|
||||
await acquire(new msg.AcquireRequest(3, "test"));
|
||||
assert.equal((await acquire(new msg.AcquireRequest(2, "test"))).token, undefined);
|
||||
});
|
||||
it("should send the stored copy, with the parts the upload's components left out", async function() {
|
||||
const { sent, upload } = await setup();
|
||||
await upload(new msg.UploadRequest(1, "test", { ...data(1), force: "player", tag: "first" }));
|
||||
|
||||
Reference in new issue
Block a user