fix(worker): ensure reconciled boxes before acquire
This commit is contained in:
@@ -13,6 +13,7 @@ export type BoxHandle = { boxName: string; release: () => void };
|
|||||||
|
|
||||||
type Box = {
|
type Box = {
|
||||||
name: string;
|
name: string;
|
||||||
|
initialized: boolean;
|
||||||
refs: Set<symbol>;
|
refs: Set<symbol>;
|
||||||
idleTimer?: ReturnType<typeof setTimeout>;
|
idleTimer?: ReturnType<typeof setTimeout>;
|
||||||
};
|
};
|
||||||
@@ -71,21 +72,26 @@ export const acquireUserBox = (args: {
|
|||||||
}): Promise<BoxHandle> =>
|
}): Promise<BoxHandle> =>
|
||||||
withLock(args.username, async () => {
|
withLock(args.username, async () => {
|
||||||
let box = boxes.get(args.username);
|
let box = boxes.get(args.username);
|
||||||
let needsEnsure = false;
|
|
||||||
if (box?.idleTimer) {
|
if (box?.idleTimer) {
|
||||||
clearTimeout(box.idleTimer);
|
clearTimeout(box.idleTimer);
|
||||||
box.idleTimer = undefined;
|
box.idleTimer = undefined;
|
||||||
}
|
}
|
||||||
if (!box) {
|
if (!box) {
|
||||||
box = { name: userContainerName(args.username), refs: new Set() };
|
box = {
|
||||||
|
name: userContainerName(args.username),
|
||||||
|
initialized: false,
|
||||||
|
refs: new Set(),
|
||||||
|
};
|
||||||
boxes.set(args.username, box);
|
boxes.set(args.username, box);
|
||||||
needsEnsure = true;
|
|
||||||
}
|
}
|
||||||
// Register the ref before the slow Docker call so a failed acquire can
|
// Register the ref before the slow Docker call so a failed acquire can
|
||||||
// release it without leaking registry state.
|
// release it without leaking registry state.
|
||||||
const handle = makeHandle(args.username, box);
|
const handle = makeHandle(args.username, box);
|
||||||
try {
|
try {
|
||||||
if (needsEnsure) box.name = await ensureUserContainer(args);
|
if (!box.initialized) {
|
||||||
|
box.name = await ensureUserContainer(args);
|
||||||
|
box.initialized = true;
|
||||||
|
}
|
||||||
return handle;
|
return handle;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handle.release();
|
handle.release();
|
||||||
@@ -103,7 +109,7 @@ export const reconcileExistingBoxes = async (): Promise<void> => {
|
|||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const username = name.replace(/^spoon-box-/, '');
|
const username = name.replace(/^spoon-box-/, '');
|
||||||
if (boxes.has(username)) continue;
|
if (boxes.has(username)) continue;
|
||||||
const box: Box = { name, refs: new Set() };
|
const box: Box = { name, initialized: false, refs: new Set() };
|
||||||
boxes.set(username, box);
|
boxes.set(username, box);
|
||||||
scheduleReapIfIdle(username);
|
scheduleReapIfIdle(username);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,30 @@ describe('box registry', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ensures an adopted box once before returning acquire handles', async () => {
|
||||||
|
const module = await load();
|
||||||
|
const docker = await import('../../src/runtime/docker');
|
||||||
|
vi.mocked(docker.listWorkspaceContainerNames).mockResolvedValueOnce([
|
||||||
|
'spoon-box-casey',
|
||||||
|
]);
|
||||||
|
await module.reconcileExistingBoxes();
|
||||||
|
|
||||||
|
const first = await module.acquireUserBox({
|
||||||
|
username: 'casey',
|
||||||
|
workdir: '/w',
|
||||||
|
containerHome: '/home/casey',
|
||||||
|
});
|
||||||
|
const second = await module.acquireUserBox({
|
||||||
|
username: 'casey',
|
||||||
|
workdir: '/w',
|
||||||
|
containerHome: '/home/casey',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(docker.ensureUserContainer).toHaveBeenCalledTimes(1);
|
||||||
|
expect(first.boxName).toBe('spoon-box-casey');
|
||||||
|
expect(second.boxName).toBe('spoon-box-casey');
|
||||||
|
});
|
||||||
|
|
||||||
test('retries ensure after the first acquire fails', async () => {
|
test('retries ensure after the first acquire fails', async () => {
|
||||||
const module = await load();
|
const module = await load();
|
||||||
const docker = await import('../../src/runtime/docker');
|
const docker = await import('../../src/runtime/docker');
|
||||||
|
|||||||
Reference in New Issue
Block a user