Scope cached signed upload URL per app and command#7703
Conversation
The App Management client caches generateSignedUploadUrl responses for 59 minutes using only the organization ID as the cache key. When two `shopify app deploy --path=...` processes run concurrently for different apps in the same organization, both retrieve the same cached signed upload URL and upload their bundles to the same destination. App Management then reads whichever upload arrived last when each appVersionCreate is called, so apps end up with the wrong bundles. Add the app's apiKey and the current command id to cacheExtraKey so the cache remains useful for repeated dev hot-reload uploads of the same app, while concurrent deploys of different apps in the same org no longer share a signed upload destination. Fixes #7696. Co-authored-by: Isaac Roldan <isaac.roldan@shopify.com> Co-authored-by: Mitch Lillie <mitch.lillie@shopify.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses a concurrency bug in the App Management client where cached generateSignedUploadUrl responses could be reused across different apps in the same organization, causing parallel deploys to upload bundles to the same remote destination and potentially create app versions from the wrong bundle.
Changes:
- Scope the GraphQL request cache for signed upload URLs by adding
apiKey(and a command identifier) tocacheExtraKey. - Update unit tests to assert the new cache scoping behavior and add a regression-style assertion for differing apps in the same org.
- Add a changeset documenting the patch-level bug fix.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts | Adds extra cache scoping for signed upload URL requests to prevent cross-app collisions in parallel deploys. |
| packages/app/src/cli/utilities/developer-platform-client/app-management-client.test.ts | Updates tests to validate cache scoping and adds coverage for different-app scenarios. |
| .changeset/river-fix-parallel-deploy-cache-key.md | Declares a patch release and documents the concurrency fix. |
Comments suppressed due to low confidence (1)
packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts:739
getCurrentCommandId()is just the command name (e.g.,app:deploy) and is not unique per process/run, so two concurrentshopify app deployinvocations for the same app can still share a cached signed upload URL and overwrite each other. The CLI already sets a per-run identifier (process.env.COMMAND_RUN_ID) specifically to avoid cache collisions across parallel commands; using that here makes the cache key truly per-command-run while keeping hot-reload behavior within a single run.
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
MitchLillie
left a comment
There was a problem hiding this comment.
I ran a test using the static asset stress tester, which is how I discovered the bug. You should be able to use also (just tell it to use your local CLI and not bother with flipping any flags)
Result:
- Deploys complete: 10/10
- Apps verified: 10/10
- Expected asset config entries: 90/90
- Raw GCS checksum verified: 90/90
- Brotli decompress + checksum verified: 90/90
- GCS failures: 0
- Cross-app stress labels: none
- Junk asset leaks: none
- Active version tags all matched expected app-specific version tags
The App Management client caches generateSignedUploadUrl responses for 59 minutes using only the organization ID as the cache key. When two
shopify app deploy --path=...processes run concurrently for different apps in the same organization, both retrieve the same cached signed upload URL and upload their bundles to the same destination. App Management then reads whichever upload arrived last when each appVersionCreate is called, so apps end up with the wrong bundles.Add the app's apiKey and the current command id to cacheExtraKey so the cache remains useful for repeated dev hot-reload uploads of the same app, while concurrent deploys of different apps in the same org no longer share a signed upload destination.
Fixes #7696.
WHY are these changes introduced?
Fixes #0000
WHAT is this pull request doing?
How to test your changes?
Post-release steps
Checklist
patchfor bug fixes ·minorfor new features ·majorfor breaking changes) and added a changeset withpnpm changeset add