Skip to content

Commit 1add881

Browse files
Copiloticlanton
andauthored
Address review comments: fix response.resume race, stream retry bug, error logging, stream cleanup, and nits
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/d0190bf7-a346-4a71-93bf-d5375d98b552 Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
1 parent 96b5351 commit 1add881

4 files changed

Lines changed: 28 additions & 19 deletions

File tree

libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ export class OperationBuildCache {
190190
);
191191
updateLocalCacheSuccess = true;
192192
} catch (e) {
193+
terminal.writeVerboseLine(`Failed to update local cache: ${e}`);
193194
updateLocalCacheSuccess = false;
194195
}
195196
}
@@ -207,6 +208,7 @@ export class OperationBuildCache {
207208
);
208209
updateLocalCacheSuccess = true;
209210
} catch (e) {
211+
terminal.writeVerboseLine(`Failed to update local cache: ${e}`);
210212
updateLocalCacheSuccess = false;
211213
}
212214
}
@@ -353,11 +355,12 @@ export class OperationBuildCache {
353355
) {
354356
// Use streaming upload to avoid loading the entire cache entry into memory
355357
const entryStream: FileSystemReadStream = FileSystem.createReadStream(localCacheEntryPath);
356-
setCloudCacheEntryPromise = this._cloudBuildCacheProvider.trySetCacheEntryStreamAsync(
357-
terminal,
358-
cacheId,
359-
entryStream
360-
);
358+
setCloudCacheEntryPromise = this._cloudBuildCacheProvider
359+
.trySetCacheEntryStreamAsync(terminal, cacheId, entryStream)
360+
.catch((e: Error) => {
361+
entryStream.destroy();
362+
throw e;
363+
});
361364
} else {
362365
const cacheEntryBuffer: Buffer = await FileSystem.readFileToBufferAsync(localCacheEntryPath);
363366
setCloudCacheEntryPromise = this._cloudBuildCacheProvider.trySetCacheEntryBufferAsync(

libraries/rush-lib/src/utilities/WebClient.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ function _makeRawRequestAsync<TResponse>(
133133
headers: { location: redirectUrl }
134134
} = response;
135135
if (statusCode === 301 || statusCode === 302) {
136-
// Drain the redirect response before following
137-
response.resume();
138136
switch (redirect) {
139137
case 'follow': {
138+
// Drain the redirect response since we're discarding it
139+
response.resume();
140140
if (redirectUrl) {
141141
requestFnAsync(redirectUrl, options, true).then(resolve).catch(reject);
142142
} else {
@@ -146,6 +146,7 @@ function _makeRawRequestAsync<TResponse>(
146146
}
147147

148148
case 'error':
149+
response.resume();
149150
reject(new Error(`Received status code ${statusCode}: ${url}`));
150151
return;
151152
}

rush-plugins/rush-azure-storage-build-cache-plugin/src/AzureStorageBuildCacheProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ export class AzureStorageBuildCacheProvider
8686
public async trySetCacheEntryBufferAsync(
8787
terminal: ITerminal,
8888
cacheId: string,
89-
entryStream: Buffer
89+
entryBuffer: Buffer
9090
): Promise<boolean> {
9191
return await this._trySetBlobDataAsync(terminal, cacheId, async (blockBlobClient: BlockBlobClient) => {
92-
await blockBlobClient.upload(entryStream, entryStream.length);
92+
await blockBlobClient.upload(entryBuffer, entryBuffer.length);
9393
});
9494
}
9595

rush-plugins/rush-http-build-cache-plugin/src/HttpBuildCacheProvider.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ export class HttpBuildCacheProvider implements ICloudBuildCacheProvider {
379379
headers: headers,
380380
body: body,
381381
redirect: 'follow',
382-
timeoutMs: 0 // Use the default timeout
382+
timeoutMs: 0 // Disable timeout for streaming transfers of large cache entries
383383
};
384384

385385
const response: IWebClientResponse | IWebClientStreamResponse = stream
@@ -399,15 +399,20 @@ export class HttpBuildCacheProvider implements ICloudBuildCacheProvider {
399399
typeof credentials !== 'string' &&
400400
safeCredentialOptions === CredentialsOptions.Optional
401401
) {
402-
// If we don't already have credentials yet, and we got a response from the server
403-
// that is a "normal" failure (4xx), then we assume that credentials are probably
404-
// required. Re-attempt the request, requiring credentials this time.
405-
//
406-
// This counts as part of the "first attempt", so it is not included in the max attempts
407-
return await this._makeHttpCoreRequestAsync({
408-
...options,
409-
credentialOptions: CredentialsOptions.Required
410-
});
402+
// Skip credential fallback for stream bodies since the stream has already been consumed
403+
// by the first attempt and cannot be replayed.
404+
const isStreamBody: boolean = !!body && typeof (body as Readable).pipe === 'function';
405+
if (!isStreamBody) {
406+
// If we don't already have credentials yet, and we got a response from the server
407+
// that is a "normal" failure (4xx), then we assume that credentials are probably
408+
// required. Re-attempt the request, requiring credentials this time.
409+
//
410+
// This counts as part of the "first attempt", so it is not included in the max attempts
411+
return await this._makeHttpCoreRequestAsync({
412+
...options,
413+
credentialOptions: CredentialsOptions.Required
414+
});
415+
}
411416
}
412417

413418
if (options.maxAttempts > 1) {

0 commit comments

Comments
 (0)