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
280 changes: 280 additions & 0 deletions packages/realm-server/tests/module-cache-race-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,284 @@ module(basename(__filename), function () {
});
},
);

// CS-11029: concurrent same-path readers used to each independently call
// transpileJS (50–500 ms of babel + ember-template-compilation +
// decorator transforms). The in-flight dedup map coalesces them onto a
// single transpile; later waiters await the same promise. The realm
// tracks a monotonic transpile counter exposed via
// __testOnlyGetTranspileCallCount so the tests can assert "exactly one
// transpile call" directly rather than inferring it from timing.
module('Realm.#moduleCache in-flight transpile dedup', function (hooks) {
let realmURL = new URL('http://127.0.0.1:4444/test/');
let testRealm: Realm;
let request: RealmRequest;

function onRealmSetup(args: {
testRealm: Realm;
testRealmHttpServer: Server;
request: SuperTest<Test>;
}) {
testRealm = args.testRealm;
request = withRealmPath(args.request, realmURL);
}

setupPermissionedRealmCached(hooks, {
realmURL,
permissions: {
'*': ['read', 'write'],
user: ['read', 'write', 'realm-owner'],
'@node-test_realm:localhost': ['read', 'realm-owner'],
},
onRealmSetup,
});

const transpilerHeavySource = `
import { contains, field, CardDef, Component } from "https://cardstack.com/base/card-api";
import StringField from "https://cardstack.com/base/string";

export class DedupCard extends CardDef {
@field name = contains(StringField);
static isolated = class Isolated extends Component<typeof this> {
<template>
<div data-test-dedup><@fields.name/></div>
</template>
}
}
`;

function authHeader() {
return `Bearer ${createJWT(testRealm, 'user', ['read', 'write'])}`;
}

function fireRequest(path: string): Promise<{ status: number }> {
return request
.get(`/${path}`)
.set('Accept', SupportedMimeType.All)
.set('Authorization', authHeader())
.then((r) => r as { status: number });
}

test('N concurrent same-path readers trigger exactly one transpile call', async function (assert) {
let modulePath = 'dedup-same-path.gts';
await testRealm.write(modulePath, transpilerHeavySource);
testRealm.__testOnlyClearCaches();

let before = testRealm.__testOnlyGetTranspileCallCount();
let responses = await Promise.all([
fireRequest(modulePath),
fireRequest(modulePath),
fireRequest(modulePath),
]);
let delta = testRealm.__testOnlyGetTranspileCallCount() - before;

assert.deepEqual(
responses.map((r) => r.status),
[200, 200, 200],
'all three concurrent same-path requests succeed',
);
assert.strictEqual(
delta,
1,
'exactly one transpileJS call serviced three concurrent same-path readers',
);
assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
0,
'in-flight slot released after the shared transpile settled',
);
});

test('concurrent different-path readers each trigger their own transpile (no false coalesce)', async function (assert) {
let pathA = 'dedup-a.gts';
let pathB = 'dedup-b.gts';
await testRealm.write(pathA, transpilerHeavySource);
await testRealm.write(pathB, transpilerHeavySource);
testRealm.__testOnlyClearCaches();

let before = testRealm.__testOnlyGetTranspileCallCount();
let responses = await Promise.all([
fireRequest(pathA),
fireRequest(pathB),
]);
let delta = testRealm.__testOnlyGetTranspileCallCount() - before;

assert.deepEqual(
responses.map((r) => r.status),
[200, 200],
'both different-path requests succeed',
);
assert.strictEqual(
delta,
2,
'each distinct path triggers its own transpile — no cross-path false coalesce',
);
});

test('in-flight entry survives an unrelated path’s invalidate', async function (assert) {
let primaryPath = 'dedup-primary.gts';
let unrelatedPath = 'dedup-unrelated.gts';
await testRealm.write(primaryPath, transpilerHeavySource);
await testRealm.write(unrelatedPath, transpilerHeavySource);
testRealm.__testOnlyClearCaches();

let before = testRealm.__testOnlyGetTranspileCallCount();
let primaryInflight = fireRequest(primaryPath);
// Wait long enough for fallbackHandle to install the in-flight entry.
await new Promise((resolve) => setTimeout(resolve, 50));

assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
1,
'primary path has an in-flight entry',
);

// Invalidate an unrelated path — should not affect primary's entry.
testRealm.invalidateCache(unrelatedPath);

assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
1,
'unrelated invalidate did not drop the primary in-flight entry',
);

// A second concurrent caller for the primary path joins the same
// in-flight promise.
let primaryJoiner = fireRequest(primaryPath);
await primaryInflight;
await primaryJoiner;
let delta = testRealm.__testOnlyGetTranspileCallCount() - before;

assert.strictEqual(
delta,
1,
'both primary callers shared the same in-flight transpile',
);
});

