# Background scripts or service workers terminate when not in use

- **Pattern:** `ab-001372` (`extension-ux-performance.bundle-memory.background-terminates`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001372
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001372)

## Why it matters

MV3 service workers that never terminate because of `setInterval` or persistent WebSocket connections consume CPU and memory continuously — for every browser session, on every user's machine. ISO 25010:2011 performance-efficiency.resource-utilisation treats unkillable background processes as a resource defect. Chrome aggressively terminates service workers that misbehave, but a `setInterval` pattern keeps the worker alive indefinitely, preventing Chrome's idle eviction from ever firing. This drains battery on laptops and degrades overall system performance.

## Severity rationale

High because a background script that never terminates imposes a permanent CPU and memory tax on every user's browser, measurably degrading battery life.

## Remediation

Replace `setInterval` with `chrome.alarms` so the service worker performs its work and exits, relying on Chrome to wake it when the next alarm fires.

```js
// BAD: prevents service worker termination
setInterval(() => checkForUpdates(), 60_000);

// GOOD: service worker wakes on alarm, runs, then idles naturally
chrome.alarms.create('updateCheck', { periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'updateCheck') checkForUpdates();
});
```

For MV2 extensions, set `"persistent": false` in `manifest.json` unless the extension genuinely requires a persistent WebSocket or audio context — document the reason in a comment if `true` is unavoidable.

## Detection

- **ID:** `background-terminates`
- **Severity:** `high`
- **What to look for:** For Manifest V3 extensions, examine the service worker file for patterns that prevent termination. Look for `setInterval` calls, `setTimeout` chains (re-scheduling before expiry), `chrome.alarms` misuse, or open `WebSocket` connections kept alive indefinitely. Check whether the service worker holds resources that block idle termination. For Manifest V2 extensions, check whether a persistent background page is used when an event page would suffice (`"persistent": false` in manifest). Before evaluating, extract and quote the service worker or background script lifecycle handlers to verify proper termination patterns. Count all instances found and enumerate each.
- **Pass criteria:** MV3 service worker does not use `setInterval` or persistent `setTimeout` chains. Recurring tasks use `chrome.alarms` instead. The service worker completes its work and allows itself to terminate. MV2 extensions use event pages (`"persistent": false`) rather than persistent background pages unless there is a documented reason. At least 1 implementation must be confirmed.
- **Fail criteria:** MV3 service worker uses `setInterval` (which prevents idle termination). Long-running `setTimeout` chains re-register before completion. Open WebSocket connections or long-polling patterns prevent the service worker from going idle. MV2 extension uses `"persistent": true` with no documented justification.
- **Skip (N/A) when:** The extension has no background script (purely content-script or popup-only extension).
- **Cross-reference:** The `memory-under-50mb` check verifies that memory usage stays bounded even when the background script is actively running.
- **Detail on fail:** Describe the termination blocker. Example: `"Service worker uses setInterval(checkForUpdates, 60000) — prevents idle termination, keeps background alive indefinitely"` or `"MV2 manifest uses 'persistent': true with no feature that requires a persistent background page."`
- **Remediation:** Replace `setInterval` with `chrome.alarms` for recurring tasks:

  ```js
  // BAD: prevents service worker termination
  setInterval(() => checkForUpdates(), 60_000);

  // GOOD: use chrome.alarms (service worker wakes on alarm, then idles)
  chrome.alarms.create('updateCheck', { periodInMinutes: 1 });
  chrome.alarms.onAlarm.addListener((alarm) => {
    if (alarm.name === 'updateCheck') checkForUpdates();
  });
  ```

  For MV2, set `"persistent": false` unless you genuinely need a persistent WebSocket or audio context.

## External references

- iso-25010 performance-efficiency.resource-utilization

Taxons: cost-efficiency, performance

HTML version: https://auditbuffet.com/patterns/ab-001372
