Skip to content

Commit 9566652

Browse files
PR feedback
1 parent 6954897 commit 9566652

File tree

2 files changed

+33
-44
lines changed

2 files changed

+33
-44
lines changed

packages/nextjs/src/app-router/keyless-actions.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { redirect, RedirectType } from 'next/navigation';
66
import { errorThrower } from '../server/errorThrower';
77
import { detectClerkMiddleware } from '../server/headers-utils';
88
import { getKeylessCookieName, getKeylessCookieValue } from '../server/keyless';
9-
import { detectKeylessEnvDrift } from '../server/keyless-telemetry';
109
import { canUseKeyless } from '../utils/feature-flags';
1110

1211
type SetCookieOptions = Parameters<Awaited<ReturnType<typeof cookies>>['set']>[2];
@@ -62,8 +61,8 @@ export async function createOrReadKeylessAction(): Promise<null | Omit<Accountle
6261
return null;
6362
}
6463

65-
// Detect environment variable drift and fire telemetry events
66-
await detectKeylessEnvDrift();
64+
// Detect environment variable drift and fire telemetry event
65+
void import('../server/keyless-telemetry.js').then(m => m.detectKeylessEnvDrift()).catch(() => {});
6766

6867
const { clerkDevelopmentCache, createKeylessModeMessage } = await import('../server/keyless-log-cache.js');
6968

packages/nextjs/src/server/keyless-telemetry.ts

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,33 @@ function getTelemetryFlagFilePath(): string {
2525
}
2626

2727
/**
28-
* Checks if the telemetry event has already been fired by looking for a flag file.
29-
*/
30-
async function hasTelemetryEventBeenFired(): Promise<boolean> {
31-
try {
32-
await fs.access(getTelemetryFlagFilePath());
33-
return true;
34-
} catch {
35-
return false;
36-
}
37-
}
38-
39-
/**
40-
* Creates a flag file to mark that the telemetry event has been fired.
28+
* Atomically attempts to create a telemetry flag file to prevent duplicate events.
29+
* This uses the 'wx' flag to ensure only one process can successfully create the file.
30+
*
31+
* @returns true if this process won the race and created the flag file, false if it already exists
4132
*/
42-
async function markTelemetryEventAsFired(): Promise<void> {
33+
async function tryCreateTelemetryFlag(): Promise<boolean> {
4334
try {
4435
// Ensure the directory exists
4536
const dir = join(process.cwd(), '.clerk/.tmp');
4637
await fs.mkdir(dir, { recursive: true });
4738

48-
// Create the flag file with timestamp
39+
// Create the flag file with timestamp using 'wx' flag for atomic creation
4940
const flagData = {
5041
firedAt: new Date().toISOString(),
5142
event: EVENT_KEYLESS_ENV_DRIFT_DETECTED,
5243
};
5344

54-
await fs.writeFile(getTelemetryFlagFilePath(), JSON.stringify(flagData, null, 2));
55-
} catch (error) {
56-
// Silently handle errors to avoid breaking the application
45+
await fs.writeFile(getTelemetryFlagFilePath(), JSON.stringify(flagData, null, 2), { flag: 'wx' });
46+
return true; // Successfully created the file - we won the race
47+
} catch (error: any) {
48+
if (error.code === 'EEXIST') {
49+
// File already exists - another process won the race
50+
return false;
51+
}
52+
// Other errors (permission issues, etc.) - silently handle to avoid breaking the application
5753
console.warn('Failed to create telemetry flag file:', error);
54+
return false;
5855
}
5956
}
6057

@@ -77,11 +74,6 @@ export async function detectKeylessEnvDrift(): Promise<void> {
7774
}
7875

7976
try {
80-
// Check if telemetry event has already been fired
81-
if (await hasTelemetryEventBeenFired()) {
82-
return;
83-
}
84-
8577
// Dynamically import server-side dependencies to avoid client-side issues
8678
const { safeParseClerkFile } = await import('./keyless-node.js');
8779

@@ -101,7 +93,7 @@ export async function detectKeylessEnvDrift(): Promise<void> {
10193
const keylessFileHasKeys = Boolean(keylessFile?.publishableKey && keylessFile?.secretKey);
10294

10395
if (envVarsMissing && keylessFileHasKeys) {
104-
// Environment variables are missing but keyless file has keys - this is normal for keyless mode
96+
// Environment variables are missing and keyless file has keys. This is expected for keyless mode
10597
return;
10698
}
10799

@@ -114,22 +106,23 @@ export async function detectKeylessEnvDrift(): Promise<void> {
114106
// Check if there's a drift (mismatch between env vars and keyless file)
115107
const hasDrift = !publicKeyMatch || !secretKeyMatch;
116108

117-
const payload: EventKeylessEnvDriftPayload = {
118-
publicKeyMatch,
119-
secretKeyMatch,
120-
envVarsMissing,
121-
keylessFileHasKeys,
122-
keylessPublishableKey: keylessFile.publishableKey,
123-
envPublishableKey: envPublishableKey as string,
124-
};
109+
// Only emit telemetry if there's drift and we can atomically create the flag file
110+
if (hasDrift && (await tryCreateTelemetryFlag())) {
111+
const payload: EventKeylessEnvDriftPayload = {
112+
publicKeyMatch,
113+
secretKeyMatch,
114+
envVarsMissing,
115+
keylessFileHasKeys,
116+
keylessPublishableKey: keylessFile.publishableKey,
117+
envPublishableKey: envPublishableKey as string,
118+
};
125119

126-
// Create a clerk client to access telemetry
127-
const clerkClient = createClerkClientWithOptions({
128-
publishableKey: keylessFile.publishableKey,
129-
secretKey: keylessFile.secretKey,
130-
});
120+
// Create a clerk client to access telemetry
121+
const clerkClient = createClerkClientWithOptions({
122+
publishableKey: keylessFile.publishableKey,
123+
secretKey: keylessFile.secretKey,
124+
});
131125

132-
if (hasDrift) {
133126
// Fire drift detected event
134127
const driftDetectedEvent: TelemetryEventRaw<EventKeylessEnvDriftPayload> = {
135128
event: EVENT_KEYLESS_ENV_DRIFT_DETECTED,
@@ -138,9 +131,6 @@ export async function detectKeylessEnvDrift(): Promise<void> {
138131
};
139132

140133
clerkClient.telemetry?.record(driftDetectedEvent);
141-
142-
// Mark the telemetry event as fired to prevent future executions
143-
await markTelemetryEventAsFired();
144134
}
145135
} catch (error) {
146136
// Silently handle errors to avoid breaking the application

0 commit comments

Comments
 (0)