Skip to main content
Glama
throughput.test.js7.56 kB
/** * Performance tests for indexing throughput * Measures items/second for mocked indexing operations */ import { describe, it, expect, beforeEach, vi } from 'vitest' import { createEmbeddingMock, BATCH_SIZE, EMBEDDING_DIM } from '../helpers/indexing-mocks.js' import { generateTestEmails, generateTestMessages, generateCalendarEvents, generateSearchTexts } from '../helpers/test-data-generators.js' import { measureTime, calculateThroughput, ThroughputTracker, assertThroughput } from '../helpers/performance-utils.js' describe('Indexing Throughput', () => { let mockEmbedder beforeEach(() => { vi.clearAllMocks() const mock = createEmbeddingMock() mockEmbedder = mock.mockEmbedder }) describe('email processing throughput', () => { it('should process emails at >= 100 items/sec (mocked)', async () => { const count = 100 const emails = generateTestEmails(count) const { duration } = await measureTime(async () => { // Simulate email processing: parse + embed const searchTexts = emails.map(e => e.content.substring(0, 500)) for (let i = 0; i < searchTexts.length; i += BATCH_SIZE) { const batch = searchTexts.slice(i, i + BATCH_SIZE) await mockEmbedder(batch, { pooling: 'mean', normalize: true }) } }) const throughput = calculateThroughput(count, duration) console.log(`Email throughput: ${throughput.toFixed(1)} items/sec`) expect(throughput).toBeGreaterThanOrEqual(100) }) it('should maintain throughput with larger batches', async () => { const count = 320 // 10 batches of 32 const { duration } = await measureTime(async () => { const texts = generateSearchTexts(count) for (let i = 0; i < texts.length; i += BATCH_SIZE) { const batch = texts.slice(i, i + BATCH_SIZE) await mockEmbedder(batch, { pooling: 'mean', normalize: true }) } }) const throughput = calculateThroughput(count, duration) expect(throughput).toBeGreaterThanOrEqual(100) }) }) describe('message processing throughput', () => { it('should process messages at >= 200 items/sec (mocked)', async () => { const count = 200 const messages = generateTestMessages(count) const { duration } = await measureTime(async () => { const searchTexts = messages.map(m => `From: ${m.sender}\nMessage: ${m.text}`.substring(0, 500)) for (let i = 0; i < searchTexts.length; i += BATCH_SIZE) { const batch = searchTexts.slice(i, i + BATCH_SIZE) await mockEmbedder(batch, { pooling: 'mean', normalize: true }) } }) const throughput = calculateThroughput(count, duration) console.log(`Message throughput: ${throughput.toFixed(1)} items/sec`) expect(throughput).toBeGreaterThanOrEqual(200) }) }) describe('calendar processing throughput', () => { it('should process calendar events at >= 150 items/sec (mocked)', async () => { const count = 150 const events = generateCalendarEvents(count) const { duration } = await measureTime(async () => { const searchTexts = events.map(e => `Event: ${e.title}\nCalendar: ${e.calendar}\nLocation: ${e.location}`.substring(0, 500) ) for (let i = 0; i < searchTexts.length; i += BATCH_SIZE) { const batch = searchTexts.slice(i, i + BATCH_SIZE) await mockEmbedder(batch, { pooling: 'mean', normalize: true }) } }) const throughput = calculateThroughput(count, duration) console.log(`Calendar throughput: ${throughput.toFixed(1)} items/sec`) expect(throughput).toBeGreaterThanOrEqual(150) }) }) describe('full indexAll throughput', () => { it('should complete indexAll within 10 seconds for 500 items (mocked)', async () => { const emailCount = 200 const messageCount = 200 const calendarCount = 100 const totalCount = emailCount + messageCount + calendarCount const emails = generateTestEmails(emailCount) const messages = generateTestMessages(messageCount) const events = generateCalendarEvents(calendarCount) const { duration } = await measureTime(async () => { // Simulate indexAll: process all three sources // Emails const emailTexts = emails.map(e => e.content.substring(0, 500)) for (let i = 0; i < emailTexts.length; i += BATCH_SIZE) { await mockEmbedder(emailTexts.slice(i, i + BATCH_SIZE), { pooling: 'mean', normalize: true }) } // Messages const msgTexts = messages.map(m => m.text.substring(0, 500)) for (let i = 0; i < msgTexts.length; i += BATCH_SIZE) { await mockEmbedder(msgTexts.slice(i, i + BATCH_SIZE), { pooling: 'mean', normalize: true }) } // Calendar const eventTexts = events.map(e => e.title) for (let i = 0; i < eventTexts.length; i += BATCH_SIZE) { await mockEmbedder(eventTexts.slice(i, i + BATCH_SIZE), { pooling: 'mean', normalize: true }) } }) console.log(`Full indexAll (${totalCount} items): ${duration.toFixed(0)}ms`) expect(duration).toBeLessThan(10000) // < 10 seconds }) }) }) describe('Throughput Tracker', () => { it('should track batch throughput correctly', async () => { const tracker = new ThroughputTracker() tracker.start() for (let i = 0; i < 5; i++) { await new Promise(r => setTimeout(r, 10)) tracker.recordBatch(32) } const summary = tracker.getSummary() expect(summary.totalItems).toBe(160) expect(summary.batchCount).toBe(5) expect(summary.overallThroughput).toBeGreaterThan(0) }) it('should calculate batch-level statistics', async () => { const tracker = new ThroughputTracker() tracker.start() tracker.recordBatch(32) await new Promise(r => setTimeout(r, 50)) tracker.recordBatch(32) await new Promise(r => setTimeout(r, 50)) tracker.recordBatch(32) const summary = tracker.getSummary() expect(summary.avgBatchThroughput).toBeGreaterThan(0) expect(summary.minBatchThroughput).toBeLessThanOrEqual(summary.avgBatchThroughput) expect(summary.maxBatchThroughput).toBeGreaterThanOrEqual(summary.avgBatchThroughput) }) }) describe('Throughput under load', () => { it('should maintain consistent throughput over multiple batches', async () => { const mockEmbedder = createEmbeddingMock().mockEmbedder const batchThroughputs = [] // Warmup batch - not measured (eliminates V8 JIT cold-start overhead) const warmupTexts = generateSearchTexts(BATCH_SIZE) await mockEmbedder(warmupTexts, { pooling: 'mean', normalize: true }) for (let batch = 0; batch < 10; batch++) { const texts = generateSearchTexts(BATCH_SIZE) const start = performance.now() await mockEmbedder(texts, { pooling: 'mean', normalize: true }) const duration = performance.now() - start batchThroughputs.push(calculateThroughput(BATCH_SIZE, duration)) } const avgThroughput = batchThroughputs.reduce((a, b) => a + b, 0) / batchThroughputs.length const minThroughput = Math.min(...batchThroughputs) console.log(`Average throughput: ${avgThroughput.toFixed(0)} items/sec`) console.log(`Min throughput: ${minThroughput.toFixed(0)} items/sec`) // Min should be at least 50% of average (achievable after warmup) expect(minThroughput).toBeGreaterThan(avgThroughput * 0.5) }) })

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sfls1397/Apple-Tools-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server