How Camofox-Browser Tab Recycling Manages Per-Session Limits
When a session reaches its tab limit, camofox-browser automatically recycles the least-used tab by closing the page, releasing locks, and updating metrics to accommodate new requests instead of rejecting them.
Camofox-browser enforces configurable per-session and global tab limits to prevent resource exhaustion. The camofox-browser tab recycling mechanism activates automatically when these thresholds are reached, ensuring continuous operation by reclaiming the oldest resources first. This approach maintains system stability without rejecting valid user requests during high-load scenarios.
Configuring Tab Limits in lib/config.js
The recycling thresholds are defined through environment variables and exposed as constants in server.js.
Environment Variable Defaults
In lib/config.js, the system reads MAX_TABS_PER_SESSION and MAX_TABS_GLOBAL from the environment:
// lib/config.js
maxTabsPerSession: parseInt(process.env.MAX_TABS_PER_SESSION) || 10,
maxTabsGlobal: parseInt(process.env.MAX_TABS_GLOBAL) || 50,
These values are then imported in server.js:
// server.js
const MAX_TABS_PER_SESSION = CONFIG.maxTabsPerSession; // → 10 by default
const MAX_TABS_GLOBAL = CONFIG.maxTabsGlobal; // → 50 by default
Trigger Points for Tab Recycling
Two primary request handlers invoke the recycling logic when limits are exceeded.
Tab Creation (POST /tabs)
When creating a new tab via POST /tabs, the handler checks limits before allocation. If totalTabs >= MAX_TABS_PER_SESSION or the global count exceeds MAX_TABS_GLOBAL, the system calls recycleOldestTab (implemented in server.js lines 887-894).
Navigation Requests (POST /tabs/:tabId/navigate)
For navigation to non-existent tabs, the handler at server.js lines 42-48 checks if the session is already at its limit. If so, it recycles the oldest tab before creating the new navigation context.
Both handlers use the same invocation pattern:
const recycled = await recycleOldestTab(session, req.reqId);
If recycling fails, the request aborts with a 429 or 500 error.
The recycleOldestTab Selection Algorithm
The recycleOldestTab function in server.js (lines 874-899) implements a least-recently-used strategy based on toolCalls counters.
Selection Criteria
The function iterates through session.tabGroups to find the tab with the fewest toolCalls:
async function recycleOldestTab(session, reqId) {
let oldestTab = null;
let oldestGroup = null;
let oldestGroupKey = null;
let oldestTabId = null;
// Scan every group (sessionKey) and every tab inside it
for (const [gKey, group] of session.tabGroups) {
for (const [tid, ts] of group) {
// `toolCalls` counts how many times the tab has been used
if (!oldestTab || ts.toolCalls < oldestTab.toolCalls) {
oldestTab = ts;
oldestGroup = group;
oldestGroupKey = gKey;
oldestTabId = tid;
}
}
}
// ... cleanup logic
}
Recycling Steps
Once identified, the function performs four critical operations:
- Close the page using
safePageClose(oldestTab.page) - Remove the tab from its group via
oldestGroup.delete(oldestTabId) - Clean up locks by draining any pending locks in
tabLocks - Update metrics by incrementing
tabsRecycledTotaland logging the event
The function returns the reclaimed tabId and group key, allowing the caller to proceed with fresh resource allocation.
Monitoring Recycling Events
Recycling activity is tracked in lib/metrics.js (lines 78-81) using a Prometheus counter:
// lib/metrics.js
tabsRecycledTotal: new client.Counter({
name: 'camofox_tabs_recycled_total',
help: 'Tabs recycled when tab limit reached',
}),
Operators can monitor how often the server discards tabs to stay within limits by scraping the metrics endpoint.
Test Coverage
The unit test suite in tests/unit/tabRecycling.test.js validates the recycling behavior:
- Verifies that creating tabs beyond the limit triggers recycling of the earliest tab
- Confirms that the tab with the fewest
toolCallsis selected when multiple tabs exist - Tests automatic recycling during navigation to non-existent tabs
These tests ensure the recycling logic functions correctly under various load conditions.
Practical Code Examples
Example 1: Exceeding the Per-Session Limit
The following client code demonstrates recycling when creating more than the default five tabs (assuming a reduced limit for demonstration):
const client = createClient('http://localhost:9377');
// Fill the session to its limit
const tabIds = [];
for (let i = 0; i < 5; i++) {
const { tabId } = await client.createTab(`https://example.com/page${i}`);
tabIds.push(tabId);
}
// The 6th tab triggers recycling of the oldest tab (tabIds[0])
const { tabId: newId } = await client.createTab('https://example.com/extra');
console.log('New tab created:', newId);
// The recycled tab is no longer reachable
try {
await client.getSnapshot(tabIds[0]);
} catch (e) {
console.log('Oldest tab was recycled (404)', e.status); // → 404
}
Example 2: Automatic Recycling During Navigation
When navigating to a non-existent tab while at the session limit, the server automatically recycles:
// Assume the session is already at its limit (5 tabs)
const fakeId = 'does-not-exist';
// Navigate – server will recycle the least-used tab and create a fresh one
const result = await client.navigate(fakeId, 'https://example.com/new-page');
console.log('Navigate succeeded, new tab URL:', result.url);
Example 3: Scraping Recycling Metrics
With PROMETHEUS_ENABLED=1, monitor recycling frequency:
curl http://localhost:9377/metrics | grep camofox_tabs_recycled_total
# Example output:
# camofox_tabs_recycled_total 3
Summary
- Camofox-browser tab recycling prevents request rejection by reclaiming the least-used tab when
MAX_TABS_PER_SESSION(default 10) orMAX_TABS_GLOBAL(default 50) is reached. - The
recycleOldestTabfunction inserver.jsselects targets based on the lowesttoolCallscount, ensuring the most active tabs remain available. - Recycling triggers during both tab creation (
POST /tabs) and navigation to missing tabs (POST /tabs/:tabId/navigate). - Each recycling event closes the page, removes the tab from
session.tabGroups, drains associated locks, and increments thecamofox_tabs_recycled_totalPrometheus metric. - The behavior is verified by the test suite in
tests/unit/tabRecycling.test.js.
Frequently Asked Questions
How does camofox-browser determine which tab to recycle?
The system selects the tab with the fewest toolCalls within the session's tabGroups map. This metric tracks how many times the tab has been used for navigation, snapshots, or other operations, ensuring the least-active tab is reclaimed first.
What happens if tab recycling fails?
If recycleOldestTab returns null (indicating no tab could be identified or reclaimed), the requesting handler aborts the operation. The client receives either a 429 Too Many Requests or 500 Internal Server Error response, depending on the failure context.
Can I disable tab recycling?
No, tab recycling is integral to the resource management strategy in camofox-browser. However, you can effectively prevent it by setting MAX_TABS_PER_SESSION and MAX_TABS_GLOBAL to sufficiently high values in lib/config.js via environment variables, though this risks memory exhaustion under heavy load.
Where are the recycling limits configured?
The limits are defined in lib/config.js using the MAX_TABS_PER_SESSION and MAX_TABS_GLOBAL environment variables, then consumed in server.js. The default values are 10 tabs per session and 50 globally, but operators can adjust these based on available system resources.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →