https://gitlab.synchro.net/main/sbbs/-/issues/1129#note_9470
Working with my Claude on this. This issue seems fixed on myside:
# Issue #1129 — Long Pauses When Clicking Around Sync WebV4
## Summary
Two bugs identified and patched. Both are required for a complete fix. Tested on X-Bit BBS (Synchronet v3.22a, Windows 10) with 200+ message areas across FidoNet, DoveNET, fsxNet, tqwNet, and Zer0net.
---
## Bug 1 — `listSubs()` opens MsgBase for every sub unnecessarily (server-side)
**File:** `webv4/lib/forum.js`
### Root Cause
`listSubs()` is called at page render time every time a user clicks a group (e.g. FidoNet) to view its list of sub-boards. It opens a `MsgBase` instance for **every sub in the group**, even though the two fields that actually require an open MsgBase (`unread` and `newest`) are both **commented out**:
```javascript
// unread: is_user() ? getSubUnreadCount(mb) : null,
// newest: getNewestMessageInSub(mb),
```
On a system with 141 FidoNet echos, this means 141 `MsgBase.open()` + `MsgBase.close()` calls on every single click of the FidoNet group link — purely wasted I/O with no effect on the rendered output. This was enough to cause the page render thread to stall for several seconds, which manifests as the browser spinner when navigating back and forth rapidly.
### Fix
Remove the MsgBase open/close entirely from `listSubs()` since neither field requiring it is currently active:
**Before:**
```javascript
function listSubs(group) {
return msg_area.grp_list[group].sub_list.map(function (e) {
const mb = new MsgBase(e.code);
if (!mb.open()) throw new Error(mb.error);
const ret = {
index: e.index,
code: e.code,
grp_index: e.grp_index,
grp_name: e.grp_name,
name: e.name,
description: e.description,
can_post: e.can_post,
is_operator: e.is_operator,
is_moderated: e.is_moderated,
scan_ptr: e.scan_ptr,
scan_cfg: e.scan_cfg,
// unread: is_user() ? getSubUnreadCount(mb) : null,
// newest: getNewestMessageInSub(mb),
};
mb.close();
return ret;
});
}
```
**After:**
```javascript
function listSubs(group) {
return msg_area.grp_list[group].sub_list.map(function (e) {
return {
index: e.index,
code: e.code,
grp_index: e.grp_index,
grp_name: e.grp_name,
name: e.name,
description: e.description,
can_post: e.can_post,
is_operator: e.is_operator,
is_moderated: e.is_moderated,
scan_ptr: e.scan_ptr,
scan_cfg: e.scan_cfg,
// unread: is_user() ? getSubUnreadCount(mb) : null,
// newest: getNewestMessageInSub(mb),
};
});
}
```
### Impact
Page render time for the FidoNet sub list dropped from several seconds to under 150ms in testing. No functional change — the rendered output is identical.
---
## Bug 2 — AJAX requests not cancelled on navigation, SSE not closed on unload (client-side)
**File:** `webv4/root/js/common.js`
### Root Cause
Two related issues in the client-side fetch layer:
**a) No AbortController on `v4_fetch()`**
All AJAX calls via `v4_get()` and `v4_post()` use `fetch()` with no `AbortController`. When the user navigates away mid-request, the browser abandons its interest in the response but the **server-side Synchronet JS thread keeps running** until the full operation completes. On systems with many subs, `getGroupUnreadCounts()` and `getSubUnreadCounts()` can take several seconds, meaning orphaned server threads accumulate with rapid navigation.
**b) EventSource not closed on navigation**
The SSE connection to `events.ssjs` is created on every page load but never explicitly closed before navigation. While the browser eventually closes the TCP connection, the server-side `events.ssjs` loop (`while (client.socket.is_connected)`) continues running through its `mswait(1000)` cycle until the disconnect is detected, holding a server thread open longer than necessary.
### Fix
**Before:**
```javascript
var updateInterval = 60000;
const _sbbs_events = {};
async function v4_fetch(url, method, body) {
const init = { method, headers: {} };
...
try {
const response = await fetch(url, init);
const data = await response.json();
return data;
} catch (err) {
console.error('Error on fetch', url, init);
}
}
```
And at the bottom of `window.onload`:
```javascript
const es = new EventSource(`./api/events.ssjs${qs}`); Object.keys(_sbbs_events).forEach(e => es.addEventListener(e, _sbbs_events[e].callback));
```
**After:**
```javascript
var updateInterval = 60000;
const _sbbs_events = {};
let _v4_abort_controller = null;
async function v4_fetch(url, method, body) {
if (_v4_abort_controller) _v4_abort_controller.abort();
_v4_abort_controller = new AbortController();
const init = { method, headers: {}, signal: _v4_abort_controller.signal };
...
try {
const response = await fetch(url, init);
const data = await response.json();
return data;
} catch (err) {
if (err.name === 'AbortError') return;
console.error('Error on fetch', url, init);
}
}
```
And at the bottom of `window.onload`:
```javascript
const es = new EventSource(`./api/events.ssjs${qs}`); Object.keys(_sbbs_events).forEach(e => es.addEventListener(e, _sbbs_events[e].callback));
window.addEventListener('beforeunload', () => es.close());
```
### Impact
In-flight AJAX requests are now cancelled when a new request is made, preventing orphaned server threads from accumulating. The SSE connection is cleanly closed before navigation, allowing the server-side event loop to exit promptly.
---
## Test Results
Tested on X-Bit BBS (x-bit.org), Synchronet v3.22a, Windows 10, with 200+ sub-boards across multiple FidoNet and other networks.
- **Before:** Rapid Forum → FidoNet → Forum → FidoNet navigation caused the browser to spin indefinitely. Network tab showed requests accumulating as `(pending)` with no resolution.
- **After (Bug 1 fix only):** Page renders in ~146ms. No pending requests. No spinner.
- **After (Bug 2 fix only):** Network tab shows cancelled requests instead of pending ones piling up, confirming orphaned fetches are being cleaned up.
- **Both fixes together:** Clean navigation with no stalls observed across extended testing.
---
## Notes
- Bug 1 is the primary fix. The MsgBase overhead scales with the number of subs in a group, so systems with large FidoNet or other network echo lists are most affected.
- Bug 2 is a correctness improvement that prevents resource accumulation and should be applied regardless.
- If/when `unread` and `newest` are re-enabled in `listSubs()`, the MsgBase open will need to be restored — but at that point the unread counts should come via the SSE event system rather than blocking the page render.
- Note: a 16-second SSE stall observed during testing on a local test BBS was traced to `localhost` IPv4/IPv6 resolution fallback and is **not** a webv4 bug.
* Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)