perf: dashboard revision-first 조회 계약을 추가

content revision과 별도인 source revision을 선택적으로 교환하고 coverage가 충족될 때만 payload loader를 생략한다. 구형 client와 미완성 producer coverage는 기존 full computation으로 안전하게 복구한다.
This commit is contained in:
2026-08-16 18:26:15 +00:00
parent f858ca43f4
commit 299470590a
9 changed files with 697 additions and 77 deletions
+7
View File
@@ -11,16 +11,19 @@ export type ReadModelDelta<T> =
| {
kind: 'snapshot';
revision: string;
sourceRevision?: string;
data: T;
}
| {
kind: 'unchanged';
revision: string;
sourceRevision?: string;
}
| {
kind: 'patch';
baseRevision: string;
revision: string;
sourceRevision?: string;
operations: JsonPatchOperation[];
};
@@ -41,6 +44,7 @@ export class ReadModelDeltaApplyError extends Error {
export interface AppliedReadModelDelta<T> {
data: T;
revision: string;
sourceRevision?: string;
}
/**
@@ -143,6 +147,7 @@ export const applyReadModelDelta = <T>(
return {
data: delta.data,
revision: delta.revision,
...(delta.sourceRevision ? { sourceRevision: delta.sourceRevision } : {}),
};
}
@@ -159,6 +164,7 @@ export const applyReadModelDelta = <T>(
return {
data: current,
revision: currentRevision,
...(delta.sourceRevision ? { sourceRevision: delta.sourceRevision } : {}),
};
}
@@ -185,5 +191,6 @@ export const applyReadModelDelta = <T>(
return {
data: next,
revision: delta.revision,
...(delta.sourceRevision ? { sourceRevision: delta.sourceRevision } : {}),
};
};
@@ -118,6 +118,21 @@ describe('applyReadModelDelta', () => {
expect(applied.data).toBe(current);
});
it('carries an optional source revision without changing content revision semantics', () => {
const current = { value: 1 };
const applied = applyReadModelDelta(current, 'revision-1', {
kind: 'unchanged',
revision: 'revision-1',
sourceRevision: 'source-revision-2',
});
expect(applied).toEqual({
data: current,
revision: 'revision-1',
sourceRevision: 'source-revision-2',
});
});
it('rejects a patch based on a different snapshot', () => {
expect(() =>
applyReadModelDelta({ value: 1 }, 'revision-2', {