Skip to content

Add streaming APIs to build cache provider interface and all cache plugins#5746

Draft
Copilot wants to merge 16 commits intomainfrom
copilot/stream-cache-entry-for-http-plugin
Draft

Add streaming APIs to build cache provider interface and all cache plugins#5746
Copilot wants to merge 16 commits intomainfrom
copilot/stream-cache-entry-for-http-plugin

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 5, 2026

  • Add streaming methods to ICloudBuildCacheProvider interface
  • Implement streaming in FileSystemBuildCacheProvider
  • Implement streaming in HttpBuildCacheProvider
  • Add fetchStreamAsync to WebClient
  • Add FileSystem.createReadStream/createWriteStream and type aliases
  • Add IFileSystemCreateWriteStreamOptions with ensureFolderExists option
  • Add streaming support to Amazon S3 build cache plugin
  • Add streaming support to Azure Storage build cache plugin
  • Gate streaming cache usage behind useStreamingBuildCache experiment flag
  • Fix CI: bridge plugin and WebClient type mismatch
  • Address code duplication feedback:
    • DRY up makeStreamRequestAsync with makeRequestAsync in WebClient.ts via shared _makeRawRequestAsync
    • DRY up getObjectStreamAsync with getObjectAsync in AmazonS3Client.ts via shared _handleGetResponseAsync
    • DRY up _makeStreamRequestAsync with _makeRequestAsync into _makeSignedRequestAsync in AmazonS3Client.ts
    • DRY up _makeHttpStreamRequestAsync with _makeHttpRequestAsync via shared _makeHttpCoreRequestAsync in HttpBuildCacheProvider.ts
    • Address: "Why aren't we using streams for read?" in FileSystemBuildCacheProvider.ts (explained in comment)
  • Build and test all affected packages (rush-lib 599, S3 45, HTTP 2 tests pass)

Copilot AI and others added 4 commits April 5, 2026 03:35
…P plugin

Add optional tryGetCacheEntryStreamByIdAsync and trySetCacheEntryStreamAsync
methods to ICloudBuildCacheProvider. Implement streaming in HttpBuildCacheProvider
and update OperationBuildCache to prefer streaming when available. Add
fetchStreamAsync to WebClient and stream write support to
FileSystemBuildCacheProvider.

Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/050e10a7-3cad-4da4-93e5-9941453283b9

Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
@iclanton
Copy link
Copy Markdown
Member

iclanton commented Apr 5, 2026

@copilot - add APIs to FileSystem in node-core-library for the fs APIs you're making use of in this PR. Ideally, none of our projects (except for FileSystem itself) should use fs. Everything should use FileSystem. The change to node-core-library will also need a changefile. Take a look at the conventions in FileSystem and follow that pattern.

…rary and update consumers to use FileSystem instead of fs directly

Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/5ad20d4d-c9a4-4855-bb13-8dd9e2c1350b

Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 5, 2026

@copilot - add APIs to FileSystem in node-core-library for the fs APIs you're making use of in this PR. Ideally, none of our projects (except for FileSystem itself) should use fs. Everything...

Added createReadStream() and createWriteStream() static methods to FileSystem in node-core-library, along with FileSystemReadStream and FileSystemWriteStream type aliases (following the same pattern as FileSystemStats and FolderItem). Updated FileSystemBuildCacheProvider and OperationBuildCache to use the new FileSystem APIs instead of importing from node:fs directly. Change file for @rushstack/node-core-library included as a minor bump. Commit: 468caf7

Copilot AI requested a review from iclanton April 5, 2026 04:32
@iclanton
Copy link
Copy Markdown
Member

iclanton commented Apr 5, 2026

@copilot - are there async variants of those fs APIs? Should we expose and use those too?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 5, 2026

@copilot - are there async variants of those fs APIs? Should we expose and use those too?

No, fs.createReadStream() and fs.createWriteStream() don't have async variants — they're already inherently asynchronous. They return stream objects immediately and all the actual I/O happens asynchronously through the stream events/pipeline. The fs/promises module doesn't include them for this reason.

@iclanton
Copy link
Copy Markdown
Member

iclanton commented Apr 5, 2026

@copilot - take a look at the other FileSystem APIs. Many have an optional options parameter with useful utilities. For example, the write operations have an option to create the parent folder if it doesn't exist. Consider adding useful options to these new APIs

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 5, 2026

@copilot - take a look at the other FileSystem APIs. Many have an optional options parameter with useful utilities. For example, the write operations have an option to create the parent folder if it...

