Skip to content

External Storage: add external_storage_runner to handle core extstore logic#2094

Open
cconstable wants to merge 18 commits into
extstore-foundationfrom
extstore-core-logic
Open

External Storage: add external_storage_runner to handle core extstore logic#2094
cconstable wants to merge 18 commits into
extstore-foundationfrom
extstore-core-logic

Conversation

@cconstable

@cconstable cconstable commented May 28, 2026

Copy link
Copy Markdown
Contributor

What was changed

  • Adds a external_storage_runner that handles core external storage logic.
  • Adds some tentative errors and error codes. These just drafts and will change.

Checklist

  • New tests.

@cconstable cconstable requested a review from a team as a code owner May 28, 2026 14:07
@cconstable cconstable changed the title feat(extstore): add external_storage_runner to handle core extstore logic. External Storage: add external_storage_runner to handle core extstore logic May 28, 2026

await t.throwsAsync(() => runExternalStore({ externalStorage, payloads: [makePayload(1)] }), {
instanceOf: ExternalStorageSelectorInvalidDriverError,
message: /TMPRL1109/,

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.

Forgot to update these tests. Updating now.

* @internal
* @experimental
*/
export async function runExternalStore({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we create a class that has store and retrieve methods on it? I'm thinking ahead to where we want to add more capabilities, such as caching, and not wanting to pass more things via parameters. So that thing that are shared across all invocations (ExternalStorage object, hypothetical cache object/configuration) are provided at construction and the remaining variable pieces (payloads, target, abort signal) are parameters to store and retrieve functions.


payloads: Payload[];

/** Composed via `AbortSignal.any` with an internal controller so a failing driver call aborts siblings. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's not document implementation details, just expected behavior and usage.

abortSignal,
}: ExternalRetrieveOptions): Promise<Payload[]> {
if (payloads.length === 0) return payloads;
if (!payloads.some(isReferencePayload)) return payloads;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can remove this check to avoid multiple iteration of the payloads arg. And then inline the external storage check within the for loop.

interface RetrieveItem {
index: number;
claim: StorageDriverClaim;
/** `0` for legacy references that pre-date the size_bytes field. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this comment is inaccurate. All externally stored payloads had set this additional data on the payload protobuf message.

}
for (const [j, retrievedPayload] of retrieved.entries()) {
const item = group.items[j]!;
if (item.expectedSize > 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this check is necessary and in fact might prevent retrieval of otherwise valid externally stored payloads.

const item = group.items[j]!;
if (item.expectedSize > 0) {
const actual = payloadProtoSize(retrievedPayload);
if (actual !== item.expectedSize) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is performing extra validation that the other SDKs are not doing. Let's remove for now and add back if we feel this is necessary, but to all SDKs.

Comment thread packages/common/src/errors.ts Outdated
export class CompleteAsyncError extends Error {}

/// -----------------------------------------------------------------------
/// External Storage Errors (draft)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not convinced we need specialized error types for validations that do not cross into workflow/activity user code. These mostly are carrying specific error messages and error codes and fields that are not consumed by the rest of the system. I'm not opposed to the information they carry or the error codes necessarily, just the new error types themselves if they aren't specially treated.

For errors that drivers or the driver selector may raise that we want to specifically know about, those make sense to have dedicated error types.

Comment thread packages/common/src/errors.ts Outdated
@SymbolBasedInstanceOfError('ExternalStorageNotConfiguredError')
export class ExternalStorageNotConfiguredError extends Error {
constructor(message: string) {
super(`[TMPRL-SDK-00001] ${message}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

According to the rules page this should just emit a log... it doesn't look like an error. Regardless, I'll add the log for it when we actually integrate that piece. I preemptively added it here thinking it would be used in this PR.

@cconstable cconstable force-pushed the extstore-core-logic branch from 96233a3 to 570b9c8 Compare June 12, 2026 15:39
@cconstable cconstable force-pushed the extstore-foundation branch from 460485a to af5dbe1 Compare June 15, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants