| … | … | @@ -86,6 +86,33 @@ export class Room extends ReadyResource { |
| 86 | 86 | ackInterval: 1000, |
| 87 | 87 | ackThreshold: 4 |
| 88 | 88 | }) |
| 89 | |
| 90 | // Resilience (pear-git #3): autobase routes any drain/apply failure through |
| 91 | // _onError → emit('error'). With NO 'error' listener, EventEmitter re-throws, |
| 92 | // so ONE broken tracker SIGABRTs the whole daemon — crash-looping and taking |
| 93 | // every other repo down. A standalone peer hits this when its stale local |
| 94 | // state can't reconcile a manifest migration that happened in the group, |
| 95 | // whereas full-replica mirrors never do (they take the finished picture). |
| 96 | // Attach handlers so a broken tracker is ISOLATED + observable instead of |
| 97 | // fatal — matching keet's room-autobase 'error'/'warning' handlers. Consumers |
| 98 | // react to the re-emitted 'base-error' (Increment 2: fresh-namespace re-sync). |
| 99 | // Custom event names never throw when unlistened, so re-emitting is safe. |
| 100 | this._baseError = null |
| 101 | this._base.on('error', (err) => { |
| 102 | this._baseError = err |
| 103 | try { |
| 104 | // eslint-disable-next-line no-console |
| 105 | console.error('[hyper-rooms] tracker autobase error (ns=' + this._namespace + '): ' + (err && err.message ? err.message : err)) |
| 106 | } catch {} |
| 107 | this.emit('base-error', err) |
| 108 | }) |
| 109 | this._base.on('warning', (err) => { |
| 110 | try { |
| 111 | // eslint-disable-next-line no-console |
| 112 | console.error('[hyper-rooms] tracker autobase warning (ns=' + this._namespace + '): ' + (err && err.message ? err.message : err)) |
| 113 | } catch {} |
| 114 | this.emit('base-warning', err) |
| 115 | }) |
| 89 | 116 | } |
| 90 | 117 | |
| 91 | 118 | // ── Lifecycle ──────────────────────────────────────────────── |
| … | … | @@ -112,6 +139,14 @@ export class Room extends ReadyResource { |
| 112 | 139 | /** True if this peer can append ops. */ |
| 113 | 140 | get writable () { return this._base.writable } |
| 114 | 141 | |
| 142 | /** |
| 143 | * The autobase error that isolated this room, if any (resilience, #3). |
| 144 | * Non-null once the tracker's autobase has emitted 'error' (e.g. a stale |
| 145 | * local state that couldn't reconcile a group manifest migration). A consumer |
| 146 | * treats a non-null value as "this tracker is broken — re-sync it fresh". |
| 147 | */ |
| 148 | get errored () { return this._baseError } |
| 149 | |
| 115 | 150 | /** Read-only access to the Hyperbee view. */ |
| 116 | 151 | get view () { return this._base.view } |
| 117 | 152 | |