Added IFileSystemCreateWriteStreamOptions with ensureFolderExists to createWriteStream(), following the same pattern as IFileSystemWriteBinaryFileOptions and other write APIs. Updated FileSystemBuildCacheProvider to use the new option instead of manual ensureFolderAsync(). For createReadStream, there aren't useful options from the existing FileSystem conventions (read methods use encoding/convertLineEndings which don't apply to raw streams). Commit: 91161d8

@iclanton
Copy link
Copy Markdown
Member

iclanton commented Apr 5, 2026

@copilot - can we add stream support to the other cache plugins?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 5, 2026

@copilot - can we add stream support to the other cache plugins?

Added streaming support to both remaining cache plugins:

Amazon S3 plugin (733ef5c):

  • Refactored AmazonS3Client._makeRequestAsync to extract signing into _buildSignedRequest
  • Added _makeStreamRequestAsync using WebClient.fetchStreamAsync() with UNSIGNED-PAYLOAD for AWS Sig V4 signing of streaming uploads
  • Added getObjectStreamAsync() (with retry support) and uploadObjectStreamAsync() (single attempt, since consumed streams can't be replayed)
  • Added tryGetCacheEntryStreamByIdAsync and trySetCacheEntryStreamAsync to AmazonS3BuildCacheProvider

Azure Storage plugin (733ef5c):

  • Added tryGetCacheEntryStreamByIdAsync using blobClient.download() with readableStreamBody
  • Added trySetCacheEntryStreamAsync using blockBlobClient.uploadStream()
  • Added _logBlobError helper for consistent error handling across buffer and stream methods

All three cache plugins (HTTP, S3, Azure) now implement both streaming methods. All existing tests pass (45 S3, 10 Azure).

Copilot AI changed the title Add streaming APIs to build cache provider interface and HTTP plugin Add streaming APIs to build cache provider interface and all cache plugins Apr 5, 2026
@iclanton
Copy link
Copy Markdown
Member

iclanton commented Apr 5, 2026

@copilot - Can you put usage of this functionality behind a Rush experiment?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 5, 2026

@copilot - Can you put usage of this functionality behind a Rush experiment?

Added a useStreamingBuildCache experiment flag in experiments.json. The streaming code paths in OperationBuildCache now only activate when both the experiment is enabled AND the cloud provider implements the streaming methods. Without the experiment enabled, all cache operations use the existing buffer-based approach. Commit: 2493ed8

Copy link
Copy Markdown
Member

@iclanton iclanton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot - This change introduces a lot of duplicated code. I've called out some instances of it. Can you review with that in mind and clean that up?

* Makes an HTTP request that resolves as soon as headers are received, providing the
* response body as a readable stream. This avoids buffering the entire response in memory.
*/
const makeStreamRequestAsync: StreamFetchFn = async (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot - This function should be able to be DRY'd up with makeRequestAsync.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted shared _makeRawRequestAsync function that handles URL parsing, protocol selection, redirect following, body sending, and error handling. makeRequestAsync and makeStreamRequestAsync now both delegate to this shared core, each providing only their response-handling callback. Commit: a5dfa47

* does not use retry logic because the stream is consumed after the first attempt and cannot be
* replayed. The caller should handle failures accordingly.
*/
public async uploadObjectStreamAsync(objectName: string, objectStream: Readable): Promise<void> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot - This should be able to be DRY'd up with uploadObjectStreamAsync.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uploadObjectAsync and uploadObjectStreamAsync now both use the shared _makeSignedRequestAsync method. The upload methods still differ in retry behavior (buffer retries, stream doesn't), so they remain as separate public methods. Commit: a5dfa47

…lient private member type mismatch

Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/1b32730c-9ce0-4e75-a2e1-45f06a628960

Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
case WebClientProxy.Fiddler:
// For debugging, disable cert validation
// eslint-disable-next-line
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';

Check failure

Code scanning / CodeQL

Disabling certificate validation High

Disabling certificate validation is strongly discouraged.
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 5, 2026

@copilot - This change introduces a lot of duplicated code. I've called out some instances of it. Can you review with that in mind and clean that up?

Addressed all duplication instances across the three files:

  • WebClient.ts: Extracted _makeRawRequestAsync shared by both makeRequestAsync and makeStreamRequestAsync
  • AmazonS3Client.ts: Merged request methods into _makeSignedRequestAsync with overloads; extracted _handleGetResponseAsync for shared GET response handling
  • HttpBuildCacheProvider.ts: Extracted _makeHttpCoreRequestAsync shared by _makeHttpRequestAsync and _makeHttpStreamRequestAsync

Net result: -4 lines (337 added, 341 removed). Commit: a5dfa47

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs triage

Development

Successfully merging this pull request may close these issues.

3 participants