fix(api): simplify projection reconciliation

This commit is contained in:
pku-xht 2026-08-27 11:02:06 +08:00
parent c3b694312f
commit 8c67d49ca5
2 changed files with 14 additions and 47 deletions

View file

@ -10,7 +10,6 @@ import type {
SessionControlFrame,
SessionQueuedItem,
SessionError,
SessionProjectionHints,
SessionSummary,
SessionJob as JobView,
} from '../../types.ts'
@ -489,7 +488,10 @@ export class SessionManager {
session.handleBlank(s.blank)
session.handleRunning(s.running)
}
this.reconcileListProjectionHints(result.value.items, mutations)
for (const summary of this.summaries) {
const projections = summary.projections
if (projections !== undefined) this.projectionStore(summary.sessionId).prewarm(projections)
}
} else {
this.listState = 'error'
this.listError = result.error
@ -705,31 +707,6 @@ export class SessionManager {
this.notifier.markDirty()
}
/**
* Apply pull-time hints before later list mutations without letting a stale
* in-flight response overwrite a newer session-added hint or recreate a
* Session removed while the request was pending.
*/
private reconcileListProjectionHints(
items: readonly SessionSummary[],
mutations: readonly SessionListMutation[],
): void {
const hints = new Map<SessionId, SessionProjectionHints>()
for (const summary of items) {
if (summary.projections !== undefined) hints.set(summary.sessionId, summary.projections)
}
for (const mutation of mutations) {
if (mutation.kind === 'remove') {
hints.delete(mutation.sessionId)
} else if (mutation.kind === 'upsert' && mutation.summary.projections !== undefined) {
hints.set(mutation.summary.sessionId, mutation.summary.projections)
}
}
for (const [sessionId, hint] of hints) {
this.projectionStore(sessionId).prewarm(hint)
}
}
/**
* Apply one Session-list addition forwarded through `ctx.remote.$on`.
* @param summary - current Host summary for the added Session.

View file

@ -209,7 +209,7 @@ export class ProjectionValueStore {
&& row.revision > token.revision
&& row.seq > baseline.asOfSeq)
this.completeBaselineInstalled = true
this.installCompleteBaseline(baseline, ++this.revision)
this.replaceRows(baseline, ++this.revision, 'authoritative')
for (const [key, row] of retained) this.installRow(key, row)
}
@ -231,7 +231,7 @@ export class ProjectionValueStore {
const revision = ++this.revision
this.completeBaselineInstalled = true
this.latestControlBaseline = { revision, asOfSeq: baseline.asOfSeq }
this.installCompleteBaseline(baseline, revision)
this.replaceRows(baseline, revision, 'authoritative')
}
/**
@ -251,21 +251,7 @@ export class ProjectionValueStore {
}
return
}
const values = hint.values as Record<string, unknown>
const keys = new Set([...this.rows.keys(), ...Object.keys(values)])
for (const key of keys) {
if (!Object.hasOwn(values, key)) {
this.rows.delete(key)
this.changed(key)
continue
}
this.installRow(key, {
value: values[key],
seq: hint.asOfSeq,
provenance: 'tentative',
revision,
})
}
this.replaceRows(hint, revision, 'tentative')
}
private changed(key: string): void {
@ -274,8 +260,12 @@ export class ProjectionValueStore {
this.anyNotifier.markDirty()
}
/** Replace every row with one complete authoritative baseline. */
private installCompleteBaseline(baseline: ProjectionsBaseline, revision: number): void {
/** Replace every row with one baseline at the supplied authority. */
private replaceRows(
baseline: ProjectionsBaseline,
revision: number,
provenance: Row['provenance'],
): void {
const values = baseline.values as Record<string, unknown>
const keys = new Set([...this.rows.keys(), ...Object.keys(values)])
for (const key of keys) {
@ -287,7 +277,7 @@ export class ProjectionValueStore {
this.installRow(key, {
value: values[key],
seq: baseline.asOfSeq,
provenance: 'authoritative',
provenance,
revision,
})
}