Skip to content

Commit 29e6aa6

Browse files
committed
[server] Prefer exact matches plus order incremental matches by commit history
1 parent f4eac6d commit 29e6aa6

File tree

1 file changed

+54
-38
lines changed

1 file changed

+54
-38
lines changed

components/server/src/prebuilds/incremental-workspace-service.ts

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@ import {
1616
} from "@gitpod/gitpod-protocol";
1717
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1818
import { PrebuiltWorkspaceState, WithCommitHistory } from "@gitpod/gitpod-protocol/lib/protocol";
19-
import { WorkspaceDB } from "@gitpod/gitpod-db/lib";
19+
import { PrebuildWithWorkspace, WorkspaceDB } from "@gitpod/gitpod-db/lib";
2020
import { Config } from "../config";
2121
import { HostContextProvider } from "../auth/host-context-provider";
2222
import { ImageSourceProvider } from "../workspace/image-source-provider";
2323

2424
const MAX_HISTORY_DEPTH = 100;
2525

26-
enum Match {
27-
None = 0,
28-
Loose = 1,
29-
Exact = 2,
30-
}
26+
type IncrementalWorkspaceMatch = "none" | "incremental" | "exact";
3127

3228
@injectable()
3329
export class IncrementalWorkspaceService {
@@ -93,30 +89,44 @@ export class IncrementalWorkspaceService {
9389
const imageSource = await imageSourcePromise;
9490

9591
// traverse prebuilds by commit history instead of their creationTime, so that we don't match prebuilds created for older revisions but triggered later
96-
for (const commit of history.commitHistory) {
97-
const prebuildsForCommit = recentPrebuilds.filter(({ prebuild }) => prebuild.commit === commit);
98-
for (const entry of prebuildsForCommit) {
99-
const { prebuild, workspace } = entry;
100-
const match = this.isMatchForIncrementalBuild(
101-
history,
102-
config,
103-
imageSource,
92+
const candidates: { candidate: PrebuildWithWorkspace; index: number }[] = [];
93+
for (const recentPrebuild of recentPrebuilds) {
94+
const { prebuild, workspace } = recentPrebuild;
95+
const { match, index } = this.isMatchForIncrementalBuild(
96+
history,
97+
config,
98+
imageSource,
99+
prebuild,
100+
workspace,
101+
includeUnfinishedPrebuilds,
102+
);
103+
if (match === "exact") {
104+
console.log("Found base for incremental build", {
104105
prebuild,
105106
workspace,
106-
includeUnfinishedPrebuilds,
107-
);
108-
if (match > Match.None) {
109-
console.log("Found base for incremental build", {
110-
prebuild,
111-
workspace,
112-
exactMatch: match === Match.Exact,
113-
});
114-
return prebuild;
115-
}
107+
exactMatch: true,
108+
});
109+
return prebuild;
110+
}
111+
if (match === "incremental") {
112+
candidates.push({ candidate: recentPrebuild, index: index! });
116113
}
117114
}
118115

119-
return undefined;
116+
if (candidates.length === 0) {
117+
return undefined;
118+
}
119+
120+
// Sort by index ASC
121+
candidates.sort((a, b) => a.index - b.index);
122+
const { prebuild, workspace } = candidates[0].candidate;
123+
124+
console.log("Found base for incremental build", {
125+
prebuild,
126+
workspace,
127+
exactMatch: false,
128+
});
129+
return prebuild;
120130
}
121131

122132
private isMatchForIncrementalBuild(
@@ -126,13 +136,13 @@ export class IncrementalWorkspaceService {
126136
candidatePrebuild: PrebuiltWorkspace,
127137
candidateWorkspace: Workspace,
128138
includeUnfinishedPrebuilds?: boolean,
129-
): Match {
139+
): { match: Omit<IncrementalWorkspaceMatch, "none">; index: number } | { match: "none"; index?: undefined } {
130140
// make typescript happy, we know that history.commitHistory is defined
131141
if (!history.commitHistory) {
132-
return Match.None;
142+
return { match: "none" };
133143
}
134144
if (!CommitContext.is(candidateWorkspace.context)) {
135-
return Match.None;
145+
return { match: "none" };
136146
}
137147

138148
const acceptableStates: PrebuiltWorkspaceState[] = ["available"];
@@ -141,35 +151,40 @@ export class IncrementalWorkspaceService {
141151
acceptableStates.push("queued");
142152
}
143153
if (!acceptableStates.includes(candidatePrebuild.state)) {
144-
return Match.None;
154+
return { match: "none" };
145155
}
146156

147157
// we are only considering full prebuilds (we are not building on top of incremental prebuilds)
148158
if (candidateWorkspace.basedOnPrebuildId) {
149-
return Match.None;
159+
return { match: "none" };
150160
}
151161

152162
// check if the amount of additional repositories matches the candidate
153163
if (
154164
candidateWorkspace.context.additionalRepositoryCheckoutInfo?.length !==
155165
history.additionalRepositoryCommitHistories?.length
156166
) {
157-
return Match.None;
167+
return { match: "none" };
158168
}
159169

160170
const candidateCtx = candidateWorkspace.context;
161171

162172
// check for overlapping commit history
173+
// TODO(gpl) Isn't "candidateCtx.revision" identical to "candidatePrebuild.commit"? If yes, we could do .indexOf once...
174+
if (candidateCtx.revision !== candidatePrebuild.commit) {
175+
log.warn("Prebuild matching: commits mismatch!", { candidateCtx, candidatePrebuild });
176+
}
163177
if (!history.commitHistory.some((sha) => sha === candidateCtx.revision)) {
164-
return Match.None;
178+
return { match: "none" };
165179
}
180+
166181
// check for overlapping git history for each additional repo
167182
for (const subRepo of candidateWorkspace.context.additionalRepositoryCheckoutInfo ?? []) {
168183
const matchingRepo = history.additionalRepositoryCommitHistories?.find(
169184
(repo) => repo.cloneUrl === subRepo.repository.cloneUrl,
170185
);
171186
if (!matchingRepo || !matchingRepo.commitHistory.some((sha) => sha === subRepo.revision)) {
172-
return Match.None;
187+
return { match: "none" };
173188
}
174189
}
175190

@@ -179,7 +194,7 @@ export class IncrementalWorkspaceService {
179194
imageSource,
180195
parentImageSource: candidateWorkspace.imageSource,
181196
});
182-
return Match.None;
197+
return { match: "none" };
183198
}
184199

185200
// ensure the tasks haven't changed
@@ -190,14 +205,15 @@ export class IncrementalWorkspaceService {
190205
prebuildTasks,
191206
parentPrebuildTasks,
192207
});
193-
return Match.None;
208+
return { match: "none" };
194209
}
195210

196-
if (candidatePrebuild.commit === history.commitHistory[0]) {
197-
return Match.Exact;
211+
const index = history.commitHistory.indexOf(candidatePrebuild.commit);
212+
if (index === 0) {
213+
return { match: "exact", index };
198214
}
199215

200-
return Match.Loose;
216+
return { match: "incremental", index };
201217
}
202218

203219
/**

0 commit comments

Comments
 (0)