test('in-flight entry is dropped when its own path is invalidated; later caller starts a fresh transpile', async function (assert) {
let modulePath = 'dedup-self-invalidate.gts';
await testRealm.write(modulePath, transpilerHeavySource);
testRealm.__testOnlyClearCaches();

let before = testRealm.__testOnlyGetTranspileCallCount();
let firstInflight = fireRequest(modulePath);
await new Promise((resolve) => setTimeout(resolve, 50));

// Invalidate the path — drops the in-flight entry. The original
// caller still awaits the same promise (its bytes are correct for
// its happens-before-invalidate request).
testRealm.invalidateCache(modulePath);

assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
0,
'in-flight entry dropped by invalidateCache',
);

// A caller arriving after the invalidate must not join the stale
// in-flight (it was dropped); it should start a fresh transpile.
let secondInflight = fireRequest(modulePath);
await new Promise((resolve) => setTimeout(resolve, 50));

assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
1,
'second caller installed its own fresh in-flight entry',
);

await firstInflight;
await secondInflight;
let delta = testRealm.__testOnlyGetTranspileCallCount() - before;
assert.strictEqual(
delta,
2,
'invalidate forces a second transpile — the dropped slot is not joined by post-invalidate callers',
);
});

test('identity-checked cleanup: A in-flight + invalidate + B in-flight + A settles → B survives', async function (assert) {
let modulePath = 'dedup-identity.gts';
await testRealm.write(modulePath, transpilerHeavySource);
testRealm.__testOnlyClearCaches();

let pendingA = fireRequest(modulePath);
await new Promise((resolve) => setTimeout(resolve, 30));

// Invalidate drops A from the map. A's promise is still alive
// (held by pendingA); when it settles, .finally must NOT delete
// the slot that a newer caller installed in the meantime.
testRealm.invalidateCache(modulePath);

let pendingB = fireRequest(modulePath);
await new Promise((resolve) => setTimeout(resolve, 30));

assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
1,
'B installed a fresh in-flight entry after invalidate dropped A',
);

// Resolve A. Its .finally runs identity check — map.get(localPath)
// is B's pending, not A's — so the slot is preserved for B.
await pendingA;
assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
1,
'B’s in-flight entry survives A’s settle (identity check held)',
);

// Resolve B. Its .finally identity check passes; slot cleaned up.
await pendingB;
assert.strictEqual(
testRealm.__testOnlyGetInFlightTranspileCount(),
0,
'B’s in-flight entry cleaned up after B settled',
);
});

test('errored transpile is shared with concurrent waiters and the in-flight slot releases on rejection', async function (assert) {
let modulePath = 'dedup-error.gts';
// Syntactically invalid .gts source — transpileJS will throw.
await testRealm.write(
modulePath,
'this is not a valid gts file <template>oops</template>',
);
testRealm.__testOnlyClearCaches();

let before = testRealm.__testOnlyGetTranspileCallCount();
let [resA, resB] = await Promise.all([
fireRequest(modulePath),
fireRequest(modulePath),
]);
let deltaShared = testRealm.__testOnlyGetTranspileCallCount() - before;

assert.strictEqual(
resA.status,
406,
'first errored request returns a transpile-failed response',
);
assert.strictEqual(
resB.status,
406,
'concurrent waiter shares the same error response',
);
assert.strictEqual(
deltaShared,
1,
'concurrent waiters shared exactly one transpile attempt — the rejection propagates without a second babel pass',
);

// After both fail, the in-flight slot must release so a fresh
// caller re-attempts transpile against current source.
let resC = await fireRequest(modulePath);
assert.strictEqual(resC.status, 406);
let deltaAfter = testRealm.__testOnlyGetTranspileCallCount() - before;
assert.strictEqual(
deltaAfter,
2,
'subsequent caller triggers a fresh transpile attempt — error did not pin the in-flight slot',
);
});
});
});
Loading
Loading