Skip to main content
Glama

documcp

by tosin2013
memory-stress-testing.test.ts20.3 kB
/** * Memory System Stress Testing * Tests memory system under extreme conditions and edge cases * Part of Issue #57 - Memory System Performance and Load Testing */ import { promises as fs } from "fs"; import path from "path"; import os from "os"; import { performance } from "perf_hooks"; import { MemoryManager } from "../../src/memory/manager.js"; import { JSONLStorage } from "../../src/memory/storage.js"; describe("Memory System Stress Testing", () => { let tempDir: string; let memoryManager: MemoryManager; beforeEach(async () => { tempDir = path.join( os.tmpdir(), `memory-stress-test-${Date.now()}-${Math.random() .toString(36) .substr(2, 9)}`, ); await fs.mkdir(tempDir, { recursive: true }); memoryManager = new MemoryManager(tempDir); await memoryManager.initialize(); }); afterEach(async () => { try { await fs.rm(tempDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); describe("High Volume Stress Tests", () => { test("should handle extremely large datasets", async () => { memoryManager.setContext({ projectId: "extreme-volume-test" }); const largeDatasetSize = 10000; // 10K memories const batchSize = 1000; const startTime = performance.now(); console.log( `Starting extreme volume test with ${largeDatasetSize} memories...`, ); let processedCount = 0; for (let batch = 0; batch < largeDatasetSize / batchSize; batch++) { const batchData = Array.from({ length: batchSize }, (_, i) => ({ projectId: "extreme-volume-test", batch, index: i, globalIndex: processedCount + i, data: `stress-test-data-${processedCount + i}`, timestamp: new Date().toISOString(), metadata: { batch, processingOrder: processedCount + i, complexity: i % 5, }, })); // Process batch const batchPromises = batchData.map((data) => memoryManager.remember("analysis", data), ); await Promise.all(batchPromises); processedCount += batchSize; // Progress update if (batch % 2 === 0) { const elapsed = performance.now() - startTime; const rate = processedCount / (elapsed / 1000); console.log( `Processed ${processedCount}/${largeDatasetSize} memories (${rate.toFixed( 0, )} memories/sec)`, ); } // Verify memory usage doesn't spiral out of control const memUsage = process.memoryUsage(); expect(memUsage.heapUsed).toBeLessThan(500 * 1024 * 1024); // Less than 500MB } const endTime = performance.now(); const totalTime = endTime - startTime; const averageRate = largeDatasetSize / (totalTime / 1000); console.log( `Completed ${largeDatasetSize} memories in ${(totalTime / 1000).toFixed( 2, )}s (${averageRate.toFixed(0)} memories/sec)`, ); // Verify all memories were stored const allMemories = await memoryManager.search({ projectId: "extreme-volume-test", }); expect(allMemories.length).toBe(largeDatasetSize); // Performance expectations expect(totalTime).toBeLessThan(300000); // Should complete within 5 minutes expect(averageRate).toBeGreaterThan(30); // At least 30 memories per second }, 360000); // 6 minute timeout test("should handle rapid burst operations", async () => { memoryManager.setContext({ projectId: "burst-test" }); const burstSize = 1000; const burstCount = 5; const burstResults: number[] = []; console.log( `Testing ${burstCount} bursts of ${burstSize} operations each...`, ); for (let burst = 0; burst < burstCount; burst++) { const burstStartTime = performance.now(); // Create burst data const burstData = Array.from({ length: burstSize }, (_, i) => ({ projectId: "burst-test", burst, index: i, data: `burst-${burst}-item-${i}`, timestamp: new Date().toISOString(), })); // Execute burst const burstPromises = burstData.map((data) => memoryManager.remember("analysis", data), ); await Promise.all(burstPromises); const burstEndTime = performance.now(); const burstTime = burstEndTime - burstStartTime; burstResults.push(burstTime); console.log( `Burst ${burst + 1}: ${burstTime.toFixed(2)}ms (${( burstSize / (burstTime / 1000) ).toFixed(0)} ops/sec)`, ); // Small delay between bursts await new Promise((resolve) => setTimeout(resolve, 100)); } // Analyze burst performance consistency const avgBurstTime = burstResults.reduce((sum, time) => sum + time, 0) / burstResults.length; const maxBurstTime = Math.max(...burstResults); const minBurstTime = Math.min(...burstResults); const performanceVariation = (maxBurstTime - minBurstTime) / avgBurstTime; console.log( `Burst analysis: Avg ${avgBurstTime.toFixed( 2, )}ms, Min ${minBurstTime.toFixed(2)}ms, Max ${maxBurstTime.toFixed( 2, )}ms`, ); console.log( `Performance variation: ${(performanceVariation * 100).toFixed(1)}%`, ); // Performance should be consistent across bursts expect(avgBurstTime).toBeLessThan(10000); // Average burst < 10 seconds expect(performanceVariation).toBeLessThan(3); // Less than 300% variation }); }); describe("Resource Exhaustion Tests", () => { test("should handle memory pressure gracefully", async () => { memoryManager.setContext({ projectId: "memory-pressure-test" }); const largeItemSize = 1024 * 1024; // 1MB per item const maxItems = 100; // 100MB of data const memorySnapshots: Array<{ count: number; heapUsed: number; time: number; }> = []; console.log("Testing memory pressure handling..."); const startMemory = process.memoryUsage(); const startTime = performance.now(); for (let i = 0; i < maxItems; i++) { const largeData = { projectId: "memory-pressure-test", index: i, payload: "x".repeat(largeItemSize), timestamp: new Date().toISOString(), }; await memoryManager.remember("analysis", largeData); if (i % 10 === 0) { const currentMemory = process.memoryUsage(); memorySnapshots.push({ count: i + 1, heapUsed: currentMemory.heapUsed - startMemory.heapUsed, time: performance.now() - startTime, }); // Force garbage collection if available if (global.gc) { global.gc(); } } // Check for memory leaks - memory shouldn't grow unbounded const currentMemory = process.memoryUsage(); const memoryUsed = currentMemory.heapUsed - startMemory.heapUsed; // Allow for reasonable memory growth but prevent runaway usage const expectedMaxMemory = (i + 1) * largeItemSize * 2; // 2x overhead allowance expect(memoryUsed).toBeLessThan( Math.max(expectedMaxMemory, 200 * 1024 * 1024), ); // Max 200MB } const finalSnapshot = memorySnapshots[memorySnapshots.length - 1]; console.log( `Memory pressure test: ${finalSnapshot.count} items, ${( finalSnapshot.heapUsed / 1024 / 1024 ).toFixed(2)}MB used`, ); // Verify data integrity under pressure const allMemories = await memoryManager.search({ projectId: "memory-pressure-test", }); expect(allMemories.length).toBe(maxItems); }); test("should handle storage device pressure", async () => { memoryManager.setContext({ projectId: "storage-pressure-test" }); // Create many small files to stress the storage system const fileCount = 1000; const operationResults: boolean[] = []; console.log(`Testing storage pressure with ${fileCount} operations...`); for (let i = 0; i < fileCount; i++) { try { const data = { projectId: "storage-pressure-test", index: i, data: `storage-pressure-item-${i}`, timestamp: new Date().toISOString(), }; await memoryManager.remember("analysis", data); operationResults.push(true); if (i % 100 === 0) { console.log(`Storage operations completed: ${i + 1}/${fileCount}`); } } catch (error) { operationResults.push(false); console.error(`Storage operation ${i} failed:`, error); } } const successRate = operationResults.filter((result) => result).length / operationResults.length; console.log( `Storage pressure test: ${(successRate * 100).toFixed( 1, )}% success rate`, ); // Should handle most operations successfully expect(successRate).toBeGreaterThan(0.95); // At least 95% success rate // Verify storage integrity const storedMemories = await memoryManager.search({ projectId: "storage-pressure-test", }); expect(storedMemories.length).toBeGreaterThan(fileCount * 0.9); // At least 90% stored }); }); describe("Edge Case Stress Tests", () => { test("should handle extremely large individual memories", async () => { memoryManager.setContext({ projectId: "large-individual-test" }); const extremeSizes = [ { name: "huge", size: 5 * 1024 * 1024 }, // 5MB { name: "massive", size: 10 * 1024 * 1024 }, // 10MB { name: "gigantic", size: 25 * 1024 * 1024 }, // 25MB ]; for (const testSize of extremeSizes) { console.log( `Testing ${testSize.name} memory (${( testSize.size / 1024 / 1024 ).toFixed(1)}MB)...`, ); const startTime = performance.now(); const largeData = { projectId: "large-individual-test", size: testSize.name, payload: "x".repeat(testSize.size), metadata: { originalSize: testSize.size }, }; try { const memory = await memoryManager.remember("analysis", largeData); const createTime = performance.now() - startTime; // Verify storage const readStartTime = performance.now(); const retrieved = await memoryManager.recall(memory.id); const readTime = performance.now() - readStartTime; expect(retrieved).not.toBeNull(); expect(retrieved?.data.payload.length).toBe(testSize.size); console.log( `${testSize.name}: Create ${createTime.toFixed( 2, )}ms, Read ${readTime.toFixed(2)}ms`, ); // Performance should be reasonable even for large items expect(createTime).toBeLessThan(30000); // 30 seconds max expect(readTime).toBeLessThan(10000); // 10 seconds max } catch (error) { console.error(`Failed to handle ${testSize.name} memory:`, error); throw error; } } }); test("should handle deeply nested data structures", async () => { memoryManager.setContext({ projectId: "nested-structure-test" }); // Create deeply nested object const createNestedObject = (depth: number): any => { if (depth === 0) { return { value: "leaf-node", depth: 0 }; } return { level: depth, children: Array.from({ length: 3 }, (_, i) => ({ id: `child-${depth}-${i}`, data: createNestedObject(depth - 1), metadata: { parent: depth, index: i }, })), metadata: { depth, totalChildren: 3 }, }; }; const testDepths = [10, 15, 20]; for (const depth of testDepths) { console.log(`Testing nested structure depth ${depth}...`); const startTime = performance.now(); const nestedData = { projectId: "nested-structure-test", depth, structure: createNestedObject(depth), metadata: { maxDepth: depth, type: "stress-test" }, }; try { const memory = await memoryManager.remember("analysis", nestedData); const createTime = performance.now() - startTime; // Verify retrieval const readStartTime = performance.now(); const retrieved = await memoryManager.recall(memory.id); const readTime = performance.now() - readStartTime; expect(retrieved).not.toBeNull(); expect(retrieved?.data.depth).toBe(depth); expect(retrieved?.data.structure.level).toBe(depth); console.log( `Depth ${depth}: Create ${createTime.toFixed( 2, )}ms, Read ${readTime.toFixed(2)}ms`, ); // Should handle complex structures efficiently expect(createTime).toBeLessThan(5000); // 5 seconds max expect(readTime).toBeLessThan(2000); // 2 seconds max } catch (error) { console.error( `Failed to handle nested structure depth ${depth}:`, error, ); throw error; } } }); test("should handle rapid context switching", async () => { const contextCount = 100; const operationsPerContext = 10; const totalOperations = contextCount * operationsPerContext; console.log( `Testing rapid context switching: ${contextCount} contexts, ${operationsPerContext} ops each...`, ); const startTime = performance.now(); const results: Array<{ context: string; operationTime: number }> = []; for (let context = 0; context < contextCount; context++) { const contextId = `rapid-context-${context}`; const contextStartTime = performance.now(); memoryManager.setContext({ projectId: contextId }); // Perform operations in this context const contextPromises = Array.from( { length: operationsPerContext }, async (_, i) => { return await memoryManager.remember("analysis", { projectId: contextId, contextIndex: context, operationIndex: i, data: `context-${context}-operation-${i}`, timestamp: new Date().toISOString(), }); }, ); await Promise.all(contextPromises); const contextTime = performance.now() - contextStartTime; results.push({ context: contextId, operationTime: contextTime }); if (context % 20 === 0) { console.log(`Completed context ${context}/${contextCount}`); } } const totalTime = performance.now() - startTime; const avgContextTime = results.reduce((sum, r) => sum + r.operationTime, 0) / results.length; const totalRate = totalOperations / (totalTime / 1000); console.log( `Context switching test: ${(totalTime / 1000).toFixed( 2, )}s total, ${avgContextTime.toFixed(2)}ms avg per context`, ); console.log(`Overall rate: ${totalRate.toFixed(0)} operations/sec`); // Verify all operations completed const allMemories = await memoryManager.search(""); expect(allMemories.length).toBeGreaterThanOrEqual(totalOperations * 0.95); // Allow for 5% loss // Performance should remain reasonable expect(totalTime).toBeLessThan(60000); // Complete within 1 minute expect(totalRate).toBeGreaterThan(50); // At least 50 ops/sec overall }); }); describe("Failure Recovery Stress Tests", () => { test("should recover from simulated storage failures", async () => { memoryManager.setContext({ projectId: "storage-failure-test" }); // Create initial data const initialMemories = []; for (let i = 0; i < 100; i++) { const memory = await memoryManager.remember("analysis", { projectId: "storage-failure-test", index: i, data: `initial-data-${i}`, phase: "before-failure", }); initialMemories.push(memory); } // Simulate storage failure recovery by creating new manager instance const recoveryManager = new MemoryManager(tempDir); await recoveryManager.initialize(); // Verify recovery const recoveredMemories = await recoveryManager.search({ projectId: "storage-failure-test", }); expect(recoveredMemories.length).toBe(100); // Continue operations after recovery recoveryManager.setContext({ projectId: "storage-failure-test" }); for (let i = 0; i < 50; i++) { await recoveryManager.remember("analysis", { projectId: "storage-failure-test", index: 100 + i, data: `post-recovery-data-${i}`, phase: "after-recovery", }); } // Verify total state const finalMemories = await recoveryManager.search({ projectId: "storage-failure-test", }); expect(finalMemories.length).toBe(150); const beforeFailure = finalMemories.filter( (m) => m.data.phase === "before-failure", ); const afterRecovery = finalMemories.filter( (m) => m.data.phase === "after-recovery", ); expect(beforeFailure.length).toBe(100); expect(afterRecovery.length).toBe(50); console.log("Storage failure recovery test completed successfully"); }); test("should handle concurrent access corruption scenarios", async () => { memoryManager.setContext({ projectId: "corruption-test" }); const concurrentWorkers = 5; const operationsPerWorker = 100; const conflictData = Array.from( { length: concurrentWorkers }, (_, workerId) => Array.from({ length: operationsPerWorker }, (_, opId) => ({ projectId: "corruption-test", workerId, operationId: opId, data: `worker-${workerId}-operation-${opId}`, timestamp: new Date().toISOString(), })), ); console.log( `Testing concurrent access with ${concurrentWorkers} workers, ${operationsPerWorker} ops each...`, ); // Execute concurrent operations that might cause conflicts const workerPromises = conflictData.map(async (workerData, workerId) => { const results = []; for (const data of workerData) { try { const memory = await memoryManager.remember("analysis", data); results.push({ success: true, id: memory.id }); } catch (error) { results.push({ success: false, error: (error as Error).message }); } } return { workerId, results }; }); const workerResults = await Promise.all(workerPromises); // Analyze results let totalOperations = 0; let successfulOperations = 0; workerResults.forEach(({ workerId, results }) => { const successful = results.filter((r) => r.success).length; totalOperations += results.length; successfulOperations += successful; console.log( `Worker ${workerId}: ${successful}/${results.length} operations successful`, ); }); const successRate = successfulOperations / totalOperations; console.log( `Overall concurrent access success rate: ${(successRate * 100).toFixed( 1, )}%`, ); // Should handle most concurrent operations successfully expect(successRate).toBeGreaterThan(0.9); // At least 90% success rate // Verify data integrity const allMemories = await memoryManager.search({ projectId: "corruption-test", }); expect(allMemories.length).toBeGreaterThanOrEqual(totalOperations * 0.85); // Allow for some conflicts }); }); });

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/tosin2013/documcp'

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