Skip to content

Commit b64f7a3

Browse files
authored
perf: remove intermediate activities after stream ends (#5798)
* perf: remove intermediate activities after stream ends * Changelog * test p1 * html tests * Fix another html test * Fix comments
1 parent 94c9e6f commit b64f7a3

14 files changed

+58
-49
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ Breaking changes in this release:
343343
- Removed unused deps `simple-git`, by [@compulim](https://github.com/compulim), in PR [#5786](https://github.com/microsoft/BotFramework-WebChat/pull/5786)
344344
- Improved `ActivityKeyerComposer` performance for append scenarios by adding an incremental fast path that only processes newly-appended activities, in PR [#5790](https://github.com/microsoft/BotFramework-WebChat/pull/5790), in PR [#5797](https://github.com/microsoft/BotFramework-WebChat/pull/5797), by [@OEvgeny](https://github.com/OEvgeny)
345345
- Added frozen window optimization to limit reference comparisons to the last 1,000 activities with deferred verification of the frozen portion, in PR [#5797](https://github.com/microsoft/BotFramework-WebChat/pull/5797), by [@OEvgeny](https://github.com/OEvgeny)
346+
- Improved livestream performance by pruning intermediate revision activities after a stream session is finalized, in PR [#5798](https://github.com/microsoft/BotFramework-WebChat/pull/5798), by [@OEvgeny](https://github.com/OEvgeny)
346347
- Bumped to [`adaptivecards@3.0.6`](https://www.npmjs.com/package/adaptivecards/v/3.0.6) in PR [#5800](https://github.com/microsoft/BotFramework-WebChat/pull/5800) by [@compulim](https://github.com/compulim)
347348

348349
### Deprecated

__tests__/html2/livestream/activityOrder.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@
246246
expect(currentActivityKeysWithId).toEqual([
247247
[firstActivityKey, ['a-00001']],
248248
[thirdActivityKey, ['a-00003']],
249-
[secondActivityKey, ['t-00001', 't-00002', 't-00003', 'a-00002']]
249+
[secondActivityKey, ['a-00002']]
250250
]);
251251
});
252252
</script>

__tests__/html2/livestream/backtrackToEmpty.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@
264264
// THEN: Should have 2 activity keys only.
265265
expect(currentActivityKeysWithId).toEqual([
266266
[expect.any(String), ['a-00001']],
267-
[firstTypingActivityKey, ['t-00001', 't-00002', 't-00003', 't-00004']]
267+
[firstTypingActivityKey, ['t-00004']]
268268
]);
269269

270270
// THEN: Should have no active typing.
@@ -287,7 +287,7 @@
287287
// THEN: Should have 2 activity keys only.
288288
expect(currentActivityKeysWithId).toEqual([
289289
[expect.any(String), ['a-00001']],
290-
[firstTypingActivityKey, ['t-00001', 't-00002', 't-00003', 't-00004']],
290+
[firstTypingActivityKey, ['t-00004']],
291291
[expect.any(String), ['a-00002']]
292292
]);
293293

__tests__/html2/livestream/chunk.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@
204204
// THEN: Should have 2 activity keys.
205205
expect(currentActivityKeysWithId).toEqual([
206206
[firstActivityKey, ['a-00001']],
207-
[secondActivityKey, ['t-00001', 't-00002', 't-00003', 'a-00002']]
207+
[secondActivityKey, ['a-00002']]
208208
]);
209209
});
210210
</script>

__tests__/html2/livestream/concludedLivestream.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
await host.snapshot('local');
138138

139139
// THEN: Should have 1 activity key only.
140-
expect(currentActivityKeysWithId).toEqual([[firstTypingActivityKey, ['t-00001', 't-00002']]]);
140+
expect(currentActivityKeysWithId).toEqual([[firstTypingActivityKey, ['t-00002']]]);
141141

142142
// THEN: Should have no active typing.
143143
expect(currentActiveTyping).toHaveProperty('u-00001', undefined);
@@ -167,8 +167,8 @@
167167
);
168168
await host.snapshot('local');
169169

170-
// THEN: Should have 1 activity key associated with 2 activity IDs only.
171-
expect(currentActivityKeysWithId).toEqual([[firstTypingActivityKey, ['t-00001', 't-00002']]]);
170+
// THEN: Should have 1 activity key associated with a single activity ID only.
171+
expect(currentActivityKeysWithId).toEqual([[firstTypingActivityKey, ['t-00002']]]);
172172

173173
// THEN: Should have no active typing.
174174
expect(currentActiveTyping).toHaveProperty('u-00001', undefined);
@@ -198,8 +198,8 @@
198198
);
199199
await host.snapshot('local');
200200

201-
// THEN: Should have 1 activity key associated with 2 activity IDs only.
202-
expect(currentActivityKeysWithId).toEqual([[firstTypingActivityKey, ['t-00001', 't-00002']]]);
201+
// THEN: Should have 1 activity key associated with 1 activity ID only (intermediate revisions pruned).
202+
expect(currentActivityKeysWithId).toEqual([[firstTypingActivityKey, ['t-00002']]]);
203203

