Skip to content

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

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

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

Conversation

@cconstable
Copy link
Copy Markdown
Contributor

@cconstable cconstable commented May 28, 2026

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.

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.

@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.

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