diff --git a/.changeset/weak-cats-admire.md b/.changeset/weak-cats-admire.md new file mode 100644 index 0000000000..e514c2aa17 --- /dev/null +++ b/.changeset/weak-cats-admire.md @@ -0,0 +1,5 @@ +--- +"@workflow/world-vercel": patch +--- + +Use undici dispatcher for queue client diff --git a/packages/world-vercel/src/http-client.ts b/packages/world-vercel/src/http-client.ts index a6c1d7f63b..fb3851dbb9 100644 --- a/packages/world-vercel/src/http-client.ts +++ b/packages/world-vercel/src/http-client.ts @@ -8,12 +8,6 @@ let _dispatcher: RetryAgent | undefined; * - Connection pooling (up to 8 connections per origin) * - Retry: Automatic retry on 429/5xx or network errors with exponential backoff * - Observes Retry-After header if received and lower than 30s - * - * Note: HTTP/2 is disabled because undici's experimental H2 support hangs - * in certain Vercel runtime environments (sveltekit). HTTP/1.1 pipelining - * is also disabled (pipelining: 1) because it causes head-of-line blocking - * that deadlocks the webhook respondWith mechanism. The primary benefits - * from undici here are retry logic and connection pooling. */ export function getDispatcher(): RetryAgent { if (!_dispatcher) { @@ -21,6 +15,12 @@ export function getDispatcher(): RetryAgent { new Agent({ connections: 8, keepAliveTimeout: 10_000, + // H2 is specifically incompatible with SvelteKit on Vercel prod. Everything else + // runs fine. + // TODO: Investigate/fix the failure on SvelteKit so we can re-enable H2. + allowH2: false, + // HTTP/1.1 pipelining is disabled (pipelining: 1) because it causes + // head-of-line blocking that deadlocks the webhook respondWith mechanism. pipelining: 1, }), { diff --git a/packages/world-vercel/src/queue.ts b/packages/world-vercel/src/queue.ts index 91ad590d9c..dae66172ed 100644 --- a/packages/world-vercel/src/queue.ts +++ b/packages/world-vercel/src/queue.ts @@ -8,6 +8,7 @@ import { ValidQueueName, } from '@workflow/world'; import * as z from 'zod'; +import { getDispatcher } from './http-client.js'; import { type APIConfig, getHeaders, getHttpUrl } from './utils.js'; const MessageWrapper = z.object({ @@ -84,6 +85,7 @@ export function createQueue(config?: APIConfig): Queue { const clientOptions = { region, + dispatcher: getDispatcher(), ...(usingProxy && { // final path will be /queues-proxy/api/v3/topic/... // and the proxy will strip the /queues-proxy prefix before forwarding to VQS diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c23103142e..5a72662b38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,8 +22,8 @@ catalogs: specifier: 3.2.0 version: 3.2.0 '@vercel/queue': - specifier: 0.1.1 - version: 0.1.1 + specifier: 0.1.4 + version: 0.1.4 '@vitest/coverage-v8': specifier: ^4.0.18 version: 4.0.18 @@ -1283,7 +1283,7 @@ importers: dependencies: '@vercel/queue': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.4 '@workflow/errors': specifier: workspace:* version: link:../errors @@ -1329,7 +1329,7 @@ importers: dependencies: '@vercel/queue': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.4 '@workflow/errors': specifier: workspace:* version: link:../errors @@ -1430,7 +1430,7 @@ importers: version: 3.2.0 '@vercel/queue': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.4 '@workflow/errors': specifier: workspace:* version: link:../errors @@ -2037,7 +2037,7 @@ importers: version: 1.15.3 '@vercel/analytics': specifier: latest - version: 2.0.1(3eb18ee0ef09bb7b6ddb50c31f32f06d) + version: 2.0.0(3eb18ee0ef09bb7b6ddb50c31f32f06d) '@workflow/swc-plugin': specifier: workspace:* version: link:../../packages/swc-plugin-workflow @@ -7996,8 +7996,8 @@ packages: vue-router: optional: true - '@vercel/analytics@2.0.1': - resolution: {integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==} + '@vercel/analytics@2.0.0': + resolution: {integrity: sha512-fP/ASXXz+1K/C2vWTnocd8RsGnkO9f1qOIDrhgQ3DagJtnea1EsM9AV9fDzjXlPIPb2vBQapxOIMCjtGIW8PZw==} peerDependencies: '@remix-run/react': ^2 '@sveltejs/kit': ^1 || ^2 @@ -8014,8 +8014,6 @@ packages: optional: true next: optional: true - nuxt: - optional: true react: optional: true svelte: @@ -8105,8 +8103,8 @@ packages: '@opentelemetry/sdk-metrics': '>=1.19.0 <2.0.0' '@opentelemetry/sdk-trace-base': '>=1.19.0 <2.0.0' - '@vercel/queue@0.1.1': - resolution: {integrity: sha512-ozO0tSBXUYN4gUkK65GbcqgxpC55qaaiY9MzNuXW4cvOSJ5nCkcgO+DQXcfyfL7h+0uIC5HTcP0mPvQ3dW3EhQ==} + '@vercel/queue@0.1.4': + resolution: {integrity: sha512-wo+jCycmCX078vQSbkX+RcLvySONDCK0f9aQp5UMKQD1+B+xKt3YVbIYbZukvoHQpbm5nnk6If+ADSeK/PmCgQ==} engines: {node: '>=20.0.0'} '@vercel/routing-utils@5.3.0': @@ -12154,10 +12152,6 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - minimatch@10.0.3: - resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} - engines: {node: 20 || >=22} - minimatch@10.1.1: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} @@ -22561,13 +22555,13 @@ snapshots: '@ts-morph/common@0.28.0': dependencies: - minimatch: 10.0.3 + minimatch: 10.2.4 path-browserify: 1.0.1 tinyglobby: 0.2.15 '@ts-morph/common@0.28.1': dependencies: - minimatch: 10.1.1 + minimatch: 10.2.4 path-browserify: 1.0.1 tinyglobby: 0.2.15 @@ -22971,11 +22965,12 @@ snapshots: vue: 3.5.22(typescript@5.9.3) vue-router: 4.6.3(vue@3.5.22(typescript@5.9.3)) - '@vercel/analytics@2.0.1(3eb18ee0ef09bb7b6ddb50c31f32f06d)': + '@vercel/analytics@2.0.0(3eb18ee0ef09bb7b6ddb50c31f32f06d)': + dependencies: + nuxt: 4.1.3(@biomejs/biome@2.4.4)(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.19.0)(@vercel/blob@2.0.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.22)(better-sqlite3@11.10.0)(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.45.1(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.8)))(drizzle-orm@0.45.1(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.8))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1) optionalDependencies: '@sveltejs/kit': 2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) next: 16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - nuxt: 4.1.3(@biomejs/biome@2.4.4)(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.19.0)(@vercel/blob@2.0.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.22)(better-sqlite3@11.10.0)(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.45.1(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.8)))(drizzle-orm@0.45.1(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.8))(eslint@9.38.0(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(yaml@2.8.1) react: 19.2.4 svelte: 5.43.3 vue: 3.5.22(typescript@5.9.3) @@ -23094,10 +23089,12 @@ snapshots: '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@vercel/queue@0.1.1': + '@vercel/queue@0.1.4': dependencies: - '@vercel/oidc': 3.0.5 + '@vercel/oidc': 3.2.0 + minimatch: 10.2.4 mixpart: 0.0.5 + picocolors: 1.1.1 '@vercel/routing-utils@5.3.0': dependencies: @@ -26454,7 +26451,7 @@ snapshots: glob@13.0.0: dependencies: - minimatch: 10.1.1 + minimatch: 10.2.4 minipass: 7.1.2 path-scurry: 2.0.1 @@ -28125,10 +28122,6 @@ snapshots: mimic-response@4.0.0: {} - minimatch@10.0.3: - dependencies: - '@isaacs/brace-expansion': 5.0.0 - minimatch@10.1.1: dependencies: '@isaacs/brace-expansion': 5.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 224401005c..ab59742ae0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,7 +11,7 @@ catalog: "@types/node": 22.19.0 "@vercel/functions": ^3.4.3 "@vercel/oidc": 3.2.0 - "@vercel/queue": 0.1.1 + "@vercel/queue": 0.1.4 "@vitest/coverage-v8": ^4.0.18 ai: 5.0.104 esbuild: ^0.27.3