From cc97f63b7e3b0c380b4d1f87b3384f9c8675e3fa Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 25 Jun 2024 10:04:11 +0200 Subject: [PATCH 1/2] Implement auto-shutdown for workers after inactivity --- README.md | 7 ++++++- src/plugins/unplugin.ts | 14 ++++++++++++++ test/e2e.spec.ts | 14 +++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63f8083c..03523298 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ SSR-safe, zero-config Web Workers integration for [Nuxt](https://nuxt.com). - 🔥 SSR-safe usage of Web Workers - ✨ auto-imported, zero-configuration - 💪 fully typed +- 🕒 Automatic shutdown of workers after 1 second of inactivity ## Quick Setup @@ -58,6 +59,10 @@ const message = await hi() > [!TIP] > Even if your function is synchronous, it will always become async when it is called within a worker because communication with a worker will always be asynchronous. +### Worker Auto-Shutdown + +The module automatically shuts down workers after 1 second of inactivity to free up resources. This behavior ensures that your application remains efficient and responsive, without unnecessary resource consumption by idle workers. + ## Roadmap - [x] 📖 basic documentation @@ -65,7 +70,7 @@ const message = await hi() - [ ] 📦 webpack support - [ ] ⚠️ type-level + dev warning if non serialisable args are passed - [ ] 🤝 automatic shared workers with `.shared.ts` suffix -- [ ] 💤 worker auto-shutdown +- [x] 💤 worker auto-shutdown ## 💻 Development diff --git a/src/plugins/unplugin.ts b/src/plugins/unplugin.ts index b7b99bbe..59316de4 100644 --- a/src/plugins/unplugin.ts +++ b/src/plugins/unplugin.ts @@ -85,6 +85,7 @@ export const WorkerPlugin = (opts: WorkerPluginOptions) => createUnplugin(() => source += ` const counts = {} const map = {} +const workerTimeouts = {} function initWorker (worker, name) { map[name] = {} @@ -96,7 +97,20 @@ function initWorker (worker, name) { } else { resolve(e.data.result) } + // Reset the inactivity timer upon receiving a message + clearTimeout(workerTimeouts[name]); + workerTimeouts[name] = setTimeout(() => { + worker.terminate(); + delete workerTimeouts[name]; + console.log(\`Worker \${name} terminated due to inactivity.\`); + }, 1000); // 1 second of inactivity } + // Initialize the inactivity timer + workerTimeouts[name] = setTimeout(() => { + worker.terminate(); + delete workerTimeouts[name]; + console.log(\`Worker \${name} terminated due to inactivity.\`); + }, 1000); // 1 second of inactivity return worker }` } diff --git a/test/e2e.spec.ts b/test/e2e.spec.ts index e957e21e..d8ee3e52 100644 --- a/test/e2e.spec.ts +++ b/test/e2e.spec.ts @@ -1,5 +1,5 @@ import { fileURLToPath } from 'node:url' -import { describe, it, expect } from 'vitest' +import { describe, it, expect, vi } from 'vitest' import { setup, $fetch, createPage, url } from '@nuxt/test-utils/e2e' await setup({ @@ -26,4 +26,16 @@ describe('nuxt-workers', () => { expect(await page.getByText('Client-side message: Hello from worker!').textContent()).toBeDefined() await page.close() }) + + it('should automatically shut down the worker after 1 second of inactivity', async () => { + const page = await createPage() + await page.goto(url('/')) + await page.getByText('Load client side message').click() + await new Promise(resolve => setTimeout(resolve, 1500)) // wait for more than 1 second to ensure worker shutdown + const logs = await page.evaluate(() => console.log.mock.calls) + expect(logs).toContainEqual(expect.stringContaining('Worker terminated due to inactivity.')) + await page.close() + }) }) + +vi.spyOn(console, 'log').mockImplementation(() => {}) From ca5e271449ca3503be1b2b0929eb54338ac47c34 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 25 Jun 2024 10:42:50 +0200 Subject: [PATCH 2/2] test: update test for playwright compat --- test/e2e.spec.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/e2e.spec.ts b/test/e2e.spec.ts index d8ee3e52..0b58bb6f 100644 --- a/test/e2e.spec.ts +++ b/test/e2e.spec.ts @@ -1,5 +1,5 @@ import { fileURLToPath } from 'node:url' -import { describe, it, expect, vi } from 'vitest' +import { describe, it, expect } from 'vitest' import { setup, $fetch, createPage, url } from '@nuxt/test-utils/e2e' await setup({ @@ -29,13 +29,15 @@ describe('nuxt-workers', () => { it('should automatically shut down the worker after 1 second of inactivity', async () => { const page = await createPage() + const logs: string[] = [] + page.on('console', (l) => { + logs.push(l.text()) + }) await page.goto(url('/')) await page.getByText('Load client side message').click() - await new Promise(resolve => setTimeout(resolve, 1500)) // wait for more than 1 second to ensure worker shutdown - const logs = await page.evaluate(() => console.log.mock.calls) - expect(logs).toContainEqual(expect.stringContaining('Worker terminated due to inactivity.')) + // wait for more than 1 second to ensure worker shutdown + await new Promise(resolve => setTimeout(resolve, 1500)) + expect(logs).toContainEqual(expect.stringContaining('Worker hi terminated due to inactivity.')) await page.close() }) }) - -vi.spyOn(console, 'log').mockImplementation(() => {})