Skip to content

Commit d377ca2

Browse files
committed
Add param to force invalidation of entire realm to from-scratch-job
1 parent 2ab7cb1 commit d377ca2

File tree

7 files changed

+105
-56
lines changed

7 files changed

+105
-56
lines changed

packages/host/app/components/card-prerender.gts

+27-16
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ export default class CardPrerender extends Component {
5252
}
5353
}
5454

55-
private async fromScratch(realmURL: URL): Promise<IndexResults> {
55+
private async fromScratch(
56+
realmURL: URL,
57+
invalidateEntireRealm: boolean,
58+
): Promise<IndexResults> {
5659
try {
57-
let results = await this.doFromScratch.perform(realmURL);
60+
let results = await this.doFromScratch.perform(
61+
realmURL,
62+
invalidateEntireRealm,
63+
);
5864
return results;
5965
} catch (e: any) {
6066
if (!didCancel(e)) {
@@ -99,21 +105,26 @@ export default class CardPrerender extends Component {
99105
await register(this.fromScratch.bind(this), this.incremental.bind(this));
100106
});
101107

102-
private doFromScratch = enqueueTask(async (realmURL: URL) => {
103-
let { reader, indexWriter } = this.getRunnerParams(realmURL);
104-
let currentRun = new CurrentRun({
105-
realmURL,
106-
reader,
107-
indexWriter,
108-
renderCard: this.renderService.renderCard,
109-
render: this.renderService.render,
110-
});
111-
setOwner(currentRun, getOwner(this)!);
108+
private doFromScratch = enqueueTask(
109+
async (realmURL: URL, invalidateEntireRealm: boolean) => {
110+
let { reader, indexWriter } = this.getRunnerParams(realmURL);
111+
let currentRun = new CurrentRun({
112+
realmURL,
113+
reader,
114+
indexWriter,
115+
renderCard: this.renderService.renderCard,
116+
render: this.renderService.render,
117+
});
118+
setOwner(currentRun, getOwner(this)!);
112119

113-
let current = await CurrentRun.fromScratch(currentRun);
114-
this.renderService.indexRunDeferred?.fulfill();
115-
return current;
116-
});
120+
let current = await CurrentRun.fromScratch(
121+
currentRun,
122+
invalidateEntireRealm,
123+
);
124+
this.renderService.indexRunDeferred?.fulfill();
125+
return current;
126+
},
127+
);
117128

118129
private doIncremental = enqueueTask(
119130
async (

packages/host/app/lib/current-run.ts

+37-21
Original file line numberDiff line numberDiff line change
@@ -121,26 +121,50 @@ export class CurrentRun {
121121
this.#render = render;
122122
}
123123

124-
static async fromScratch(current: CurrentRun): Promise<IndexResults> {
124+
static async fromScratch(
125+
current: CurrentRun,
126+
invalidateEntireRealm?: boolean,
127+
): Promise<IndexResults> {
125128
let start = Date.now();
126129
log.debug(`starting from scratch indexing`);
127130
console.log(
128131
`starting from scratch indexing for realm ${current.realmURL.href}`,
129132
);
130133

131134
current.#batch = await current.#indexWriter.createBatch(current.realmURL);
132-
let mtimesStart = Date.now();
133-
let mtimes = await current.batch.getModifiedTimes();
134-
console.log(
135-
`completed getting index mtimes in ${Date.now() - mtimesStart} ms`,
136-
);
137-
let invalidateStart = Date.now();
138-
let invalidations = (
139-
await current.discoverInvalidations(current.realmURL, mtimes)
140-
).map((href) => new URL(href));
141-
console.log(
142-
`completed invalidations in ${Date.now() - invalidateStart} ms`,
143-
);
135+
let invalidations: URL[] = [];
136+
if (invalidateEntireRealm) {
137+
console.log(
138+
`flag was set to invalidate entire realm ${current.realmURL.href}, skipping invalidation discovery`,
139+
);
140+
let mtimesStart = Date.now();
141+
let filesystemMtimes = await current.#reader.mtimes();
142+
console.log(
143+
`time to get file system mtimes ${Date.now() - mtimesStart} ms`,
144+
);
145+
invalidations = Object.keys(filesystemMtimes)
146+
.filter(
147+
(url) =>
148+
// Only allow json and executable files to be invalidated so that we
149+
// don't end up with invalidated files that weren't meant to be indexed
150+
// (images, etc)
151+
url.endsWith('.json') || hasExecutableExtension(url),
152+
)
153+
.map((url) => new URL(url));
154+
} else {
155+
let mtimesStart = Date.now();
156+
let mtimes = await current.batch.getModifiedTimes();
157+
console.log(
158+
`completed getting index mtimes in ${Date.now() - mtimesStart} ms`,
159+
);
160+
let invalidateStart = Date.now();
161+
invalidations = (
162+
await current.discoverInvalidations(current.realmURL, mtimes)
163+
).map((href) => new URL(href));
164+
console.log(
165+
`completed invalidations in ${Date.now() - invalidateStart} ms`,
166+
);
167+
}
144168

145169
await current.whileIndexing(async () => {
146170
let visitStart = Date.now();
@@ -299,17 +323,9 @@ export class CurrentRun {
299323
if (skipList.length === 0) {
300324
// the whole realm needs to be visited, no need to calculate
301325
// invalidations--it's everything
302-
console.log(
303-
`entire realm ${this.realmURL.href} is invalidated--skipping invalidation calculation`,
304-
);
305326
return invalidationList;
306327
}
307328

308-
console.log(
309-
`unable to use optimized invalidation for realm ${
310-
this.realmURL.href
311-
}, skipped these files ${JSON.stringify(skipList)}`,
312-
);
313329
let invalidationStart = Date.now();
314330
for (let invalidationURL of invalidationList) {
315331
await this.batch.invalidate(new URL(invalidationURL));

packages/host/app/services/local-indexer.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import { type TestRealmAdapter } from '@cardstack/host/tests/helpers/adapter';
88
// for the test-realm-adapter
99
export default class LocalIndexer extends Service {
1010
setup(
11-
_fromScratch: (realmURL: URL) => Promise<IndexResults>,
11+
_fromScratch: (
12+
realmURL: URL,
13+
invalidateEntireRealm: boolean,
14+
) => Promise<IndexResults>,
1215
_incremental: (
1316
url: URL,
1417
realmURL: URL,

packages/realm-server/server.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -347,18 +347,21 @@ export class RealmServer {
347347
this.log.debug(`seed files for new realm ${url} copied to ${realmPath}`);
348348
}
349349

350-
let realm = new Realm({
351-
url,
352-
adapter,
353-
secretSeed: this.secretSeed,
354-
virtualNetwork: this.virtualNetwork,
355-
dbAdapter: this.dbAdapter,
356-
queue: this.queue,
357-
matrix: {
358-
url: this.matrixClient.matrixURL,
359-
username,
350+
let realm = new Realm(
351+
{
352+
url,
353+
adapter,
354+
secretSeed: this.secretSeed,
355+
virtualNetwork: this.virtualNetwork,
356+
dbAdapter: this.dbAdapter,
357+
queue: this.queue,
358+
matrix: {
359+
url: this.matrixClient.matrixURL,
360+
username,
361+
},
360362
},
361-
});
363+
{ invalidateEntireRealm: true },
364+
);
362365
this.realms.push(realm);
363366
this.virtualNetwork.mount(realm.handle);
364367
return realm;

packages/runtime-common/realm-index-updater.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type Stats,
88
type DBAdapter,
99
type QueuePublisher,
10-
type WorkerArgs,
10+
type FromScratchArgs,
1111
type FromScratchResult,
1212
type IncrementalArgs,
1313
type IncrementalResult,
@@ -92,12 +92,13 @@ export class RealmIndexUpdater {
9292
// TODO consider triggering SSE events for invalidations now that we can
9393
// calculate fine grained invalidations for from-scratch indexing by passing
9494
// in an onInvalidation callback
95-
async fullIndex() {
95+
async fullIndex(invalidateEntireRealm?: boolean) {
9696
this.#indexingDeferred = new Deferred<void>();
9797
try {
98-
let args: WorkerArgs = {
98+
let args: FromScratchArgs = {
9999
realmURL: this.#realm.url,
100100
realmUsername: await this.getRealmUsername(),
101+
invalidateEntireRealm: Boolean(invalidateEntireRealm),
101102
};
102103
let job = await this.#queue.publish<FromScratchResult>(
103104
`from-scratch-index`,

packages/runtime-common/realm.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ export interface RealmAdapter {
163163

164164
interface Options {
165165
disableModuleCaching?: true;
166+
invalidateEntireRealm?: true;
166167
}
167168

168169
interface UpdateItem {
@@ -252,6 +253,7 @@ export class Realm {
252253
#recentWrites: Map<string, number> = new Map();
253254
#realmSecretSeed: string;
254255
#disableModuleCaching = false;
256+
#invalidateEntireRealm = false;
255257

256258
#publicEndpoints: RouteTable<true> = new Map([
257259
[
@@ -305,6 +307,7 @@ export class Realm {
305307
seed: secretSeed,
306308
});
307309
this.#disableModuleCaching = Boolean(opts?.disableModuleCaching);
310+
this.#invalidateEntireRealm = Boolean(opts?.invalidateEntireRealm);
308311

309312
let fetch = fetcher(virtualNetwork.fetch, [
310313
async (req, next) => {
@@ -450,7 +453,7 @@ export class Realm {
450453
}
451454

452455
async fullIndex() {
453-
await this.realmIndexUpdater.fullIndex();
456+
await this.realmIndexUpdater.fullIndex(this.#invalidateEntireRealm);
454457
}
455458

456459
async flushUpdateEvents() {

packages/runtime-common/worker.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ export interface Reader {
3939
}
4040

4141
export type RunnerRegistration = (
42-
fromScratch: (realmURL: URL) => Promise<IndexResults>,
42+
fromScratch: (
43+
realmURL: URL,
44+
invalidateEntireRealm: boolean,
45+
) => Promise<IndexResults>,
4346
incremental: (
4447
url: URL,
4548
realmURL: URL,
@@ -72,6 +75,10 @@ export interface IncrementalResult {
7275
stats: Stats;
7376
}
7477

78+
export interface FromScratchArgs extends WorkerArgs {
79+
invalidateEntireRealm: boolean;
80+
}
81+
7582
export interface FromScratchResult extends JSONTypes.Object {
7683
ignoreData: Record<string, string>;
7784
stats: Stats;
@@ -119,7 +126,11 @@ export class Worker {
119126
#matrixClientCache: Map<string, MatrixClient> = new Map();
120127
#secretSeed: string;
121128
#fromScratch:
122-
| ((realmURL: URL, boom?: true) => Promise<IndexResults>)
129+
| ((
130+
realmURL: URL,
131+
invalidateEntireRealm: boolean,
132+
boom?: true,
133+
) => Promise<IndexResults>)
123134
| undefined;
124135
#incremental:
125136
| ((
@@ -239,13 +250,14 @@ export class Worker {
239250
return result;
240251
}
241252

242-
private fromScratch = async (args: WorkerArgs) => {
253+
private fromScratch = async (args: FromScratchArgs) => {
243254
return await this.prepareAndRunJob<FromScratchResult>(args, async () => {
244255
if (!this.#fromScratch) {
245256
throw new Error(`Index runner has not been registered`);
246257
}
247258
let { ignoreData, stats } = await this.#fromScratch(
248259
new URL(args.realmURL),
260+
args.invalidateEntireRealm,
249261
);
250262
return {
251263
ignoreData: { ...ignoreData },

0 commit comments

Comments
 (0)