General Actions to handlers Refacto
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
|
||||
export class PrepareQuorum {
|
||||
|
||||
constructor({ ackChannel, timeoutMs, matchesChan, debug = false }) {
|
||||
this.ackChannel = ackChannel
|
||||
this.timeoutMs = timeoutMs
|
||||
this.matchesChan = matchesChan
|
||||
this.debug = debug
|
||||
this.expected = new Set()
|
||||
this.ready = new Map()
|
||||
this.simulationId = null
|
||||
this.active = false
|
||||
this.resolve = null
|
||||
this.timer = null
|
||||
}
|
||||
|
||||
begin(expectedParticipantIds, simulationId) {
|
||||
this.cancel()
|
||||
this.expected = new Set(expectedParticipantIds)
|
||||
this.ready.clear()
|
||||
this.simulationId = simulationId
|
||||
this.active = true
|
||||
|
||||
if(this.debug) {
|
||||
console.log(
|
||||
`[Maestro] Prepare quorum armed: ${this.expected.size} participant(s) ` +
|
||||
`(agents + primordial daemons), timeout ${this.timeoutMs}ms`
|
||||
)
|
||||
}
|
||||
|
||||
return(new Promise(resolve => {
|
||||
this.resolve = resolve
|
||||
this.timer = setTimeout(() => {
|
||||
const missing = [...this.expected].filter(id => !this.ready.has(id))
|
||||
this.#finish({
|
||||
ok: false,
|
||||
err: `Timeout waiting for readyToStart (${this.timeoutMs}ms); ` +
|
||||
`missing: ${missing.join(', ') || 'unknown'}`,
|
||||
})
|
||||
}, this.timeoutMs)
|
||||
}))
|
||||
}
|
||||
|
||||
handleMessage(msg, chan) {
|
||||
if(!this.active) return(false)
|
||||
if(typeof(this.matchesChan) !== 'function') return(false)
|
||||
if(!this.matchesChan(chan, this.ackChannel)) return(false)
|
||||
if(msg?.eventType !== 'readyToStart') return(false)
|
||||
|
||||
const payload = msg.payload ?? {}
|
||||
if(payload.simulationId !== this.simulationId) return(false)
|
||||
|
||||
const sender = msg.sender
|
||||
if(!sender || !this.expected.has(sender)) {
|
||||
if(this.debug) {
|
||||
console.warn(`[Maestro] Ignoring readyToStart from unexpected participant: ${sender}`)
|
||||
}
|
||||
return(true)
|
||||
}
|
||||
|
||||
if(!payload.success) {
|
||||
this.#finish({
|
||||
ok: false,
|
||||
err: payload.err ?? `Participant ${sender} failed prepare`,
|
||||
})
|
||||
return(true)
|
||||
}
|
||||
|
||||
this.ready.set(sender, payload)
|
||||
if(this.debug) {
|
||||
console.log(
|
||||
`[Maestro] readyToStart from ${sender} ` +
|
||||
`(${this.ready.size}/${this.expected.size})`
|
||||
)
|
||||
}
|
||||
|
||||
for(const participantId of this.expected) {
|
||||
if(!this.ready.has(participantId)) return(true)
|
||||
}
|
||||
|
||||
this.#finish({ ok: true })
|
||||
return(true)
|
||||
}
|
||||
|
||||
cancel() {
|
||||
if(this.timer) {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
this.active = false
|
||||
this.resolve = null
|
||||
this.expected.clear()
|
||||
this.ready.clear()
|
||||
this.simulationId = null
|
||||
}
|
||||
|
||||
#finish(result) {
|
||||
const resolve = this.resolve
|
||||
this.cancel()
|
||||
if(typeof(resolve) === 'function') resolve(result)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user