Skip to content

Commit f215e31

Browse files
chore: handle pipeline failures explicitly
1 parent a7e815a commit f215e31

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
@@ -51,10 +51,11 @@ export const LogId = {
5151

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

6061
export abstract class LoggerBase {

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)