Skip to content

Commit 1684dad

Browse files
committed
fixup! fix(cloudflare): Don't treat DO constructor work as incoming RPC calls
1 parent ef077a7 commit 1684dad

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
- **fix(cloudflare)**: Durable Object constructors now run in their own isolation scope. When work that the constructor starts, for example a `blockConcurrencyWhile` callback, calls a method of the instance, that call is no longer treated as an incoming RPC call, so an error the instance catches itself is no longer reported as unhandled. As a result, `Sentry.setTag()`, `setUser()` and `setContext()` in a constructor now apply only to that constructor work. They no longer reach later `fetch`, RPC or `alarm` invocations, because the scope they used to write to is shared by every Durable Object and handler in the isolate. Use `initialScope` for static data, and set per-instance data in each handler.
8+
79
## 11.2.0
810

911
### Important Changes

‎packages/cloudflare/test/durableobject.test.ts‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,54 @@ describe('instrumentDurableObjectWithSentry', () => {
272272
expect(events[1]?.user).toBeUndefined();
273273
});
274274

275+
// The only scope that all later invocations share is the default isolation scope of the isolate, so
276+
// data set in the constructor stays in the constructor's own scope.
277+
it('does not apply scope data set in the constructor to later invocations', async () => {
278+
const events: Event[] = [];
279+
const waits: Promise<unknown>[] = [];
280+
const mockContext = {
281+
waitUntil: vi.fn((promise: Promise<unknown>) => {
282+
waits.push(promise);
283+
}),
284+
} as any;
285+
286+
const testClass = class {
287+
constructor() {
288+
SentryCore.setTag('constructor_tag', 'from-constructor');
289+
SentryCore.setUser({ id: 'user-from-constructor' });
290+
}
291+
292+
alarm() {
293+
SentryCore.captureMessage('alarm');
294+
}
295+
296+
rpcMethod() {
297+
SentryCore.captureMessage('rpc');
298+
}
299+
};
300+
const obj = Reflect.construct(
301+
instrumentDurableObjectWithSentry(
302+
() => ({
303+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
304+
beforeSend(event: Event) {
305+
events.push(event);
306+
return null;
307+
},
308+
}),
309+
testClass as any,
310+
),
311+
[mockContext, {} as any],
312+
);
313+
314+
await obj.alarm();
315+
obj.rpcMethod();
316+
await Promise.all(waits);
317+
318+
expect(events.map(event => event.message)).toEqual(['alarm', 'rpc']);
319+
expect(events.map(event => event.tags?.constructor_tag)).toEqual([undefined, undefined]);
320+
expect(events.map(event => event.user)).toEqual([undefined, undefined]);
321+
});
322+
275323
it('Built-in durable object methods are always instrumented', () => {
276324
const testClass = class {
277325
fetch() {}

0 commit comments

Comments
 (0)