Skip to content

Commit f5bbac3

Browse files
chore: handle pipeline failures explicitly
1 parent 6630922 commit f5bbac3

File tree

3 files changed

+184
-75
lines changed

3 files changed

+184
-75
lines changed

src/common/logger.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,11 @@ export const LogId = {
5252

5353
exportCleanupError: mongoLogId(1_007_001),
5454
exportCreationError: mongoLogId(1_007_002),
55-
exportReadError: mongoLogId(1_007_003),
56-
exportCloseError: mongoLogId(1_007_004),
57-
exportedDataListError: mongoLogId(1_007_005),
58-
exportedDataAutoCompleteError: mongoLogId(1_007_006),
55+
exportCreationCleanupError: mongoLogId(1_007_003),
56+
exportReadError: mongoLogId(1_007_004),
57+
exportCloseError: mongoLogId(1_007_005),
58+
exportedDataListError: mongoLogId(1_007_006),
59+
exportedDataAutoCompleteError: mongoLogId(1_007_007),
5960
} as const;
6061

6162
interface LogPayload {

src/common/sessionExportsManager.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -118,28 +118,44 @@ export class SessionExportsManager {
118118
jsonExportFormat: JSONExportFormat;
119119
}): Promise<void> {
120120
try {
121+
const exportNameWithExtension = this.withExtension(exportName, "json");
122+
const inputStream = input.stream();
123+
const ejsonDocStream = this.docToEJSONStream(this.getEJSONOptionsForFormat(jsonExportFormat));
121124
await this.withExportsLock<void>(async (exportsDirectoryPath) => {
122-
const exportNameWithExtension = this.withExtension(exportName, "json");
123125
const exportFilePath = path.join(exportsDirectoryPath, exportNameWithExtension);
124126
const outputStream = createWriteStream(exportFilePath);
125127
outputStream.write("[");
128+
let pipeSuccessful = false;
126129
try {
127-
const inputStream = input.stream();
128-
const ejsonOptions = this.getEJSONOptionsForFormat(jsonExportFormat);
129-
await pipeline([inputStream, this.docToEJSONStream(ejsonOptions), outputStream]);
130+
await pipeline([inputStream, ejsonDocStream, outputStream]);
131+
pipeSuccessful = true;
132+
} catch (pipelineError) {
133+
// If the pipeline errors out then we might end up with
134+
// partial and incorrect export so we remove it entirely.
135+
await fs.unlink(exportFilePath).catch((error) => {
136+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
137+
logger.error(
138+
LogId.exportCreationCleanupError,
139+
"Error when removing partial export",
140+
error instanceof Error ? error.message : String(error)
141+
);
142+
}
143+
});
144+
throw pipelineError;
130145
} finally {
131-
outputStream.write("]\n");
132-
const resourceURI = this.exportNameToResourceURI(exportNameWithExtension);
133-
this.mutableExports = [
134-
...this.mutableExports,
135-
{
136-
createdAt: (await fs.stat(exportFilePath)).birthtimeMs,
137-
name: exportNameWithExtension,
138-
uri: resourceURI,
139-
},
140-
];
141-
this.session.emit("export-available", resourceURI);
142146
void input.close();
147+
if (pipeSuccessful) {
148+
const resourceURI = this.exportNameToResourceURI(exportNameWithExtension);
149+
this.mutableExports = [
150+
...this.mutableExports,
151+
{
152+
createdAt: (await fs.stat(exportFilePath)).birthtimeMs,
153+
name: exportNameWithExtension,
154+
uri: resourceURI,
155+
},
156+
];
157+
this.session.emit("export-available", resourceURI);
158+
}
143159
}
144160
});
145161
} catch (error) {

0 commit comments

Comments
 (0)