Performance Guide
Optimization strategies for large-scale testing with tsunagiya.
Best Practices for Large Volume Event Processing
It is recommended to use EventBuilder.bulk() to efficiently generate events. Using timeline() for time-series data is also important. Leveraging limit to narrow filtering can significantly improve performance.
Memory Usage Optimization
Free Memory with pool.reset()
When reusing a MockPool between tests, use reset() to clear the store and received message log.
Watch Snapshot Size
Snapshots create deep copies of the store and received log. With large amounts of data, memory usage can double.
Disable Unnecessary Logging
logging: true accumulates all messages in memory. Disable it for large-scale tests (disabled by default).
Benchmark Results
Reference values from tsunagiya v0.3.0 (environment-dependent).
Event Store Registration
1,000 events → < 5ms
10,000 events → < 30ms
100,000 events → < 300msFilter Matching (filterEvents)
From a store of 10,000 events:
kinds only → < 5ms
kinds + authors → < 8ms
tag filter → < 15ms
limit: 10 → < 3ms (slice after sort)WebSocket Message Send/Receive
1 REQ → 100 EVENT + EOSE → < 10ms (latency: 0)
1 REQ → 1000 EVENT + EOSE → < 50ms (latency: 0)Code Examples
Efficient generation with EventBuilder.bulk():
// ✅ Efficient: bulk generation
const events = EventBuilder.bulk(1000, { kind: 1 });
for (const e of events) relay.store(e);
// ❌ Inefficient: use builder individually
for (let i = 0; i < 1000; i++) {
relay.store(EventBuilder.kind1().content(`event ${i}`).build());
}Time series data generation with timeline():
const events = EventBuilder.timeline(1000, {
kind: 1,
interval: 60,
startTime: 1700000000,
});Efficient filtering with limit:
// ✅ fetch only what you need
ws.send(JSON.stringify(["REQ", "s", { kinds: [1], limit: 50 }]));Free memory with pool.reset():
Deno.test("test suite", async (t) => {
const pool = new MockPool();
const relay = pool.relay("wss://relay.example.com");
pool.install();
try {
await t.step("test 1", () => {
const events = EventBuilder.bulk(10000);
for (const e of events) relay.store(e);
// ...
pool.reset(); // ← release 10000 events
});
await t.step("test 2", () => {
// clean state
});
} finally {
pool.uninstall();
}
});Fetching 1000 events at once:
Deno.test("fetch 1000 events at once", async () => {
const pool = new MockPool();
const relay = pool.relay("wss://relay.example.com");
const events = EventBuilder.bulk(1000, { kind: 1 });
for (const e of events) relay.store(e);
pool.install();
try {
const received: NostrEvent[] = [];
const ws = new WebSocket("wss://relay.example.com");
await new Promise<void>((resolve) => {
ws.onopen = () => ws.send(JSON.stringify(["REQ", "s", { kinds: [1] }]));
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg[0] === "EVENT") received.push(msg[2]);
if (msg[0] === "EOSE") ws.close();
};
ws.onclose = () => resolve();
});
assertEquals(received.length, 1000);
} finally {
pool.uninstall();
}
});Stream delivery pattern:
Deno.test("stream delivery of 1000 events", async () => {
const pool = new MockPool();
const relay = pool.relay("wss://relay.example.com");
pool.install();
try {
const received: NostrEvent[] = [];
const ws = new WebSocket("wss://relay.example.com");
await new Promise<void>((resolve) => {
ws.onopen = () => {
ws.send(JSON.stringify(["REQ", "live", { kinds: [1] }]));
const stream = startStream(relay, {
eventGenerator: () => EventBuilder.random({ kind: 1 }),
interval: 1,
count: 1000,
});
setTimeout(() => {
stream.stop();
ws.close();
}, 2000);
};
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg[0] === "EVENT") received.push(msg[2]);
};
ws.onclose = () => resolve();
});
assertEquals(received.length, 1000);
} finally {
pool.uninstall();
}
});Performance measurement with Deno.bench:
Deno.bench("EventBuilder.bulk(1000)", () => {
EventBuilder.bulk(1000, { kind: 1 });
});
Deno.bench("filterEvents 100 from 10000", () => {
const events = EventBuilder.bulk(10000, { kind: 1 });
filterEvents(events, { kinds: [1], limit: 100 });
});
Deno.bench("matchFilter", () => {
const event = EventBuilder.kind1().build();
matchFilter(event, { kinds: [1], authors: [event.pubkey] });
});Run command:
deno benchManual measurement:
const start = performance.now();
const events = EventBuilder.bulk(10000);
for (const e of events) relay.store(e);
const elapsed = performance.now() - start;
console.log(`${elapsed.toFixed(2)}ms`);Related Documentation
- Best Practices — Test design guidelines
- API Reference — API details
- Test Patterns — Test patterns