mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-11 22:32:26 +00:00
fix(workers): tear down a deleted source's worker after its FK nulls root_dir_id
This commit is contained in:
1 parent
ab7773a5d5
commit
bd7e70edfd
2 files changed
+43
-5
No files matched your search
@@ -719,6 +719,37 @@ describe('WorkerDriver hot reload', () => {
|
||||
expect(path).toContain(user.username);
|
||||
});
|
||||
|
||||
it('tears the worker down when the delete nulls its root_dir_id', async () => {
|
||||
// MySQL and Postgres null `subdomains.root_dir_id` when its fsentry row
|
||||
// is deleted (FK ON DELETE SET NULL). SQLite's schema has no such FK,
|
||||
// so a trigger stands in for it.
|
||||
await server.clients.db.write(
|
||||
'CREATE TRIGGER test_subdomain_root_set_null AFTER DELETE ON fsentries ' +
|
||||
'BEGIN UPDATE subdomains SET root_dir_id = NULL WHERE root_dir_id = OLD.id; END',
|
||||
);
|
||||
try {
|
||||
const { user, actor, path, name } = await deployWorker();
|
||||
fetchSpy.mockClear();
|
||||
const entry = await server.stores.fsEntry.getEntryByPath(path);
|
||||
|
||||
await inCtx(actor, () =>
|
||||
server.services.fs.remove(user.id, { entry: entry! }),
|
||||
);
|
||||
|
||||
await waitFor(
|
||||
() => deleteCalls().length > 0,
|
||||
'edge delete after source delete',
|
||||
);
|
||||
expect(deleteCalls().map((call) => call[0])).toContain(
|
||||
`${SCRIPTS_BASE}/${name}/`,
|
||||
);
|
||||
} finally {
|
||||
await server.clients.db.write(
|
||||
'DROP TRIGGER IF EXISTS test_subdomain_root_set_null',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('tears the worker down when its source file is moved to Trash', async () => {
|
||||
const { user, actor, entry, name } = await deployWorker();
|
||||
fetchSpy.mockClear();
|
||||
|
||||
@@ -1207,7 +1207,9 @@ export class WorkerDriver extends PuterDriver {
|
||||
const entry = this.#extractFsEntryFromEvent(data);
|
||||
if (!entry || entry.isDir) return;
|
||||
|
||||
const matched = await this.#listWorkerRowsForEntry(entry);
|
||||
const matched = await this.#listWorkerRowsForEntry(entry, {
|
||||
deleted: true,
|
||||
});
|
||||
for (const row of matched) {
|
||||
await this.#deleteWorkerForSourceRow(row, entry.userId);
|
||||
}
|
||||
@@ -1259,9 +1261,13 @@ export class WorkerDriver extends PuterDriver {
|
||||
* The entry already names the worker rows pointing at it, so a file no
|
||||
* worker is bound to (nearly every write) costs no query. The rows
|
||||
* themselves come from the primary: one deleted by cascade can outlive its
|
||||
* by-name cache entry.
|
||||
* by-name cache entry. Once the entry is deleted, its FK has already set
|
||||
* those rows' `root_dir_id` to NULL.
|
||||
*/
|
||||
async #listWorkerRowsForEntry(entry: FSEntry): Promise<SubdomainRow[]> {
|
||||
async #listWorkerRowsForEntry(
|
||||
entry: FSEntry,
|
||||
{ deleted = false }: { deleted?: boolean } = {},
|
||||
): Promise<SubdomainRow[]> {
|
||||
const names = (entry.workers ?? []).map((worker) => worker.subdomain);
|
||||
if (names.length === 0) return [];
|
||||
const rowsByName = await this.stores.subdomain.getBySubdomains(names, {
|
||||
@@ -1269,8 +1275,9 @@ export class WorkerDriver extends PuterDriver {
|
||||
});
|
||||
return [...rowsByName.values()].filter(
|
||||
(row) =>
|
||||
Number(row.root_dir_id) === entry.id &&
|
||||
Number(row.user_id) === entry.userId,
|
||||
Number(row.user_id) === entry.userId &&
|
||||
(Number(row.root_dir_id) === entry.id ||
|
||||
(deleted && row.root_dir_id == null)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user