| 97 | } |
| 98 | |
| 99 | async function emit(): Promise<Session[]> { |
| 100 | scenario(`emit ${SESSIONS} sessions × ${EVENTS_PER_SESSION} events (concurrency ${CONCURRENCY})`); |
| 101 | const sessions: Session[] = []; |
| 102 | let done = 0; |
| 103 | let errors = 0; |
| 104 | const started = Date.now(); |
| 105 | |
| 106 | await runPool( |
| 107 | Array.from({ length: SESSIONS }, (_, i) => i), |
| 108 | CONCURRENCY, |
| 109 | async (i) => { |
| 110 | const ip = ipForSession(i); |
| 111 | try { |
| 112 | const first = await sendSessionEvent(ip, i, 0); // entry is a screen_view |
| 113 | for (let e = 1; e < EVENTS_PER_SESSION; e++) { |
| 114 | await sendSessionEvent(ip, i, e); |
| 115 | } |
| 116 | sessions.push({ sessionId: first.sessionId, deviceId: first.deviceId }); |
| 117 | } catch (error) { |
| 118 | errors++; |
| 119 | if (errors <= 3) console.warn(` session ${i} failed: ${(error as Error).message}`); |
| 120 | } |
| 121 | done++; |
| 122 | if (done % 100 === 0 || done === SESSIONS) { |
| 123 | console.log(` …emitted ${done}/${SESSIONS}`); |
| 124 | } |
| 125 | } |
| 126 | ); |
| 127 | |
| 128 | const uniq = new Set(sessions.map((s) => s.sessionId)).size; |
| 129 | check('all sessions sent without HTTP errors', errors === 0, `${errors} errors`); |
| 130 | check( |
| 131 | 'each session got a distinct session id', |
| 132 | uniq === SESSIONS, |
| 133 | `${uniq} unique / ${sessions.length} ok / ${SESSIONS} sent` |
| 134 | ); |
| 135 | console.log(` emit took ${((Date.now() - started) / 1000).toFixed(1)}s`); |
| 136 | return sessions; |
| 137 | } |
| 138 | |
| 139 | async function settle(sessions: Session[]) { |
| 140 | scenario('settle: all sessions opened in ClickHouse'); |