// Repro hunt: the daemon SIGABRTs ~seconds after the game starts spamming
// state.set (presence) while polling state.list. The game issues many
// CONCURRENT state.set RPCs → many concurrent base.append()+update() on one
// StateBase local writer. Our serialized probes never hit it. This hammers
// concurrent (non-serialized) writes + interleaved reads to surface the
// abort WITH its error text (no TUI to swallow it).
//
// bare test/probe-state-hammer.js
// If it aborts, the preceding stderr is the root cause we need.
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
import crypto from 'hypercore-crypto'
import process from 'bare-process'
import { StateBase } from '../lib/state-base.js'
process.on('uncaughtException', (e) => { console.error('UNCAUGHT EXCEPTION:', e && e.stack || e); Bare.exit(3) })
process.on('unhandledRejection', (e) => { console.error('UNHANDLED REJECTION:', e && e.stack || e); Bare.exit(3) })
const roomTag = crypto.hash(Buffer.from('hammer-room', 'utf-8'))
const dir = path.join(os.tmpdir(), 'probe-hammer-' + Date.now())
fs.mkdirSync(dir, { recursive: true })
// network:false isolates the autobase from replication — tests whether the
// crash is intrinsic to concurrent local writes vs needs a remote peer.
const sb = new StateBase(dir, roomTag, { network: false })
let exitCode = 1
try {
await sb.ready()
await sb.ensureFounder()
console.log('founder ready, writable=', sb.writable)
// Round 1: concurrent fire-and-forget writes (like presence spam) +
// interleaved reads, NOT serialized — mirrors many concurrent UDS RPCs.
console.log('round 1: 300 concurrent set() + interleaved list()...')
let inflight = []
for (let i = 0; i < 300; i++) {
inflight.push(sb.set('presence:' + (i % 6), 'v' + i).catch(e => { console.error('set rejected:', e.message); throw e }))
if (i % 7 === 0) inflight.push(sb.list('').catch(e => { console.error('list rejected:', e.message); throw e }))
}
await Promise.all(inflight)
console.log('round 1 survived, keys=', Object.keys(await sb.list('')).length)
// Round 2: tight loop of unawaited writes (max concurrency) for 5s.
console.log('round 2: unawaited write storm for 5s...')
const t0 = Date.now()
let n = 0
while (Date.now() - t0 < 5000) {
sb.set('p:' + (n % 4), 'x' + n).catch(e => console.error('storm set rejected:', e.message))
n++
if (n % 50 === 0) await new Promise(r => setTimeout(r, 1))
}
await new Promise(r => setTimeout(r, 1500))
console.log('round 2 survived, issued', n, 'writes, keys=', Object.keys(await sb.list('')).length)
console.log('\nNO CRASH with network:false — the abort needs replication/a remote peer.')
exitCode = 0
} catch (err) {
console.error('CAUGHT:', err && err.stack || err)
} finally {
try { await sb.close() } catch {}
try { fs.rmSync(dir, { recursive: true, force: true }) } catch {}
Bare.exit(exitCode)
}