authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-16 10:54:00-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-16 10:54:00-07:00
loga5efb7cb6b9e80b9543bc3e0aabfea37d3b459c5
treedea81b01ef04943b45317f5dc3600a4dbb452db9
parent8f9911f79dcfa6890c6f14f9c712c236e8453cea
signature Signed by PGP key B5690EEEBB952194

Parallelize VCS status refresh and status reads (#3112)


3 files changed, 140 insertions(+), 30 deletions(-)

apps/server/src/git/GitManager.ts+3-1
......@@ -1361,7 +1361,9 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
13611361 },
13621362 );
13631363 const status: GitManagerShape["status"] = Effect.fn("status")(function* (input) {
1364 const [local, remote] = yield* Effect.all([localStatus(input), remoteStatus(input)]);
1364 const [local, remote] = yield* Effect.all([localStatus(input), remoteStatus(input)], {
1365 concurrency: "unbounded",
1366 });
13651367 return mergeGitStatusParts(local, remote);
13661368 });
13671369 const invalidateLocalStatus: GitManagerShape["invalidateLocalStatus"] = Effect.fn(
apps/server/src/vcs/VcsStatusBroadcaster.test.ts+66
......@@ -17,6 +17,7 @@ import type {
1717 VcsStatusResult,
1818 VcsStatusStreamEvent,
1919} from "@t3tools/contracts";
20import { GitManagerError } from "@t3tools/contracts";
2021
2122import * as VcsStatusBroadcaster from "./VcsStatusBroadcaster.ts";
2223import * as GitWorkflowService from "../git/GitWorkflowService.ts";
......@@ -149,6 +150,71 @@ describe("VcsStatusBroadcaster", () => {
149150 }).pipe(Effect.provide(makeTestLayer(state)));
150151 });
151152
153 it.effect("keeps the cached snapshot unchanged when a refresh branch fails", () => {
154 const state = {
155 currentLocalStatus: baseLocalStatus,
156 currentRemoteStatus: baseRemoteStatus,
157 localStatusCalls: 0,
158 remoteStatusCalls: 0,
159 localInvalidationCalls: 0,
160 remoteInvalidationCalls: 0,
161 failRemoteStatus: false,
162 };
163 const testLayer = VcsStatusBroadcaster.layer.pipe(
164 Layer.provideMerge(NodeServices.layer),
165 Layer.provide(
166 Layer.mock(GitWorkflowService.GitWorkflowService)({
167 localStatus: () =>
168 Effect.sync(() => {
169 state.localStatusCalls += 1;
170 return state.currentLocalStatus;
171 }),
172 remoteStatus: () =>
173 Effect.suspend(() => {
174 state.remoteStatusCalls += 1;
175 return state.failRemoteStatus
176 ? Effect.fail(
177 new GitManagerError({
178 operation: "VcsStatusBroadcaster.test",
179 detail: "remote status failed",
180 }),
181 )
182 : Effect.succeed(state.currentRemoteStatus);
183 }),
184 invalidateLocalStatus: () =>
185 Effect.sync(() => {
186 state.localInvalidationCalls += 1;
187 }),
188 invalidateRemoteStatus: () =>
189 Effect.sync(() => {
190 state.remoteInvalidationCalls += 1;
191 }),
192 }),
193 ),
194 );
195
196 return Effect.gen(function* () {
197 const broadcaster = yield* VcsStatusBroadcaster.VcsStatusBroadcaster;
198 yield* broadcaster.getStatus({ cwd: "/repo" });
199
200 state.currentLocalStatus = {
201 ...baseLocalStatus,
202 refName: "feature/partial-refresh",
203 };
204 state.currentRemoteStatus = {
205 ...baseRemoteStatus,
206 aheadCount: 3,
207 };
208 state.failRemoteStatus = true;
209
210 const refreshExit = yield* broadcaster.refreshStatus("/repo").pipe(Effect.exit);
211 const cached = yield* broadcaster.getStatus({ cwd: "/repo" });
212
213 assert.isTrue(Exit.isFailure(refreshExit));
214 assert.deepStrictEqual(cached, baseStatus);
215 }).pipe(Effect.provide(testLayer));
216 });
217
152218 it.effect("refreshes only the cached local snapshot when requested", () => {
153219 const state = {
154220 currentLocalStatus: baseLocalStatus,
apps/server/src/vcs/VcsStatusBroadcaster.ts+71-29
......@@ -179,18 +179,53 @@ export const layer = Layer.effect(
179179 },
180180 );
181181
182 const loadLocalStatus = Effect.fn("VcsStatusBroadcaster.loadLocalStatus")(function* (
182 const updateCachedStatus = Effect.fn("VcsStatusBroadcaster.updateCachedStatus")(function* (
183183 cwd: string,
184 local: VcsStatusLocalResult,
185 remote: VcsStatusRemoteResult | null,
186 options?: { publish?: boolean },
184187 ) {
185 const local = yield* workflow.localStatus({ cwd });
186 return yield* updateCachedLocalStatus(cwd, local);
188 const nextLocal = {
189 fingerprint: fingerprintStatusPart(local),
190 value: local,
191 } satisfies CachedValue<VcsStatusLocalResult>;
192 const nextRemote = {
193 fingerprint: fingerprintStatusPart(remote),
194 value: remote,
195 } satisfies CachedValue<VcsStatusRemoteResult | null>;
196 const shouldPublish = yield* Ref.modify(cacheRef, (cache) => {
197 const previous = cache.get(cwd) ?? { local: null, remote: null };
198 const nextCache = new Map(cache);
199 nextCache.set(cwd, {
200 local: nextLocal,
201 remote: nextRemote,
202 });
203 return [
204 previous.local?.fingerprint !== nextLocal.fingerprint ||
205 previous.remote?.fingerprint !== nextRemote.fingerprint,
206 nextCache,
207 ] as const;
208 });
209
210 if (options?.publish && shouldPublish) {
211 yield* PubSub.publish(changesPubSub, {
212 cwd,
213 event: {
214 _tag: "snapshot",
215 local,
216 remote,
217 },
218 });
219 }
220
221 return mergeGitStatusParts(local, remote);
187222 });
188223
189 const loadRemoteStatus = Effect.fn("VcsStatusBroadcaster.loadRemoteStatus")(function* (
224 const loadLocalStatus = Effect.fn("VcsStatusBroadcaster.loadLocalStatus")(function* (
190225 cwd: string,
191226 ) {
192 const remote = yield* workflow.remoteStatus({ cwd });
193 return yield* updateCachedRemoteStatus(cwd, remote);
227 const local = yield* workflow.localStatus({ cwd });
228 return yield* updateCachedLocalStatus(cwd, local);
194229 });
195230
196231 const getOrLoadLocalStatus = Effect.fn("VcsStatusBroadcaster.getOrLoadLocalStatus")(function* (
......@@ -203,36 +238,39 @@ export const layer = Layer.effect(
203238 return yield* loadLocalStatus(cwd);
204239 });
205240
206 const getOrLoadRemoteStatus = Effect.fn("VcsStatusBroadcaster.getOrLoadRemoteStatus")(
207 function* (cwd: string) {
208 const cached = yield* getCachedStatus(cwd);
209 if (cached?.remote) {
210 return cached.remote.value;
211 }
212 return yield* loadRemoteStatus(cwd);
213 },
214 );
215
216241 const withFileSystem = Effect.provideService(FileSystem.FileSystem, fs);
217242
218243 const getStatus: VcsStatusBroadcasterShape["getStatus"] = Effect.fn(
219244 "VcsStatusBroadcaster.getStatus",
220245 )(function* (input) {
221246 const cwd = yield* withFileSystem(normalizeCwd(input.cwd));
222 const [local, remote] = yield* Effect.all([
223 getOrLoadLocalStatus(cwd),
224 getOrLoadRemoteStatus(cwd),
225 ]);
226 return mergeGitStatusParts(local, remote);
247 const cached = yield* getCachedStatus(cwd);
248 if (cached?.local && cached.remote) {
249 return mergeGitStatusParts(cached.local.value, cached.remote.value);
250 }
251 const [local, remote] = yield* Effect.all(
252 [
253 cached?.local ? Effect.succeed(cached.local.value) : workflow.localStatus({ cwd }),
254 cached?.remote ? Effect.succeed(cached.remote.value) : workflow.remoteStatus({ cwd }),
255 ],
256 { concurrency: "unbounded" },
257 );
258 return yield* updateCachedStatus(cwd, local, remote);
227259 });
228260
261 const refreshLocalStatusCore = Effect.fn("VcsStatusBroadcaster.refreshLocalStatusCore")(
262 function* (cwd: string) {
263 yield* workflow.invalidateLocalStatus(cwd);
264 const local = yield* workflow.localStatus({ cwd });
265 return yield* updateCachedLocalStatus(cwd, local, { publish: true });
266 },
267 );
268
229269 const refreshLocalStatus: VcsStatusBroadcasterShape["refreshLocalStatus"] = Effect.fn(
230270 "VcsStatusBroadcaster.refreshLocalStatus",
231271 )(function* (rawCwd) {
232272 const cwd = yield* withFileSystem(normalizeCwd(rawCwd));
233 yield* workflow.invalidateLocalStatus(cwd);
234 const local = yield* workflow.localStatus({ cwd });
235 return yield* updateCachedLocalStatus(cwd, local, { publish: true });
273 return yield* refreshLocalStatusCore(cwd);
236274 });
237275
238276 const refreshRemoteStatus = Effect.fn("VcsStatusBroadcaster.refreshRemoteStatus")(function* (
......@@ -247,11 +285,15 @@ export const layer = Layer.effect(
247285 "VcsStatusBroadcaster.refreshStatus",
248286 )(function* (rawCwd) {
249287 const cwd = yield* withFileSystem(normalizeCwd(rawCwd));
250 const [local, remote] = yield* Effect.all([
251 refreshLocalStatus(cwd),
252 refreshRemoteStatus(cwd),
253 ]);
254 return mergeGitStatusParts(local, remote);
288 yield* Effect.all(
289 [workflow.invalidateLocalStatus(cwd), workflow.invalidateRemoteStatus(cwd)],
290 { concurrency: "unbounded", discard: true },
291 );
292 const [local, remote] = yield* Effect.all(
293 [workflow.localStatus({ cwd }), workflow.remoteStatus({ cwd })],
294 { concurrency: "unbounded" },
295 );
296 return yield* updateCachedStatus(cwd, local, remote, { publish: true });
255297 });
256298
257299 const makeRemoteRefreshLoop = (