Skip to content

Commit 11c6ce5

Browse files
authored
ChatEditingSessionStorage: improve saving a session: compute files hashes async, encode content only once (microsoft#254882)
1 parent f3ed1cd commit 11c6ce5

File tree

1 file changed

+35
-29
lines changed

1 file changed

+35
-29
lines changed

src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSessionStorage.ts

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { VSBuffer } from '../../../../../base/common/buffer.js';
7-
import { StringSHA1 } from '../../../../../base/common/hash.js';
7+
import { hashAsync } from '../../../../../base/common/hash.js';
88
import { ResourceMap } from '../../../../../base/common/map.js';
99
import { joinPath } from '../../../../../base/common/resources.js';
1010
import { URI } from '../../../../../base/common/uri.js';
@@ -147,36 +147,48 @@ export class ChatEditingSessionStorage {
147147
}
148148
}
149149

150-
const fileContents = new Map<string, string>();
151-
const addFileContent = (content: string): string => {
152-
const shaComputer = new StringSHA1();
153-
shaComputer.update(content);
154-
const sha = shaComputer.digest().substring(0, 7);
155-
fileContents.set(sha, content);
156-
return sha;
150+
const contentWritePromises = new Map<string, Promise<string>>();
151+
152+
// saves a file content under a path containing a hash of the content.
153+
// Returns the hash to represent the content.
154+
const writeContent = async (content: string): Promise<string> => {
155+
const buffer = VSBuffer.fromString(content);
156+
const hash = (await hashAsync(buffer)).substring(0, 7);
157+
if (!existingContents.has(hash)) {
158+
await this._fileService.writeFile(joinPath(contentsFolder, hash), buffer);
159+
}
160+
return hash;
161+
};
162+
const addFileContent = async (content: string): Promise<string> => {
163+
let storedContentHash = contentWritePromises.get(content);
164+
if (!storedContentHash) {
165+
storedContentHash = writeContent(content);
166+
contentWritePromises.set(content, storedContentHash);
167+
}
168+
return storedContentHash;
157169
};
158-
const serializeResourceMap = <T>(resourceMap: ResourceMap<T>, serialize: (value: T) => any): ResourceMapDTO<T> => {
159-
return Array.from(resourceMap.entries()).map(([resourceURI, value]) => [resourceURI.toString(), serialize(value)]);
170+
const serializeResourceMap = async <T, U>(resourceMap: ResourceMap<T>, serialize: (value: T) => Promise<U>): Promise<ResourceMapDTO<U>> => {
171+
return await Promise.all(Array.from(resourceMap.entries()).map(async ([resourceURI, value]) => [resourceURI.toString(), await serialize(value)]));
160172
};
161-
const serializeChatEditingSessionStop = (stop: IChatEditingSessionStop): IChatEditingSessionStopDTO => {
173+
const serializeChatEditingSessionStop = async (stop: IChatEditingSessionStop): Promise<IChatEditingSessionStopDTO> => {
162174
return {
163175
stopId: stop.stopId,
164-
entries: Array.from(stop.entries.values()).map(serializeSnapshotEntry)
176+
entries: await Promise.all(Array.from(stop.entries.values()).map(serializeSnapshotEntry))
165177
};
166178
};
167-
const serializeChatEditingSessionSnapshot = (snapshot: IChatEditingSessionSnapshot): IChatEditingSessionSnapshotDTO2 => {
179+
const serializeChatEditingSessionSnapshot = async (snapshot: IChatEditingSessionSnapshot): Promise<IChatEditingSessionSnapshotDTO2> => {
168180
return {
169181
requestId: snapshot.requestId,
170-
stops: snapshot.stops.map(serializeChatEditingSessionStop),
171-
postEdit: snapshot.postEdit ? Array.from(snapshot.postEdit.values()).map(serializeSnapshotEntry) : undefined
182+
stops: await Promise.all(snapshot.stops.map(serializeChatEditingSessionStop)),
183+
postEdit: snapshot.postEdit ? await Promise.all(Array.from(snapshot.postEdit.values()).map(serializeSnapshotEntry)) : undefined
172184
};
173185
};
174-
const serializeSnapshotEntry = (entry: ISnapshotEntry): ISnapshotEntryDTO => {
186+
const serializeSnapshotEntry = async (entry: ISnapshotEntry): Promise<ISnapshotEntryDTO> => {
175187
return {
176188
resource: entry.resource.toString(),
177189
languageId: entry.languageId,
178-
originalHash: addFileContent(entry.original),
179-
currentHash: addFileContent(entry.current),
190+
originalHash: await addFileContent(entry.original),
191+
currentHash: await addFileContent(entry.current),
180192
state: entry.state,
181193
snapshotUri: entry.snapshotUri.toString(),
182194
telemetryInfo: { requestId: entry.telemetryInfo.requestId, agentId: entry.telemetryInfo.agentId, command: entry.telemetryInfo.command }
@@ -187,20 +199,14 @@ export class ChatEditingSessionStorage {
187199
const data: IChatEditingSessionDTO = {
188200
version: STORAGE_VERSION,
189201
sessionId: this.chatSessionId,
190-
linearHistory: state.linearHistory.map(serializeChatEditingSessionSnapshot),
202+
linearHistory: await Promise.all(state.linearHistory.map(serializeChatEditingSessionSnapshot)),
191203
linearHistoryIndex: state.linearHistoryIndex,
192-
initialFileContents: serializeResourceMap(state.initialFileContents, value => addFileContent(value)),
193-
pendingSnapshot: state.pendingSnapshot ? serializeChatEditingSessionStop(state.pendingSnapshot) : undefined,
194-
recentSnapshot: serializeChatEditingSessionStop(state.recentSnapshot),
204+
initialFileContents: await serializeResourceMap(state.initialFileContents, value => addFileContent(value)),
205+
pendingSnapshot: state.pendingSnapshot ? await serializeChatEditingSessionStop(state.pendingSnapshot) : undefined,
206+
recentSnapshot: await serializeChatEditingSessionStop(state.recentSnapshot),
195207
};
196208

197-
this._logService.debug(`chatEditingSession: Storing editing session at ${storageFolder.toString()}: ${fileContents.size} files`);
198-
199-
for (const [hash, content] of fileContents) {
200-
if (!existingContents.has(hash)) {
201-
await this._fileService.writeFile(joinPath(contentsFolder, hash), VSBuffer.fromString(content));
202-
}
203-
}
209+
this._logService.debug(`chatEditingSession: Storing editing session at ${storageFolder.toString()}: ${contentWritePromises.size} files`);
204210

205211
await this._fileService.writeFile(joinPath(storageFolder, STORAGE_STATE_FILE), VSBuffer.fromString(JSON.stringify(data)));
206212
} catch (e) {

0 commit comments

Comments
 (0)