Skip to content

Commit c0483af

Browse files
authored
perf(session): faster session switching via windowed rendering and staged timeline (#15474)
1 parent ae0f69e commit c0483af

4 files changed

Lines changed: 374 additions & 172 deletions

File tree

packages/app/src/context/sync.tsx

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,11 @@ type OptimisticRemoveInput = {
4343

4444
export function applyOptimisticAdd(draft: OptimisticStore, input: OptimisticAddInput) {
4545
const messages = draft.message[input.sessionID]
46-
if (!messages) {
47-
draft.message[input.sessionID] = [input.message]
48-
}
4946
if (messages) {
5047
const result = Binary.search(messages, input.message.id, (m) => m.id)
5148
messages.splice(result.index, 0, input.message)
49+
} else {
50+
draft.message[input.sessionID] = [input.message]
5251
}
5352
draft.part[input.message.id] = sortParts(input.parts)
5453
}
@@ -105,7 +104,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
105104
return globalSync.child(directory)
106105
}
107106
const absolute = (path: string) => (current()[0].path.directory + "/" + path).replace("//", "/")
108-
const messagePageSize = 400
107+
const messagePageSize = 200
109108
const inflight = new Map<string, Promise<void>>()
110109
const inflightDiff = new Map<string, Promise<void>>()
111110
const inflightTodo = new Map<string, Promise<void>>()
@@ -122,20 +121,12 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
122121
return undefined
123122
}
124123

125-
const limitFor = (count: number) => {
126-
if (count <= messagePageSize) return messagePageSize
127-
return Math.ceil(count / messagePageSize) * messagePageSize
128-
}
129-
130124
const fetchMessages = async (input: { client: typeof sdk.client; sessionID: string; limit: number }) => {
131125
const messages = await retry(() =>
132126
input.client.session.messages({ sessionID: input.sessionID, limit: input.limit }),
133127
)
134128
const items = (messages.data ?? []).filter((x) => !!x?.info?.id)
135-
const session = items
136-
.map((x) => x.info)
137-
.filter((m) => !!m?.id)
138-
.sort((a, b) => cmp(a.id, b.id))
129+
const session = items.map((x) => x.info).sort((a, b) => cmp(a.id, b.id))
139130
const part = items.map((message) => ({ id: message.info.id, part: sortParts(message.parts) }))
140131
return {
141132
session,
@@ -159,8 +150,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
159150
.then((next) => {
160151
batch(() => {
161152
input.setStore("message", input.sessionID, reconcile(next.session, { key: "id" }))
162-
for (const message of next.part) {
163-
input.setStore("part", message.id, reconcile(message.part, { key: "id" }))
153+
for (const p of next.part) {
154+
input.setStore("part", p.id, p.part)
164155
}
165156
setMeta("limit", key, input.limit)
166157
setMeta("complete", key, next.complete)
@@ -229,17 +220,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
229220
const client = sdk.client
230221
const [store, setStore] = globalSync.child(directory)
231222
const key = keyFor(directory, sessionID)
232-
const hasSession = (() => {
233-
const match = Binary.search(store.session, sessionID, (s) => s.id)
234-
return match.found
235-
})()
223+
const hasSession = Binary.search(store.session, sessionID, (s) => s.id).found
236224

237-
const hasMessages = store.message[sessionID] !== undefined
238-
const hydrated = meta.limit[key] !== undefined
239-
if (hasSession && hasMessages && hydrated) return
240-
241-
const count = store.message[sessionID]?.length ?? 0
242-
const limit = hydrated ? (meta.limit[key] ?? messagePageSize) : limitFor(count)
225+
const limit = meta.limit[key] ?? messagePageSize
243226

244227
const sessionReq = hasSession
245228
? Promise.resolve()
@@ -259,16 +242,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
259242
)
260243
})
261244

262-
const messagesReq =
263-
hasMessages && hydrated
264-
? Promise.resolve()
265-
: loadMessages({
266-
directory,
267-
client,
268-
setStore,
269-
sessionID,
270-
limit,
271-
})
245+
const messagesReq = loadMessages({
246+
directory,
247+
client,
248+
setStore,
249+
sessionID,
250+
limit,
251+
})
272252

273253
return runInflight(inflight, key, () => Promise.all([sessionReq, messagesReq]).then(() => {}))
274254
},
@@ -290,14 +270,14 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
290270
const client = sdk.client
291271
const [store, setStore] = globalSync.child(directory)
292272
const existing = store.todo[sessionID]
273+
const cached = globalSync.data.session_todo[sessionID]
293274
if (existing !== undefined) {
294-
if (globalSync.data.session_todo[sessionID] === undefined) {
275+
if (cached === undefined) {
295276
globalSync.todo.set(sessionID, existing)
296277
}
297278
return
298279
}
299280

300-
const cached = globalSync.data.session_todo[sessionID]
301281
if (cached !== undefined) {
302282
setStore("todo", sessionID, reconcile(cached, { key: "id" }))
303283
}
@@ -324,11 +304,12 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
324304
const key = keyFor(sdk.directory, sessionID)
325305
return meta.loading[key] ?? false
326306
},
327-
async loadMore(sessionID: string, count = messagePageSize) {
307+
async loadMore(sessionID: string, count?: number) {
328308
const directory = sdk.directory
329309
const client = sdk.client
330310
const [, setStore] = globalSync.child(directory)
331311
const key = keyFor(directory, sessionID)
312+
const step = count ?? messagePageSize
332313
if (meta.loading[key]) return
333314
if (meta.complete[key]) return
334315

@@ -338,7 +319,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
338319
client,
339320
setStore,
340321
sessionID,
341-
limit: currentLimit + count,
322+
limit: currentLimit + step,
342323
})
343324
},
344325
},

0 commit comments

Comments
 (0)