Separate looted currency and preserve growing realm state in atomic chunks
Core tests / core (push) Successful in 49s
Core tests / core (push) Successful in 49s
This commit is contained in:
+15
-6
@@ -1,4 +1,5 @@
|
||||
import { env } from 'cloudflare:workers';
|
||||
import { splitState } from './state-chunks';
|
||||
import { seed } from './seed';
|
||||
import type { State,StateStore,Bot,Recipe,Home } from './types';
|
||||
|
||||
@@ -68,13 +69,21 @@ export class D1Store implements StateStore {
|
||||
if(!env.DB) throw new Error('Realm database unavailable');
|
||||
const initial=seed();
|
||||
await env.DB.prepare('INSERT OR IGNORE INTO realm_state (id,revision,payload) VALUES (?,?,?)').bind('realm',0,JSON.stringify(initial)).run();
|
||||
const row=await env.DB.prepare('SELECT payload FROM realm_state WHERE id=?').bind('realm').first<{payload:string}>();
|
||||
if(!row)throw new Error('Realm state unavailable');
|
||||
return migrate(JSON.parse(row.payload));
|
||||
await this.chunks();
|
||||
const result=await env.DB.prepare("SELECT s.payload AS manifest,c.payload AS chunk FROM realm_state s LEFT JOIN realm_state_chunks c ON c.token=json_extract(s.payload,'$.chunkToken') WHERE s.id=? ORDER BY c.part").bind('realm').all<{manifest:string;chunk:string|null}>();
|
||||
const rows=result.results;if(!rows.length)throw new Error('Realm state unavailable');
|
||||
const manifest=JSON.parse(rows[0].manifest);
|
||||
if(manifest.chunkToken){if(rows.length!==manifest.parts||rows.some(r=>r.chunk===null))throw new Error('Realm state chunks incomplete');return migrate(JSON.parse(rows.map(r=>r.chunk).join('')));}
|
||||
return migrate(manifest);
|
||||
}
|
||||
private async chunks(){await env.DB!.prepare('CREATE TABLE IF NOT EXISTS realm_state_chunks (token TEXT,part INTEGER,payload TEXT,PRIMARY KEY(token,part))').run();}
|
||||
async save(s:State,expected:number){
|
||||
s.revision=expected+1;
|
||||
const result=await env.DB!.prepare('UPDATE realm_state SET revision=?,payload=? WHERE id=? AND revision=?').bind(s.revision,JSON.stringify(s),'realm',expected).run();
|
||||
if(result.meta.changes!==1)throw new Error('State changed in another request. Refresh and try again.');
|
||||
await this.chunks();s.revision=expected+1;
|
||||
const token=crypto.randomUUID(),parts=splitState(s),manifest=JSON.stringify({chunkToken:token,parts:parts.length});
|
||||
const statements=[env.DB!.prepare('UPDATE realm_state SET revision=?,payload=? WHERE id=? AND revision=?').bind(s.revision,manifest,'realm',expected),
|
||||
...parts.map((part,i)=>env.DB!.prepare('INSERT INTO realm_state_chunks(token,part,payload) SELECT ?,?,? WHERE EXISTS (SELECT 1 FROM realm_state WHERE id=? AND payload=?)').bind(token,i,part,'realm',manifest)),
|
||||
env.DB!.prepare('DELETE FROM realm_state_chunks WHERE token<>? AND EXISTS (SELECT 1 FROM realm_state WHERE id=? AND payload=?)').bind(token,'realm',manifest)];
|
||||
const result=await env.DB!.batch(statements);
|
||||
if(result[0].meta.changes!==1)throw new Error('State changed in another request. Refresh and try again.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user