Skip to content

Commit 481ff42

Browse files
committed
Fix three code quality/performance bugs
- Remove redundant no-op Effect.map((option) => option) in ProjectionSnapshotQuery.ts - Avoid double enrichment of replay events in ws.ts subscription handler by applying enrichProjectEvent only to live stream events - Memoize selectThreadIdsByProjectId in store.ts to return stable array references and avoid unnecessary React re-renders
1 parent 410dbe8 commit 481ff42

File tree

3 files changed

+55
-49
lines changed

3 files changed

+55
-49
lines changed

apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,6 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
748748
"ProjectionSnapshotQuery.getActiveProjectByWorkspaceRoot:decodeRow",
749749
),
750750
),
751-
Effect.map((option) => option),
752751
Effect.flatMap((option) =>
753752
Option.isNone(option)
754753
? Effect.succeed(Option.none<OrchestrationProject>())

apps/server/src/ws.ts

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,10 @@ const WsRpcLayer = WsRpcGroup.toLayer(
503503
Effect.catch(() => Effect.succeed([] as Array<OrchestrationEvent>)),
504504
);
505505
const replayStream = Stream.fromIterable(replayEvents);
506-
const source = Stream.merge(replayStream, orchestrationEngine.streamDomainEvents);
506+
const liveStream = orchestrationEngine.streamDomainEvents.pipe(
507+
Stream.mapEffect(enrichProjectEvent),
508+
);
509+
const source = Stream.merge(replayStream, liveStream);
507510
type SequenceState = {
508511
readonly nextSequence: number;
509512
readonly pendingBySequence: Map<number, OrchestrationEvent>;
@@ -515,43 +518,33 @@ const WsRpcLayer = WsRpcGroup.toLayer(
515518

516519
return source.pipe(
517520
Stream.mapEffect((event) =>
518-
enrichProjectEvent(event).pipe(
519-
Effect.flatMap((enrichedEvent) =>
520-
Ref.modify(
521-
state,
522-
({
523-
nextSequence,
524-
pendingBySequence,
525-
}): [Array<OrchestrationEvent>, SequenceState] => {
526-
if (
527-
enrichedEvent.sequence < nextSequence ||
528-
pendingBySequence.has(enrichedEvent.sequence)
529-
) {
530-
return [[], { nextSequence, pendingBySequence }];
531-
}
521+
Ref.modify(
522+
state,
523+
({
524+
nextSequence,
525+
pendingBySequence,
526+
}): [Array<OrchestrationEvent>, SequenceState] => {
527+
if (event.sequence < nextSequence || pendingBySequence.has(event.sequence)) {
528+
return [[], { nextSequence, pendingBySequence }];
529+
}
532530

533-
const updatedPending = new Map(pendingBySequence);
534-
updatedPending.set(enrichedEvent.sequence, enrichedEvent);
531+
const updatedPending = new Map(pendingBySequence);
532+
updatedPending.set(event.sequence, event);
535533

536-
const emit: Array<OrchestrationEvent> = [];
537-
let expected = nextSequence;
538-
for (;;) {
539-
const expectedEvent = updatedPending.get(expected);
540-
if (!expectedEvent) {
541-
break;
542-
}
543-
emit.push(expectedEvent);
544-
updatedPending.delete(expected);
545-
expected += 1;
546-
}
534+
const emit: Array<OrchestrationEvent> = [];
535+
let expected = nextSequence;
536+
for (;;) {
537+
const expectedEvent = updatedPending.get(expected);
538+
if (!expectedEvent) {
539+
break;
540+
}
541+
emit.push(expectedEvent);
542+
updatedPending.delete(expected);
543+
expected += 1;
544+
}
547545

548-
return [
549-
emit,
550-
{ nextSequence: expected, pendingBySequence: updatedPending },
551-
];
552-
},
553-
),
554-
),
546+
return [emit, { nextSequence: expected, pendingBySequence: updatedPending }];
547+
},
555548
),
556549
),
557550
Stream.flatMap((events) => Stream.fromIterable(events)),

apps/web/src/store.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,21 +1305,35 @@ export const selectSidebarThreadSummaryById =
13051305
]
13061306
: undefined;
13071307

1308+
const _threadIdsByProjectCache = new Map<
1309+
string,
1310+
{ scopedIds: string[]; sidebarMap: Record<string, SidebarThreadSummary>; result: ThreadId[] }
1311+
>();
1312+
13081313
export const selectThreadIdsByProjectId =
13091314
(projectId: ProjectId | null | undefined) =>
1310-
(state: AppState): ThreadId[] =>
1311-
projectId
1312-
? (
1313-
state.threadScopedIdsByProjectScopedId[
1314-
getProjectScopedId({
1315-
environmentId: state.activeEnvironmentId,
1316-
id: projectId,
1317-
})
1318-
] ?? EMPTY_SCOPED_IDS
1319-
)
1320-
.map((scopedId) => state.sidebarThreadsByScopedId[scopedId]?.id ?? null)
1321-
.filter((threadId): threadId is ThreadId => threadId !== null)
1322-
: EMPTY_THREAD_IDS;
1315+
(state: AppState): ThreadId[] => {
1316+
if (!projectId) return EMPTY_THREAD_IDS;
1317+
1318+
const projectScopedId = getProjectScopedId({
1319+
environmentId: state.activeEnvironmentId,
1320+
id: projectId,
1321+
});
1322+
const scopedIds = state.threadScopedIdsByProjectScopedId[projectScopedId] ?? EMPTY_SCOPED_IDS;
1323+
const sidebarMap = state.sidebarThreadsByScopedId;
1324+
1325+
const cached = _threadIdsByProjectCache.get(projectScopedId);
1326+
if (cached && cached.scopedIds === scopedIds && cached.sidebarMap === sidebarMap) {
1327+
return cached.result;
1328+
}
1329+
1330+
const result = scopedIds
1331+
.map((scopedId) => sidebarMap[scopedId]?.id ?? null)
1332+
.filter((threadId): threadId is ThreadId => threadId !== null);
1333+
1334+
_threadIdsByProjectCache.set(projectScopedId, { scopedIds, sidebarMap, result });
1335+
return result;
1336+
};
13231337

13241338
export function setError(state: AppState, threadId: ThreadId, error: string | null): AppState {
13251339
return updateThreadState(state, state.activeEnvironmentId, threadId, (t) => {

0 commit comments

Comments
 (0)