Skip to main content
Glama
n-r-w

KnowledgeGraph MCP Server

by n-r-w
database-performance.test.ts10.7 kB
import { Pool as PgPool } from 'pg'; import Database from 'better-sqlite3'; import { Entity, KnowledgeGraph } from '../../core.js'; import { SearchConfig } from '../../search/types.js'; import { PostgreSQLFuzzyStrategy } from '../../search/strategies/postgresql-strategy.js'; import { SQLiteFuzzyStrategy } from '../../search/strategies/sqlite-strategy.js'; import { PerformanceUtils } from './performance-utils.js'; // Skip global test setup for performance tests jest.mock('../setup.ts', () => ({ beforeEach: jest.fn(), afterEach: jest.fn() })); describe('Database Performance Tests', () => { const TEST_CONNECTION_STRING = process.env.KNOWLEDGEGRAPH_TEST_CONNECTION_STRING || 'postgresql://postgres:1@localhost:5432/knowledgegraph_test'; const TEST_SQLITE_PATH = ':memory:'; let pgPool: PgPool; let sqliteDb: Database.Database; let config: SearchConfig; let testEntities: Entity[]; let testProject: string; beforeAll(async () => { testProject = `perf_test_${Date.now()}`; config = { useDatabaseSearch: true, threshold: 0.3, clientSideFallback: true, fuseOptions: { threshold: 0.3, distance: 100, includeScore: true, keys: ['name', 'entityType', 'observations', 'tags'] } }; // Generate test data testEntities = PerformanceUtils.generateRealisticTestEntities(1000); // Setup PostgreSQL connection (only if available) try { pgPool = new PgPool({ connectionString: TEST_CONNECTION_STRING, max: 5, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); // Test connection const client = await pgPool.connect(); client.release(); // Setup test database await setupPostgreSQLTestData(); } catch (error) { console.warn('PostgreSQL not available for performance testing:', error); pgPool = null as any; } // Setup SQLite connection try { sqliteDb = new Database(TEST_SQLITE_PATH); await setupSQLiteTestData(); } catch (error) { console.warn('SQLite not available for performance testing:', error); sqliteDb = null as any; } }); afterAll(async () => { if (pgPool) { try { // Clean up test data const client = await pgPool.connect(); await client.query('DELETE FROM entities WHERE project = $1', [testProject]); await client.query('DELETE FROM relations WHERE project = $1', [testProject]); client.release(); await pgPool.end(); } catch (error) { console.warn('Error cleaning up PostgreSQL:', error); } } if (sqliteDb) { try { sqliteDb.close(); } catch (error) { console.warn('Error closing SQLite:', error); } } }); async function setupPostgreSQLTestData() { if (!pgPool) return; const client = await pgPool.connect(); try { // Create tables if they don't exist await client.query(` CREATE TABLE IF NOT EXISTS entities ( id SERIAL PRIMARY KEY, project TEXT NOT NULL, name TEXT NOT NULL, entity_type TEXT NOT NULL, observations TEXT[], tags TEXT[], updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); await client.query(` CREATE TABLE IF NOT EXISTS relations ( id SERIAL PRIMARY KEY, project TEXT NOT NULL, from_entity TEXT NOT NULL, to_entity TEXT NOT NULL, relation_type TEXT NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Enable fuzzy search extensions await client.query('CREATE EXTENSION IF NOT EXISTS pg_trgm'); await client.query('CREATE EXTENSION IF NOT EXISTS fuzzystrmatch'); // Create indexes for performance await client.query(` CREATE INDEX IF NOT EXISTS entities_name_trgm_idx ON entities USING GIN (name gin_trgm_ops) `); await client.query(` CREATE INDEX IF NOT EXISTS entities_type_trgm_idx ON entities USING GIN (entity_type gin_trgm_ops) `); // Insert test data await client.query('DELETE FROM entities WHERE project = $1', [testProject]); for (const entity of testEntities) { await client.query( 'INSERT INTO entities (project, name, entity_type, observations, tags) VALUES ($1, $2, $3, $4, $5)', [testProject, entity.name, entity.entityType, JSON.stringify(entity.observations), JSON.stringify(entity.tags || [])] ); } console.log(`Inserted ${testEntities.length} entities into PostgreSQL for performance testing`); } finally { client.release(); } } async function setupSQLiteTestData() { if (!sqliteDb) return; // Create tables sqliteDb.exec(` CREATE TABLE IF NOT EXISTS entities ( id INTEGER PRIMARY KEY AUTOINCREMENT, project TEXT NOT NULL, name TEXT NOT NULL, entity_type TEXT NOT NULL, observations TEXT, tags TEXT, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); sqliteDb.exec(` CREATE TABLE IF NOT EXISTS relations ( id INTEGER PRIMARY KEY AUTOINCREMENT, project TEXT NOT NULL, from_entity TEXT NOT NULL, to_entity TEXT NOT NULL, relation_type TEXT NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Insert test data const insertStmt = sqliteDb.prepare(` INSERT INTO entities (project, name, entity_type, observations, tags) VALUES (?, ?, ?, ?, ?) `); for (const entity of testEntities) { insertStmt.run( testProject, entity.name, entity.entityType, JSON.stringify(entity.observations), JSON.stringify(entity.tags || []) ); } console.log(`Inserted ${testEntities.length} entities into SQLite for performance testing`); } describe('PostgreSQL Database Performance', () => { test('should benchmark PostgreSQL database-level fuzzy search', async () => { if (!pgPool) { console.log('Skipping PostgreSQL test - database not available'); return; } const strategy = new PostgreSQLFuzzyStrategy(config, pgPool, testProject); const benchmark = await PerformanceUtils.runBenchmark(async () => { return strategy.searchDatabase('JavaScript', 0.3, testProject); }, 10); console.log('\n=== PostgreSQL Database Search (1000 entities) ==='); console.log(PerformanceUtils.formatBenchmarkResults(benchmark)); console.log(`Results found: ${benchmark.results[0].length}`); // Database search should be fast expect(benchmark.avgTime).toBeLessThan(100); // Should be under 100ms expect(benchmark.results[0].length).toBeGreaterThan(0); }); test('should compare PostgreSQL database vs client-side search', async () => { if (!pgPool) { console.log('Skipping PostgreSQL comparison test - database not available'); return; } const strategy = new PostgreSQLFuzzyStrategy(config, pgPool, testProject); // Database search benchmark const dbBenchmark = await PerformanceUtils.runBenchmark(async () => { return strategy.searchDatabase('React', 0.3, testProject); }, 10); // Client-side search benchmark const clientBenchmark = await PerformanceUtils.runBenchmark(async () => { return strategy.searchClientSide(testEntities, 'React'); }, 10); console.log('\n=== PostgreSQL: Database vs Client-side Comparison ==='); console.log('\nDatabase Search:'); console.log(PerformanceUtils.formatBenchmarkResults(dbBenchmark)); console.log(`Results: ${dbBenchmark.results[0].length}`); console.log('\nClient-side Search:'); console.log(PerformanceUtils.formatBenchmarkResults(clientBenchmark)); console.log(`Results: ${clientBenchmark.results[0].length}`); // Both should perform well, database might be faster for large datasets expect(dbBenchmark.avgTime).toBeLessThan(200); expect(clientBenchmark.avgTime).toBeLessThan(500); }); }); describe('SQLite Performance', () => { test('should benchmark SQLite client-side search performance', async () => { if (!sqliteDb) { console.log('Skipping SQLite test - database not available'); return; } const strategy = new SQLiteFuzzyStrategy(config, sqliteDb, testProject); const benchmark = await PerformanceUtils.runBenchmark(async () => { return strategy.searchClientSide(testEntities, 'TypeScript'); }, 10); console.log('\n=== SQLite Client-side Search (1000 entities) ==='); console.log(PerformanceUtils.formatBenchmarkResults(benchmark)); console.log(`Results found: ${benchmark.results[0].length}`); // SQLite client-side search should be fast expect(benchmark.avgTime).toBeLessThan(200); expect(benchmark.results[0].length).toBeGreaterThan(0); }); }); describe('Cross-Database Performance Comparison', () => { test('should compare PostgreSQL vs SQLite client-side performance', async () => { if (!pgPool || !sqliteDb) { console.log('Skipping cross-database test - databases not available'); return; } const pgStrategy = new PostgreSQLFuzzyStrategy(config, pgPool, testProject); const sqliteStrategy = new SQLiteFuzzyStrategy(config, sqliteDb, testProject); // PostgreSQL client-side benchmark const pgBenchmark = await PerformanceUtils.runBenchmark(async () => { return pgStrategy.searchClientSide(testEntities, 'Docker'); }, 10); // SQLite client-side benchmark const sqliteBenchmark = await PerformanceUtils.runBenchmark(async () => { return sqliteStrategy.searchClientSide(testEntities, 'Docker'); }, 10); console.log('\n=== Cross-Database Client-side Performance ==='); console.log('\nPostgreSQL Client-side:'); console.log(PerformanceUtils.formatBenchmarkResults(pgBenchmark)); console.log('\nSQLite Client-side:'); console.log(PerformanceUtils.formatBenchmarkResults(sqliteBenchmark)); // Both should perform similarly for client-side search expect(pgBenchmark.avgTime).toBeLessThan(200); expect(sqliteBenchmark.avgTime).toBeLessThan(200); // Results should be similar (fuzzy search may have slight variations) expect(Math.abs(pgBenchmark.results[0].length - sqliteBenchmark.results[0].length)).toBeLessThan(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/n-r-w/knowledgegraph-mcp'

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