Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,10 @@ jobs:
label: cloudflare-sentry-app
shardIndex: 1
shardTotal: 1
- project: cloudflare-sentry-app-workers-cache
label: cloudflare-sentry-app-workers-cache
shardIndex: 1
shardTotal: 1
- project: cloudflare-sentry-pages
label: cloudflare-sentry-pages
shardIndex: 1
Expand Down
18 changes: 12 additions & 6 deletions packages/cloudflare/src/cache/cdn-adapter.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
} from "vinext/server/multi-stage";
import { loadVinextRequestStage } from "vinext/server/request-stage";
import { loadVinextResponseStage } from "vinext/server/response-stage";
import { traceCachedResponseStart } from "vinext/internal/server/response-start-tracing";
import { isNonCacheableCacheControl } from "vinext/shims/cdn-cache";
import { getVinextCdnBuildIdentity, VINEXT_CDN_BUILD_ID_HEADER } from "./cdn-build-id.js";

Expand Down Expand Up @@ -401,6 +402,7 @@ function preventResponseCaching(response: Response): Response {
function markSharedResponseStage(
response: Response,
provenanceToken: string,
responseStageProps: unknown,
exposeEntrypointCacheStatus = false,
): Response {
const headers = new Headers(response.headers);
Expand All @@ -409,11 +411,15 @@ function markSharedResponseStage(
SHARED_RESPONSE_STAGE_HEADER,
cacheStatus ? `${provenanceToken}:${encodeURIComponent(cacheStatus)}` : provenanceToken,
);
return new Response(response.body, {
headers,
status: response.status,
statusText: response.statusText,
});
return traceCachedResponseStart(
new Response(response.body, {
headers,
status: response.status,
statusText: response.statusText,
}),
cacheStatus,
responseStageProps,
);
}

function finalizeGatewayResponse(response: Response, provenanceToken: string): Response {
Expand Down Expand Up @@ -691,7 +697,7 @@ export default {
: stageRequest;
const response = validateResponseStageBuildIdentity(await binding.fetch(entrypointRequest));
return usesSharedCache
? markSharedResponseStage(response, sharedResponseStageProvenance, true)
? markSharedResponseStage(response, sharedResponseStageProvenance, props, true)
: response;
} catch (error) {
if (isResponseStageReadinessRequest(stageRequest)) return responseStageUnavailable();
Expand Down
32 changes: 21 additions & 11 deletions packages/cloudflare/src/cache/response-store-adapter.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
} from "vinext/server/multi-stage";
import { loadVinextRequestStage } from "vinext/server/request-stage";
import { loadVinextResponseStage } from "vinext/server/response-stage";
import { traceCachedResponseStart } from "vinext/internal/server/response-start-tracing";
import { isNonCacheableCacheControl } from "vinext/shims/cdn-cache";
import {
applyRscCompatibilityIdHeader,
Expand Down Expand Up @@ -283,7 +284,11 @@ function isResponseStoreMiss(response: Response): boolean {
return response.status === 404 && response.headers.get("X-Workers-Response-Store") === "MISS";
}

function publicResponse(response: Response, cacheStatus?: string): Response {
function publicResponse(
response: Response,
cacheStatus: string,
responseStageProps: unknown,
): Response {
const headers = new Headers(response.headers);
const publicCacheStatus =
cacheStatus === "HIT" && headers.get("CF-Cache-Status") === "UPDATING"
Expand Down Expand Up @@ -314,11 +319,15 @@ function publicResponse(response: Response, cacheStatus?: string): Response {
if (!cacheControl || !isNonCacheableCacheControl(cacheControl)) {
headers.set("Cache-Control", "private, max-age=0, must-revalidate");
}
return new Response(response.body, {
headers,
status: response.status,
statusText: response.statusText,
});
return traceCachedResponseStart(
new Response(response.body, {
headers,
status: response.status,
statusText: response.statusText,
}),
publicCacheStatus ?? null,
responseStageProps,
);
}

let responseStore: WorkersResponseStore;
Expand Down Expand Up @@ -349,6 +358,7 @@ const handler = {
return publicResponse(
await invokeResponseStage(stageRequest, props, env, ctx, "bypass"),
"BYPASS",
props,
);
}

Expand Down Expand Up @@ -380,12 +390,12 @@ const handler = {
const key = await cacheRequest(stageRequest, props);
const stored = await responseStore.fetch(key);
if (!isResponseStoreMiss(stored)) {
if (!rscKey) return publicResponse(stored, "HIT");
if (!rscKey) return publicResponse(stored, "HIT", props);

const storedRsc = await responseStore.fetch(rscKey);
if (!isResponseStoreMiss(storedRsc)) {
await storedRsc.body?.cancel();
return publicResponse(stored, "HIT");
return publicResponse(stored, "HIT", props);
}
await Promise.all([stored.body?.cancel(), storedRsc.body?.cancel()]);
}
Expand Down Expand Up @@ -418,11 +428,11 @@ const handler = {
);
}),
);
return publicResponse(rendered, "MISS");
return publicResponse(rendered, "MISS", props);
}
if (!isCacheable(rendered)) {
void capture?.rscData?.catch(() => {});
return publicResponse(rendered, "BYPASS");
return publicResponse(rendered, "BYPASS", props);
}
if (rscSeed && !capture?.rscData) {
await rendered.body?.cancel();
Expand Down Expand Up @@ -459,7 +469,7 @@ const handler = {
},
);
}
return publicResponse(new Response(foreground, rendered), "MISS");
return publicResponse(new Response(foreground, rendered), "MISS", props);
};

