Expose mesh bounded retry timing in guild sync health rail.

Track per-guild mesh retry attempt metadata and publish countdown fields in view.guildSyncHealthRail so UI can present bounded-mode retry guidance without parsing logs.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-02 11:06:28 -04:00
co-authored by Cursor
parent 5440d8ff25
commit 0e69175eb5
2 changed files with 30 additions and 2 deletions
+1
View File
@@ -10,6 +10,7 @@ Application facade: one `PearcordPlatform` class that wires identity, database,
**Phase 410 hardening (v0.8.641):** Added cached pin snapshots in `view()` (`_pinListCacheKey/_pinListCacheGen` + `_invalidatePinListCache`) and broadened reaction snapshot cache reuse beyond light polls to reduce duplicate startup list scans.
**Phase 410 hardening (v0.8.643):** `view()` now emits cache-path span metadata for `pin.list`, `reaction.list`, and `discovery.list` (`cacheHit`, `source`), coalesces concurrent discovery listing fetches behind `_discoveryListPending`, and reports aggregate cache counters in `view.listCacheMetrics`.
**Phase 410 hardening (v0.8.643):** `view()` now supports explicit detail levels (`minimal`/`light`/`full`/`diagnostic`) through `opts.detail`, keeps legacy `opts.light` compatibility, and surfaces the resolved mode as `viewDetailLevel` in payload diagnostics.
**Phase 410 hardening (v0.8.643):** `guildSyncHealthRail` now carries bounded-retry metadata (`retryAttempt`, `retryDelayMs`, `retryMaxAttempts`, `nextRetryAt`, `retryCountdownMs`) so UI can show live “mesh bounded mode” retry countdown/tooltip state during background join retries.
**Phase 659 (v0.8.638):** DM scheduled messages slice — `sendMessage` parses `/schedule` and `/sendlater` in DM mode; queue APIs `createDmScheduledMessage`, `listDmScheduledMessages`, `cancelDmScheduledMessage`, `sendNowDmScheduledMessage`, plus due runner `runDueDmScheduledMessages()` and local timer flush. See [DM_SCHEDULED_MESSAGES.md](../../docs/DM_SCHEDULED_MESSAGES.md).
+29 -2
View File
@@ -499,6 +499,7 @@ class PearcordPlatform extends EventEmitter {
this._lastReplicationCompact = null
this._memberPageBurstTimestamps = []
this._guildMeshBoundedByGuild = new Map()
this._guildMeshPeerRetryMetaByGuild = new Map()
this._lastPartitionHeal = null
this._lastPartitionHealByGuild = new Map()
this._partitionHealInProgressByGuild = new Set()
@@ -513,7 +514,10 @@ class PearcordPlatform extends EventEmitter {
_setGuildMeshBounded (guildId, bounded) {
if (!guildId) return
if (bounded) this._guildMeshBoundedByGuild.set(guildId, true)
else this._guildMeshBoundedByGuild.delete(guildId)
else {
this._guildMeshBoundedByGuild.delete(guildId)
this._guildMeshPeerRetryMetaByGuild.delete(guildId)
}
}
_getPartitionHealCooldownUntil (guildId) {
@@ -639,7 +643,15 @@ class PearcordPlatform extends EventEmitter {
meshBounded: bounded,
partitionHealInProgress: healing,
syncProgressPct: Number(h?.syncProgressPct) || 0,
peers: Number(h?.peers) || 0
peers: Number(h?.peers) || 0,
retryAttempt: Number(this._guildMeshPeerRetryMetaByGuild.get(id)?.attempt) || 0,
retryDelayMs: Number(this._guildMeshPeerRetryMetaByGuild.get(id)?.delayMs) || 0,
retryMaxAttempts: Number(this._guildMeshPeerRetryMetaByGuild.get(id)?.maxAttempts) || 0,
nextRetryAt: Number(this._guildMeshPeerRetryMetaByGuild.get(id)?.nextRetryAt) || 0,
retryCountdownMs: Math.max(
0,
(Number(this._guildMeshPeerRetryMetaByGuild.get(id)?.nextRetryAt) || 0) - Date.now()
)
}
}
return out
@@ -12937,6 +12949,8 @@ class PearcordPlatform extends EventEmitter {
clearTimeout(this._guildMeshPeerRetryTimer)
this._guildMeshPeerRetryTimer = null
}
const activeGuildId = this.guild?.guild?.id
if (activeGuildId) this._guildMeshPeerRetryMetaByGuild.delete(activeGuildId)
this._guildMeshPeerRetryAttempts = 0
}
@@ -12944,6 +12958,12 @@ class PearcordPlatform extends EventEmitter {
if (this._guildMeshPeerRetryTimer) return
if ((this.guild?.peers?.size ?? 0) > 0) return
this._guildMeshPeerRetryAttempts = 0
this._guildMeshPeerRetryMetaByGuild.set(guildId, {
attempt: 0,
delayMs: 3000,
nextRetryAt: Date.now() + 3000,
maxAttempts: Number(process.env.PEARCORD_GUILD_MESH_PEER_RETRY_MAX) || 8
})
const attempt = () => {
if (!this.guild?.guild || this.guild.guild.id !== guildId) {
this._clearGuildMeshPeerRetry()
@@ -12957,6 +12977,7 @@ class PearcordPlatform extends EventEmitter {
this._guildMeshPeerRetryAttempts = (this._guildMeshPeerRetryAttempts || 0) + 1
const maxAttempts = Number(process.env.PEARCORD_GUILD_MESH_PEER_RETRY_MAX) || 8
if (this._guildMeshPeerRetryAttempts > maxAttempts) {
this._guildMeshPeerRetryMetaByGuild.delete(guildId)
this._clearGuildMeshPeerRetry()
return
}
@@ -13010,6 +13031,12 @@ class PearcordPlatform extends EventEmitter {
)
delayMs = retryDelays[delayIdx] || 3000
}
this._guildMeshPeerRetryMetaByGuild.set(guildId, {
attempt: this._guildMeshPeerRetryAttempts,
delayMs,
nextRetryAt: Date.now() + delayMs,
maxAttempts
})
this._guildMeshPeerRetryTimer = setTimeout(attempt, delayMs)
}
this._guildMeshPeerRetryTimer = setTimeout(attempt, 3000)