204204
// THEN: Should have no active typing.
205205
expect(currentActiveTyping).toHaveProperty('u-00001', undefined);

__tests__/html2/livestream/outOfOrder.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@
247247
// THEN: Should have 2 activity keys.
248248
expect(currentActivityKeysWithId).toEqual([
249249
[firstActivityKey, ['a-00001']],
250-
[secondActivityKey, ['t-00001', 't-00002', 't-00003', 'a-00002']]
250+
[secondActivityKey, ['a-00002']]
251251
]);
252252

253253
// THEN: Should have no typing.

__tests__/html2/livestream/outOfOrder.sequenceNumber.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@
249249
// THEN: Should have 2 activity keys.
250250
expect(currentActivityKeysWithId).toEqual([
251251
[firstActivityKey, ['a-00001']],
252-
[secondActivityKey, ['t-00001', 't-00002', 't-00003', 'a-00002']]
252+
[secondActivityKey, ['a-00002']]
253253
]);
254254

255255
// THEN: Should have no typing.

__tests__/html2/livestream/simultaneous.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@
251251
expect(currentActivityKeysWithId).toEqual([
252252
[firstActivityKey, ['a-00001']],
253253
[thirdActivityKey, ['t-10001', 't-10002']],
254-
[secondActivityKey, ['t-00001', 't-00002', 'a-00002']]
254+
[secondActivityKey, ['a-00002']]
255255
]);
256256

257257
// ---
@@ -289,8 +289,8 @@
289289
// THEN: Should have 3 activity keys.
290290
expect(currentActivityKeysWithId).toEqual([
291291
[firstActivityKey, ['a-00001']],
292-
[secondActivityKey, ['t-00001', 't-00002', 'a-00002']],
293-
[thirdActivityKey, ['t-10001', 't-10002', 'a-00003']]
292+
[secondActivityKey, ['a-00002']],
293+
[thirdActivityKey, ['a-00003']]
294294
]);
295295
});
296296
</script>

packages/api-graph/src/private/GraphProvider.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,21 @@ function GraphProvider(props: GraphProviderProps) {
7676
};
7777
}, [graph, setOrderedActivityNodes]);
7878

79-
const orderedActivitiesState = useMemo<readonly [readonly WebChatActivity[]]>(
80-
() =>
81-
Object.freeze([
82-
Object.freeze(
83-
orderedActivityNodes.map(
84-
node => node['urn:microsoft:webchat:direct-line-activity:raw-json'][0]['@value'] as WebChatActivity
85-
)
86-
)
87-
] as const),
88-
[orderedActivityNodes]
89-
);
79+
const orderedActivitiesState = useMemo<readonly [readonly WebChatActivity[]]>(() => {
80+
// Filter out stale graph nodes for activities no longer in Redux (e.g. pruned livestream revisions).
81+
// The graph does not support deletion, so stale nodes linger after computeSortedActivities prunes them.
82+
const { activities: storeActivities } = store.getState();
83+
const validActivitySet = new Set<WebChatActivity>(storeActivities);
84+
const activities: WebChatActivity[] = [];
85+
86+
for (const node of orderedActivityNodes) {
87+
const activity = node['urn:microsoft:webchat:direct-line-activity:raw-json'][0]['@value'] as WebChatActivity;
88+
89+
validActivitySet.has(activity) && activities.push(activity);
90+
}
91+
92+
return Object.freeze([Object.freeze(activities)] as const);
93+
}, [orderedActivityNodes, store]);
9094

9195
const context = useMemo<GraphContextType>(
9296
() =>

packages/core/src/reducers/activities/sort/deleteActivityByLocalId.howToWithLivestream.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,15 @@ scenario('delete livestream activities in part grouping', bdd => {
132132
activity4
133133
)
134134
)
135-
.then('should have 4 activities', (_, state) => {
135+
.then('should have 4 activities in map, 2 visible', (_, state) => {
136136
expect(state.activityMap).toHaveProperty('size', 4);
137137
expect(state.howToGroupingMap).toHaveProperty('size', 1);
138138
expect(state.livestreamSessionMap).toHaveProperty('size', 1);
139-
expect(state.sortedActivities).toHaveLength(4);
139+
expect(state.sortedActivities).toHaveLength(2);
140140
expect(state.sortedChatHistoryList).toHaveLength(1);
141141
})
142142
.when('the last livestream activity is delete', (_, state) =>
143-
deleteActivityByLocalId(state, getLocalIdFromActivity(state.sortedActivities[2]))
143+
deleteActivityByLocalId(state, getLocalIdFromActivity(state.sortedActivities[0]))
144144
)
145145
.then('should have 3 activities', (_, state) => {
146146
expect(state.activityMap).toHaveProperty('size', 3);

0 commit comments

Comments
 (0)