Skip to content

Commit c5ddb90

Browse files
authored
Merge pull request #8110 from QwikDev/fix-ssg-windows-2
fix: ssg on windows 2nd attempt
2 parents f263433 + c60f44a commit c5ddb90

File tree

8 files changed

+38
-51
lines changed

8 files changed

+38
-51
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,8 @@ jobs:
684684
# browser: firefox
685685
- host: macos-latest
686686
browser: webkit
687-
# - host: windows-latest
688-
# browser: chromium
687+
- host: windows-latest
688+
browser: chromium
689689

690690
runs-on: ${{ matrix.settings.host }}
691691

e2e/adapters-e2e/playwright.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default defineConfig({
2020
},
2121

2222
// Increase global timeout for service worker tests
23-
timeout: 30000,
23+
timeout: process.env.CI ? 120000 : 30000,
2424

2525
projects: [
2626
{
@@ -44,5 +44,6 @@ export default defineConfig({
4444
port: 3000,
4545
stdout: 'pipe',
4646
reuseExistingServer: !process.env.CI,
47+
timeout: process.env.CI ? 120000 : 30000,
4748
},
4849
});

e2e/qwik-cli-e2e/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,4 @@ export function log(text: string) {
205205
console.log(yellow('E2E: ' + text));
206206
}
207207

208-
export const DEFAULT_TIMEOUT = 30000;
208+
export const DEFAULT_TIMEOUT = process.env.CI ? 120000 : 30000;

e2e/qwik-react-e2e/playwright.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default defineConfig({
2020
},
2121

2222
// Increase global timeout for service worker tests
23-
timeout: 30000,
23+
timeout: process.env.CI ? 120000 : 30000,
2424

2525
projects: [
2626
{
@@ -44,5 +44,6 @@ export default defineConfig({
4444
port: 3000,
4545
stdout: 'pipe',
4646
reuseExistingServer: !process.env.CI,
47+
timeout: process.env.CI ? 120000 : 30000,
4748
},
4849
});

packages/qwik-city/src/static/main-thread.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ export async function mainThread(sys: System) {
216216
flushQueue();
217217
};
218218

219-
loadStaticRoutes();
219+
loadStaticRoutes().catch((e) => {
220+
console.error('SSG route loading failed', e);
221+
reject(e);
222+
});
220223
} catch (e) {
221224
reject(e);
222225
}

packages/qwik-city/src/static/node/node-main.ts

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { Worker } from 'node:worker_threads';
1313
import { isAbsolute, resolve } from 'node:path';
1414
import { ensureDir } from './node-system';
1515
import { normalizePath } from '../../utils/fs';
16-
import { createSingleThreadWorker } from '../worker-thread';
1716

1817
export async function createNodeMainProcess(sys: System, opts: StaticGenerateOptions) {
1918
const ssgWorkers: StaticGeneratorWorker[] = [];
@@ -50,34 +49,14 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
5049
sitemapOutFile = resolve(outDir, sitemapOutFile);
5150
}
5251
}
53-
54-
const singleThreadWorker = await createSingleThreadWorker(sys);
55-
56-
const createWorker = (workerIndex: number) => {
57-
if (workerIndex === 0) {
58-
// same thread worker, don't start a new process
59-
const ssgSameThreadWorker: StaticGeneratorWorker = {
60-
activeTasks: 0,
61-
totalTasks: 0,
62-
63-
render: async (staticRoute) => {
64-
ssgSameThreadWorker.activeTasks++;
65-
ssgSameThreadWorker.totalTasks++;
66-
const result = await singleThreadWorker(staticRoute);
67-
ssgSameThreadWorker.activeTasks--;
68-
return result;
69-
},
70-
71-
terminate: async () => {},
72-
};
73-
return ssgSameThreadWorker;
74-
}
75-
52+
const createWorker = () => {
7653
let terminateResolve: (() => void) | null = null;
7754
const mainTasks = new Map<string, WorkerMainTask>();
7855

7956
let workerFilePath: string | URL;
57+
let terminateTimeout: number | null = null;
8058

59+
// Launch the worker using the package's index module, which bootstraps the worker thread.
8160
if (typeof __filename === 'string') {
8261
workerFilePath = __filename;
8362
} else {
@@ -89,6 +68,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
8968
}
9069

9170
const nodeWorker = new Worker(workerFilePath, { workerData: opts });
71+
nodeWorker.unref();
9272

9373
const ssgWorker: StaticGeneratorWorker = {
9474
activeTasks: 0,
@@ -116,7 +96,9 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
11696
terminateResolve = resolve;
11797
nodeWorker.postMessage(msg);
11898
});
119-
await nodeWorker.terminate();
99+
terminateTimeout = setTimeout(async () => {
100+
await nodeWorker.terminate();
101+
}, 1000) as unknown as number;
120102
},
121103
};
122104

@@ -146,7 +128,11 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
146128
});
147129

148130
nodeWorker.on('exit', (code) => {
149-
if (code !== 1) {
131+
if (terminateTimeout) {
132+
clearTimeout(terminateTimeout);
133+
terminateTimeout = null;
134+
}
135+
if (code !== 0) {
150136
console.error(`worker exit ${code}`);
151137
}
152138
});
@@ -200,9 +186,15 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
200186
console.error(e);
201187
}
202188
}
203-
ssgWorkers.length = 0;
204189

205190
await Promise.all(promises);
191+
ssgWorkers.length = 0;
192+
193+
// On Windows, give extra time for all workers to fully exit
194+
// This prevents resource conflicts in back-to-back builds
195+
if (process.platform === 'win32') {
196+
await new Promise((resolve) => setTimeout(resolve, 300));
197+
}
206198
};
207199

208200
if (sitemapOutFile) {
@@ -214,7 +206,11 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
214206
}
215207

216208
for (let i = 0; i < maxWorkers; i++) {
217-
ssgWorkers.push(createWorker(i));
209+
ssgWorkers.push(createWorker());
210+
// On Windows, add delay between worker creation to avoid resource contention
211+
if (process.platform === 'win32' && i < maxWorkers - 1) {
212+
await new Promise((resolve) => setTimeout(resolve, 100));
213+
}
218214
}
219215

220216
const mainCtx: MainContext = {

packages/qwik-city/src/static/node/node-worker.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ export async function createNodeWorkerProcess(
66
) {
77
parentPort?.on('message', async (msg: WorkerInputMessage) => {
88
parentPort?.postMessage(await onMessage(msg));
9+
if (msg.type === 'close') {
10+
parentPort?.close();
11+
}
912
});
1013
}

packages/qwik-city/src/static/worker-thread.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,6 @@ export async function workerThread(sys: System) {
3939
});
4040
}
4141

42-
export async function createSingleThreadWorker(sys: System) {
43-
const ssgOpts = sys.getOptions();
44-
const pendingPromises = new Set<Promise<any>>();
45-
46-
const opts: StaticGenerateHandlerOptions = {
47-
...ssgOpts,
48-
render: (await import(pathToFileURL(ssgOpts.renderModulePath).href)).default,
49-
qwikCityPlan: (await import(pathToFileURL(ssgOpts.qwikCityPlanModulePath).href)).default,
50-
};
51-
52-
return (staticRoute: StaticRoute) => {
53-
return new Promise<StaticWorkerRenderResult>((resolve) => {
54-
workerRender(sys, opts, staticRoute, pendingPromises, resolve);
55-
});
56-
};
57-
}
58-
5942
async function workerRender(
6043
sys: System,
6144
opts: StaticGenerateHandlerOptions,

0 commit comments

Comments
 (0)