Add low-memory client concurrency guard
This commit is contained in:
parent
496518f190
commit
8cc92ca02b
@ -5,6 +5,11 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
|
||||
---
|
||||
|
||||
## [1.20.39] — 2026-08-19
|
||||
|
||||
### Changed
|
||||
- **Low-memory devices now receive conservative batch concurrency.** Shared frontend worker pools cap themselves at two concurrent tasks when the browser reports approximately 4 GB RAM or less, reducing memory spikes during bulk operations.
|
||||
|
||||
## [1.20.38] — 2026-08-19
|
||||
|
||||
### Added
|
||||
|
||||
2
TODO.md
2
TODO.md
@ -37,7 +37,7 @@ Priority levels:
|
||||
- [ ] Update only affected table rows instead of rebuilding entire tables.
|
||||
- [ ] Add an LRU cache for audio and images and remove old entries automatically.
|
||||
- [ ] Revoke discarded object URLs with `URL.revokeObjectURL(objectUrl)`.
|
||||
- [ ] Add low-memory mode using `navigator.deviceMemory` where available.
|
||||
- [x] Add low-memory mode using `navigator.deviceMemory` where available; shared worker pools cap at two tasks on devices reporting approximately 4 GB RAM or less.
|
||||
- [ ] Disable image preloading and reduce table/cache sizes on devices with approximately 4 GB RAM.
|
||||
|
||||
## P1 — Backend stability and bounded concurrency
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#2563EB">
|
||||
<meta name="app-version" content="1.20.38">
|
||||
<meta name="app-version" content="1.20.39">
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/icon.svg">
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.20.38">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.20.39">
|
||||
|
||||
|
||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||
@ -378,7 +378,7 @@ window.toggleNavTree = function(treeId, chevronId) {
|
||||
</script>
|
||||
|
||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||
<script src="/static/loader.js?v=1.20.36"></script>
|
||||
<script src="/static/loader.js?v=1.20.39"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -336,10 +336,20 @@ function debounce(fn, ms) { let t; return (...a) => { clearTimeout(t); t = setTi
|
||||
// Log a non-fatal error instead of silently swallowing it (was `.catch(()=>{})`).
|
||||
// Use in best-effort paths so failures are visible in the console for debugging.
|
||||
function logErr(context, err) { try { console.warn(`[${context}]`, err && (err.message || err)); } catch (_) {} }
|
||||
// Keep batch work conservative on small client devices. Desktop machines
|
||||
// without the Device Memory API keep the normal pool size; browsers that do
|
||||
// expose it get a hard cap before multiple audio/image requests can build up.
|
||||
function ttvcIsLowMemoryDevice() {
|
||||
const memory = Number(globalThis.navigator?.deviceMemory);
|
||||
return Number.isFinite(memory) && memory > 0 && memory <= 4;
|
||||
}
|
||||
window.ttvcIsLowMemoryDevice = ttvcIsLowMemoryDevice;
|
||||
// Run an async worker over items with bounded concurrency (default 4). `onProgress(done,total)`
|
||||
// fires after each item. Replaces slow serial `for…await` loops over network calls.
|
||||
async function runPool(items, worker, concurrency = 4, onProgress) {
|
||||
const arr = [...items]; let i = 0, done = 0; const total = arr.length;
|
||||
const requested = Math.max(1, Number(concurrency) || 1);
|
||||
const effectiveConcurrency = ttvcIsLowMemoryDevice() ? Math.min(2, requested) : requested;
|
||||
async function run() {
|
||||
while (i < arr.length) {
|
||||
const idx = i++;
|
||||
@ -347,7 +357,7 @@ async function runPool(items, worker, concurrency = 4, onProgress) {
|
||||
done++; if (onProgress) { try { onProgress(done, total); } catch (_) {} }
|
||||
}
|
||||
}
|
||||
await Promise.all(Array.from({ length: Math.min(concurrency, arr.length) }, run));
|
||||
await Promise.all(Array.from({ length: Math.min(effectiveConcurrency, arr.length) }, run));
|
||||
}
|
||||
async function copyText(text) {
|
||||
try {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user