const { handleRequestStage } = await loadVinextRequestStage<
Expand Down
4 changes: 4 additions & 0 deletions packages/vinext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
"types": "./dist/server/cloudflare-workers-tracing.d.ts",
"import": "./dist/server/cloudflare-workers-tracing.js"
},
"./internal/server/response-start-tracing": {
"types": "./dist/server/response-start-tracing.d.ts",
"import": "./dist/server/response-start-tracing.js"
},
"./internal/build/run-prerender": {
"types": "./dist/build/run-prerender.d.ts",
"import": "./dist/build/run-prerender.js"
Expand Down
8 changes: 6 additions & 2 deletions packages/vinext/src/server/app-page-dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import {
markRouteCacheabilityPatternDynamic,
} from "vinext/shims/cacheability-classification";
import type { AppRenderErrorContextOverrides } from "./app-rsc-error-handler.js";
import { traceResponseStart } from "./response-start-tracing.js";

type AppPageParams = Record<string, string | string[]>;
type AppPageElement = ReactNode | Readonly<Record<string, ReactNode>>;
Expand Down Expand Up @@ -634,11 +635,14 @@ export async function dispatchAppPage<TRoute extends AppPageDispatchRoute>(
options: DispatchAppPageOptions<TRoute>,
): Promise<Response> {
const dispatch = () => runWithFetchDedupe(() => dispatchAppPageInner(options));
let response: Response;
if (!options.pprFallbackShell || !options.pprRuntime) {
return await dispatch();
response = await dispatch();
} else {
response = await options.pprRuntime.run(options.pprFallbackShell, dispatch);
}

return await options.pprRuntime.run(options.pprFallbackShell, dispatch);
return options.isRscRequest ? response : traceResponseStart(response);
}

async function dispatchAppPageInner<TRoute extends AppPageDispatchRoute>(
Expand Down
29 changes: 17 additions & 12 deletions packages/vinext/src/server/app-page-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import { appendRscCompletionMetadata } from "./rsc-completion-metadata.js";
import type { AppRenderErrorContextOverrides } from "./app-rsc-error-handler.js";
import { recordAppPageRenderError, traceAppPageRender } from "./app-page-tracing.js";
import type { FrameworkSpan } from "./framework-tracer.js";
import { traceResponseStartWithCompletion } from "./response-start-tracing.js";

type AppPageBoundaryOnError = (
error: unknown,
Expand Down Expand Up @@ -674,22 +675,26 @@ export async function renderAppPageLifecycle(
try {
const prepared = await prepareAppPageElement(options);
if (prepared instanceof Response) {
resolveResponse(prepared);
const traced = traceResponseStartWithCompletion(prepared);
resolveResponse(traced.response);
await traced.started;
return;
}
const response = await renderAppPageLifecycleImpl(
{
...prepared,
onRenderComplete(completion) {
renderCompletion = completion;
void completion.catch(() => {});
options.onRenderComplete?.(completion);
const traced = traceResponseStartWithCompletion(
await renderAppPageLifecycleImpl(
{
...prepared,
onRenderComplete(completion) {
renderCompletion = completion;
void completion.catch(() => {});
options.onRenderComplete?.(completion);
},
},
},
renderSpan,
renderSpan,
),
);
resolveResponse(response);
await renderCompletion;
resolveResponse(traced.response);
await Promise.all([renderCompletion, traced.started]);
} catch (error) {
rejectResponse(error);
throw error;
Expand Down
7 changes: 7 additions & 0 deletions packages/vinext/src/server/app-route-handler-dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import {
isOnDemandRevalidateRequest,
PRERENDER_REVALIDATE_HEADER,
} from "./revalidation-request.js";
import { traceResponseStart } from "./response-start-tracing.js";

type AppRouteHandlerDispatchRoute = {
pattern: string;
Expand Down Expand Up @@ -175,6 +176,12 @@ async function runInRouteHandlerRevalidationContext(

export async function dispatchAppRouteHandler(
options: DispatchAppRouteHandlerOptions,
): Promise<Response> {
return traceResponseStart(await dispatchAppRouteHandlerImpl(options));
}

async function dispatchAppRouteHandlerImpl(
options: DispatchAppRouteHandlerOptions,
): Promise<Response> {
const { route } = options;
const handler = route.routeHandler;
Expand Down
16 changes: 16 additions & 0 deletions packages/vinext/src/server/framework-tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type FrameworkTracingBackendSpan = {
};

export type FrameworkTracingIntegration = {
captureActiveContext?(): <T>(callback: () => T) => T;
getActiveSpan?(): FrameworkTracingBackendSpan | undefined;
id: string;
enterSpan<T>(
Expand All @@ -44,6 +45,7 @@ export type FrameworkSpan = {
};

export type FrameworkTracer = {
captureActiveContext(): <T>(callback: () => T) => T;
getActiveScopeSpan(): FrameworkSpan | undefined;
trace<T>(descriptor: FrameworkSpanDescriptor, callback: (span: FrameworkSpan) => T): T;
withPropagatedContext<T>(carrier: Headers, callback: () => T): T;
Expand Down Expand Up @@ -113,6 +115,20 @@ export function createFrameworkTracer(
integrations: readonly FrameworkTracingIntegration[],
): FrameworkTracer {
return {
captureActiveContext() {
const snapshots = integrations.flatMap((integration) => {
const snapshot = integration.captureActiveContext?.();
return snapshot ? [snapshot] : [];
});
return <T>(callback: () => T): T => {
const enter = (index: number): T => {
const snapshot = snapshots[index];
return snapshot ? snapshot(() => enter(index + 1)) : callback();
};
return enter(0);
};
},

getActiveScopeSpan(): FrameworkSpan | undefined {
const spans = integrations.flatMap((integration) => {
const span = integration.getActiveSpan?.();
Expand Down
6 changes: 6 additions & 0 deletions packages/vinext/src/server/opentelemetry-tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const noopSpan: FrameworkTracingBackendSpan = {

/** Standard OpenTelemetry backend using the provider registered by the app. */
export const openTelemetryTracingIntegration: FrameworkTracingIntegration = {
captureActiveContext() {
const api = getOpenTelemetryApi();
const context = api?.context.active();
return <T>(callback: () => T): T =>
api && context ? api.context.with(context, callback) : callback();
},
getActiveSpan() {
const api = getOpenTelemetryApi();
const span = api?.trace.getSpan(api.context.active());
Expand Down
4 changes: 4 additions & 0 deletions packages/vinext/src/server/request-tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
VINEXT_TRACE_ROUTE_HEADER,
} from "./headers.js";
import { frameworkTracer } from "./tracer.js";
import { getResponseStartCompletion } from "./response-start-tracing.js";

type ActiveRequestTrace = {
recordError(error: Error): void;
Expand Down Expand Up @@ -217,6 +218,9 @@ export function traceFrameworkRequest<T>(input: RequestTraceInput<T>): Promise<T
return result;
}
resolveResult(result);
if (result instanceof Response) {
await getResponseStartCompletion(result);
}
return result;
} catch (error) {
finalizeSpan();
Expand Down
Loading
Loading