Files
qbit-collaboration/docs/performance/browser_presence_load.mjs
James MaesandClaude Sonnet 5.5 63245b14e0
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow d20a578c7d818bc7ca32a2ac2c675da4e4f27385] (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 4s
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 5s
maven / build (push) Failing after 42s
build / maven (push) Failing after 42s
feat(collaboration): integrate presence, Redis presence, virtual-field and fixture work on the attachment base
One reviewable integration on current develop 7d61f29e. It starts from
feat/record-files-standalone (attachment lifecycle, upload journal, retention
SPI; descends from develop) and adds the owner stack content from
feature/collab, feature/collab-int-20261029, feature/field-values-1196,
feature/presence-recovery-1300, test/presence-capacity-1302 and the
fix/attachment-transactions-1289 / fix/mywork-query-bounds-1291 work, plus
KOF22 adaptations: CollaborationQBitConfig.attachmentLifecycle,
CollaborationRecords.insertActivity/query(transaction), ActivityWriter source
feature routing, and the Redis/fixture tests.

Build: develop CI files are preserved. pom.xml only adds the Redis API
guardrail allow-list and an optional qbit-redis dependency; the core and
qbit-redis versions stay on the canonical SNAPSHOT lines with markers to repin
after the core and redis integration PRs are published (order CORE -> REDIS ->
COLLAB). No PRIVATE or feature-snapshot coordinate is introduced. Not yet
compiled: it needs the core integration artifact (virtual-field providers).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 17:19:49 -06:00

76 lines
3.0 KiB
JavaScript

// Bounded real HTTP load using caller-owned authenticated browser contexts.
import { performance } from 'node:perf_hooks';
import { setTimeout as delay } from 'node:timers/promises';
export async function startPresenceLoad(browser, base, expect) {
const state = { workers: 16, completed: 0, errors: [], nodes: {}, elapsed_ms: 0 };
const contexts = [];
const leases = [];
let running = true;
let stopped = false;
let tasks = [];
const began = performance.now();
const endpoint = `${base}/qqq/v1/qbit/collaboration/presence/leases`;
async function post(context, path, data) {
const response = await context.request.post(endpoint + path, { data, timeout: 10000 });
try {
expect(response.status()).toBe(200);
const node = response.headers()['x-acceptance-node'];
state.nodes[node] = (state.nodes[node] ?? 0) + 1;
return await response.json();
} finally { await response.dispose(); }
}
async function stop() {
if (stopped) return;
stopped = true;
running = false;
await Promise.all(tasks);
state.elapsed_ms = performance.now() - began;
for (const lease of leases) {
try {
const response = await lease.context.request.delete(`${base}/qqq/v1/qbit/collaboration/presence`, {
data: { ...lease.target, connectionId: lease.id, sequence: ++lease.sequence }, timeout: 10000,
});
try { expect(response.status()).toBe(200); } finally { await response.dispose(); }
} catch (error) { state.errors.push(String(error)); }
}
for (const context of contexts) await context.close();
}
try {
for (const [index, user] of ['bob', 'carol', 'dave', 'erin'].entries()) {
const context = await browser.newContext();
contexts.push(context);
await context.addCookies([
{ name: 'collab_dev_user', value: user, url: base },
{ name: 'presence_node', value: index % 2 ? 'second' : 'first', url: base },
]);
const page = await context.newPage();
await page.goto(`${base}/app/person/1?createMockSession=true`);
await expect(page.locator('[data-qqq-id="record-view-title-person"]')).toBeVisible();
await page.close();
for (let tab = 0; tab < 4; tab++) {
const target = { table: 'person', pk: '1' };
const opened = await post(context, '', target);
expect(typeof opened.connectionId).toBe('string');
leases.push({ context, target, id: opened.connectionId, sequence: 0, completed: 0 });
}
}
tasks = leases.map(async lease => {
try {
while (running) {
await post(lease.context, '/renew', { ...lease.target, connectionId: lease.id,
sequence: ++lease.sequence, mode: 'VIEWING' });
lease.completed++;
state.completed++;
await delay(100);
}
} catch (error) { state.errors.push(String(error)); running = false; }
});
await expect.poll(() => leases.filter(lease => lease.completed > 0).length).toBe(16);
return { state, stop };
} catch (error) {
await stop();
throw error;
}
}