| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- const net = require('net');
- class KVDBClient {
- constructor(host = 'localhost', port = 8080) {
- this.host = host;
- this.port = port;
- this.socket = null;
- this.connected = false;
- }
- async connect() {
- if (this.connected) return;
-
- return new Promise((resolve, reject) => {
- this.socket = new net.Socket();
- this.socket.setKeepAlive(true);
- this.socket.setNoDelay(true);
-
- const timeout = setTimeout(() => {
- reject(new Error('Connection timeout'));
- }, 5000);
-
- this.socket.connect(this.port, this.host, () => {
- clearTimeout(timeout);
- this.connected = true;
- resolve();
- });
-
- this.socket.once('error', (err) => {
- clearTimeout(timeout);
- this.connected = false;
- reject(err);
- });
- });
- }
- disconnect() {
- if (this.socket) {
- this.socket.destroy();
- this.socket = null;
- this.connected = false;
- }
- }
- sendCommand(command, debug = false) {
- return new Promise(async (resolve, reject) => {
- if (!this.socket) {
- try {
- await this.connect();
- } catch (err) {
- return reject(err);
- }
- }
-
- let response = '';
- const dataHandler = (data) => {
- response += data.toString();
-
- if (response.includes('\r')) {
- this.socket.removeListener('data', dataHandler);
- clearTimeout(timeout);
- const result = response.replace(/\r/g, '').trim();
-
- if (debug) {
- console.log(`[DEBUG] Recebido: ${result}`);
- }
-
- resolve(result);
- }
- };
- this.socket.on('data', dataHandler);
- const timeout = setTimeout(() => {
- this.socket.removeListener('data', dataHandler);
- reject(new Error(`Timeout após 5s`));
- }, 5000);
- if (debug) {
- console.log(`[DEBUG] Enviando: ${command.replace(/\r/g, '\\r')}`);
- }
-
- this.socket.write(command, (err) => {
- if (err) {
- this.socket.removeListener('data', dataHandler);
- clearTimeout(timeout);
- reject(err);
- }
- });
- });
- }
- async write(key, value) {
- return this.sendCommand(`write ${key}|${value}\r`);
- }
- async read(key) {
- return this.sendCommand(`read ${key}\r`);
- }
- async delete(key) {
- return this.sendCommand(`delete ${key}\r`);
- }
- async status() {
- return this.sendCommand(`status\r`);
- }
- async keys() {
- return this.sendCommand(`keys\r`);
- }
- async reads(prefix) {
- return this.sendCommand(`reads ${prefix}\r`);
- }
- }
- function generateValue(size) {
- return 'x'.repeat(size);
- }
- function generateKey(prefix, id) {
- return `${prefix}:${id}`;
- }
- async function testBasicFeatures() {
- console.log('\n=== TESTE 1: FUNCIONALIDADES BÁSICAS ===\n');
- const client = new KVDBClient();
- const features = {
- write: true,
- read: true,
- delete: true,
- keys: true,
- reads: true,
- status: true
- };
- try {
- console.log('Testando CONEXÃO...');
- const status = await client.status();
- console.log(`✓ Conexão OK. Status: ${status}\n`);
- console.log('Testando WRITE...');
- await client.write('test:1', 'valor1');
- console.log(`✓ Write: success`);
- console.log('\nTestando READ...');
- const readRes = await client.read('test:1');
- console.log(`✓ Read: ${readRes}`);
- console.log('\nTestando WRITE múltiplas chaves...');
- await client.write('test:2', 'valor2');
- await client.write('test:3', 'valor3');
- await client.write('other:1', 'outro');
- console.log(`✓ Múltiplas escritas concluídas`);
- console.log('\nTestando KEYS...');
- try {
- const keysRes = await client.keys();
- if (keysRes === 'error') {
- throw new Error('retornou error');
- }
- const keysList = keysRes.split('\n').filter(k => k.trim());
- console.log(`✓ Keys (${keysList.length} chaves): ${keysList.slice(0, 5).join(', ')}${keysList.length > 5 ? '...' : ''}`);
- } catch (err) {
- features.keys = false;
- console.log(`⊘ KEYS não suportado (${err.message}) - será ignorado nos testes`);
- }
- console.log('\nTestando READS com prefixo "test"...');
- try {
- const readsRes = await client.reads('test');
- if (readsRes === 'error') {
- throw new Error('retornou error');
- }
- const readsList = readsRes.split('\n').filter(v => v.trim());
- console.log(`✓ Reads (${readsList.length} valores): ${readsList.slice(0, 3).join(', ')}${readsList.length > 3 ? '...' : ''}`);
- } catch (err) {
- features.reads = false;
- console.log(`⊘ READS não suportado (${err.message}) - será ignorado nos testes`);
- }
- console.log('\nTestando DELETE...');
- try {
- const delRes = await client.delete('test:1');
- if (delRes === 'error') {
- throw new Error('retornou error');
- }
- console.log(`✓ Delete: ${delRes}`);
- console.log('Verificando se foi deletado...');
- const delReadRes = await client.read('test:1');
- if (delReadRes !== 'error') {
- throw new Error(`chave ainda existe com valor "${delReadRes}"`);
- }
- console.log(`✓ Chave foi deletada corretamente`);
- } catch (err) {
- features.delete = false;
- console.log(`⊘ DELETE não suportado (${err.message}) - será ignorado nos testes`);
- }
- console.log('\nTestando DELETE em chave inexistente...');
- if (features.delete) {
- const delNonExist = await client.delete('nao:existe');
- if (delNonExist !== 'error') {
- console.log(`⚠ DELETE deveria retornar error para chave inexistente`);
- } else {
- console.log(`✓ Delete inexistente retornou error`);
- }
- } else {
- console.log(`⊘ Pulado (DELETE não suportado)`);
- }
- console.log('\nTestando comando INVÁLIDO...');
- const invalidRes = await client.sendCommand('invalid comando\r');
- if (invalidRes !== 'error') {
- console.log(`⚠ Comando inválido deveria retornar error`);
- } else {
- console.log(`✓ Comando inválido retornou error`);
- }
- client.disconnect();
-
- console.log('\n✓✓✓ TESTES BÁSICOS CONCLUÍDOS ✓✓✓');
- console.log('\nFuncionalidades disponíveis:');
- console.log(` WRITE: ${features.write ? '✓' : '✗'}`);
- console.log(` READ: ${features.read ? '✓' : '✗'}`);
- console.log(` DELETE: ${features.delete ? '✓' : '✗'}`);
- console.log(` KEYS: ${features.keys ? '✓' : '✗'}`);
- console.log(` READS: ${features.reads ? '✓' : '✗'}`);
- console.log(` STATUS: ${features.status ? '✓' : '✗'}`);
-
- return features;
- } catch (err) {
- client.disconnect();
- console.error(`✗ ERRO FATAL: ${err.message}`);
- return null;
- }
- }
- async function testMillionRecords() {
- console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
- console.log(' TESTE 2: 1 MILHÃO DE REGISTROS');
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
- const client = new KVDBClient();
- const totalRecords = 1000000;
- try {
- await client.connect();
- console.log(`✓ Conexão persistente estabelecida\n`);
-
- console.log(`Iniciando escrita de ${totalRecords.toLocaleString()} registros...\n`);
- const writeStart = Date.now();
- let successCount = 0;
- let errorCount = 0;
- for (let i = 0; i < totalRecords; i++) {
- try {
- const key = generateKey('load', i);
- const value = `value_${i}`;
- await client.write(key, value);
- successCount++;
- if ((i + 1) % 10000 === 0) {
- const elapsed = (Date.now() - writeStart) / 1000;
- const rate = Math.floor((i + 1) / elapsed);
- const progress = ((i + 1) / totalRecords * 100).toFixed(1);
- process.stdout.write(`\r ${(i + 1).toLocaleString()} escritas | ${rate.toLocaleString()} ops/s | ${progress}% | erros: ${errorCount} `);
- }
- } catch (err) {
- errorCount++;
- if (errorCount < 5) {
- console.error(`\n Erro no registro ${i}: ${err.message}`);
- }
- if (!client.connected) {
- await client.connect();
- }
- }
- }
- const writeTime = (Date.now() - writeStart) / 1000;
- const writeRate = Math.floor(successCount / writeTime);
- console.log(`\n\n✓ Escrita concluída em ${writeTime.toFixed(2)}s | ${writeRate.toLocaleString()} ops/s`);
- console.log(` Sucessos: ${successCount.toLocaleString()} | Erros: ${errorCount}`);
- console.log('\nIniciando leitura e validação de todos os registros...\n');
- const readStart = Date.now();
- let readSuccess = 0;
- let readErrors = 0;
- let validationErrors = 0;
- for (let i = 0; i < totalRecords; i++) {
- try {
- const key = generateKey('load', i);
- const expectedValue = `value_${i}`;
- const actualValue = await client.read(key);
-
- if (actualValue !== expectedValue) {
- validationErrors++;
- if (validationErrors <= 5) {
- console.error(`\n Validação falhou para ${key}: esperado "${expectedValue}", recebido "${actualValue}"`);
- }
- }
-
- readSuccess++;
- if ((i + 1) % 10000 === 0) {
- const elapsed = (Date.now() - readStart) / 1000;
- const rate = Math.floor((i + 1) / elapsed);
- const progress = ((i + 1) / totalRecords * 100).toFixed(1);
- process.stdout.write(`\r ${(i + 1).toLocaleString()} leituras | ${rate.toLocaleString()} reads/s | ${progress}% | erros: ${readErrors} | validação: ${validationErrors} `);
- }
- } catch (err) {
- readErrors++;
- if (readErrors <= 5) {
- console.error(`\n Erro na leitura ${i}: ${err.message}`);
- }
- if (!client.connected) {
- await client.connect();
- }
- }
- }
- const readTime = (Date.now() - readStart) / 1000;
- const readRate = Math.floor(readSuccess / readTime);
- console.log(`\n\n✓ Leitura e validação concluída em ${readTime.toFixed(2)}s | ${readRate.toLocaleString()} reads/s`);
- console.log(` Sucessos: ${readSuccess.toLocaleString()} | Erros: ${readErrors} | Erros de validação: ${validationErrors}`);
- client.disconnect();
- console.log('\n✓ Teste de 1 milhão concluído com sucesso');
- return true;
- } catch (err) {
- client.disconnect();
- console.error(`✗ ERRO: ${err.message}`);
- return false;
- }
- }
- async function testMultipleReads(features) {
- if (!features.reads) {
- console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
- console.log(' TESTE 3: LEITURAS MÚLTIPLAS (READS) - PULADO');
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
- console.log('⊘ Comando READS não suportado pelo servidor');
- console.log(' Este teste compararia o desempenho de:');
- console.log(' - READS prefix (bulk read)');
- console.log(' - vs múltiplos READ individuais');
- return false;
- }
- console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
- console.log(' TESTE 3: LEITURAS MÚLTIPLAS (READS vs READ)');
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
-
- const client = new KVDBClient();
- const prefixes = ['user', 'product', 'order', 'session', 'cache'];
- const recordsPerPrefix = 1000;
- try {
- await client.connect();
- console.log('✓ Conexão estabelecida\n');
- // Preparar dados de teste
- console.log(`Preparando ${prefixes.length * recordsPerPrefix} registros...`);
- for (const prefix of prefixes) {
- for (let i = 0; i < recordsPerPrefix; i++) {
- const key = `${prefix}:${i}`;
- const value = `${prefix}_value_${i}`;
- await client.write(key, value);
- }
- console.log(` ✓ ${recordsPerPrefix} registros com prefixo "${prefix}"`);
- }
- console.log('\n' + '─'.repeat(60));
-
- // Teste 1: Usando READS (bulk)
- console.log('\nTeste 1: Usando READS (bulk read por prefixo)');
- const bulkStart = Date.now();
- const bulkResults = {};
-
- for (const prefix of prefixes) {
- const result = await client.reads(prefix);
- const values = result.split('\n').filter(v => v.trim());
- bulkResults[prefix] = values.length;
- console.log(` ${prefix}: ${values.length} valores em ${Date.now() - bulkStart}ms`);
- }
-
- const bulkTime = Date.now() - bulkStart;
- const bulkTotal = Object.values(bulkResults).reduce((a, b) => a + b, 0);
- console.log(`\n✓ READS completado: ${bulkTotal} valores em ${bulkTime}ms`);
- console.log(` Throughput: ${Math.floor((bulkTotal / bulkTime) * 1000)} valores/s`);
- // Teste 2: Usando READ individual
- console.log('\nTeste 2: Usando READ individual (um por um)');
- const individualStart = Date.now();
- let individualCount = 0;
-
- for (const prefix of prefixes) {
- const prefixStart = Date.now();
- for (let i = 0; i < recordsPerPrefix; i++) {
- const key = `${prefix}:${i}`;
- await client.read(key);
- individualCount++;
- }
- console.log(` ${prefix}: ${recordsPerPrefix} reads em ${Date.now() - prefixStart}ms`);
- }
-
- const individualTime = Date.now() - individualStart;
- console.log(`\n✓ READ individual completado: ${individualCount} leituras em ${individualTime}ms`);
- console.log(` Throughput: ${Math.floor((individualCount / individualTime) * 1000)} reads/s`);
- // Comparação
- console.log('\n' + '─'.repeat(60));
- console.log('COMPARAÇÃO DE DESEMPENHO:');
- console.log(` READS (bulk): ${bulkTime}ms`);
- console.log(` READ (individual): ${individualTime}ms`);
- console.log(` Diferença: ${individualTime - bulkTime}ms`);
- console.log(` READS é ${(individualTime / bulkTime).toFixed(2)}x mais rápido`);
- console.log('─'.repeat(60));
- client.disconnect();
- console.log('\n✓ Teste de leituras múltiplas concluído');
- return true;
- } catch (err) {
- client.disconnect();
- console.error(`✗ ERRO: ${err.message}`);
- return false;
- }
- }
- async function testConcurrency() {
- console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
- console.log(' TESTE 4: CONCORRÊNCIA (10 CONEXÕES x 100K = 1M REGISTROS)');
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
-
- const numConnections = 10;
- const recordsPerConnection = 100000;
- const totalRecords = numConnections * recordsPerConnection;
- try {
- console.log(`Iniciando ${numConnections} conexões paralelas...\n`);
- const startTime = Date.now();
- const connectionStats = [];
- const promises = [];
- for (let conn = 0; conn < numConnections; conn++) {
- const promise = (async (connectionId) => {
- const client = new KVDBClient();
- await client.connect();
-
- const connStart = Date.now();
- let success = 0;
- let errors = 0;
- for (let i = 0; i < recordsPerConnection; i++) {
- try {
- const key = generateKey(`conn${connectionId}`, i);
- const value = `conn${connectionId}_value_${i}`;
- await client.write(key, value);
- success++;
- if ((i + 1) % 10000 === 0) {
- const elapsed = (Date.now() - connStart) / 1000;
- const rate = Math.floor((i + 1) / elapsed);
- const progress = ((i + 1) / recordsPerConnection * 100).toFixed(0);
- console.log(` [Conn ${connectionId}] ${(i + 1).toLocaleString()} registros | ${rate.toLocaleString()} ops/s | ${progress}%`);
- }
- } catch (err) {
- errors++;
- if (!client.connected) {
- await client.connect();
- }
- }
- }
- const connTime = (Date.now() - connStart) / 1000;
- const connRate = Math.floor(success / connTime);
- connectionStats.push({ id: connectionId, time: connTime, rate: connRate, errors });
-
- console.log(`✓ Conexão ${connectionId} finalizada: ${connTime.toFixed(2)}s | ${connRate.toLocaleString()} ops/s | erros: ${errors}`);
-
- client.disconnect();
- })(conn);
- promises.push(promise);
- }
- await Promise.all(promises);
- const totalTime = (Date.now() - startTime) / 1000;
- const totalRate = Math.floor(totalRecords / totalTime);
- console.log('\n' + '─'.repeat(60));
- console.log(`RESULTADOS DE ESCRITA:`);
- console.log(` Total de registros: ${totalRecords.toLocaleString()}`);
- console.log(` Tempo total: ${totalTime.toFixed(2)}s`);
- console.log(` Throughput agregado: ${totalRate.toLocaleString()} ops/s`);
- console.log('─'.repeat(60));
- // Ranking
- console.log('\nRanking de Performance (escrita):');
- connectionStats.sort((a, b) => b.rate - a.rate);
- connectionStats.forEach((stat, idx) => {
- const position = `${idx + 1}º`.padEnd(4);
- console.log(` ${position} Conexão ${stat.id}: ${stat.rate.toLocaleString().padStart(8)} ops/s (${stat.time.toFixed(2)}s)`);
- });
- console.log('\nIniciando leituras e validação paralelas...\n');
- const readStart = Date.now();
- const readStats = [];
- const readPromises = [];
- for (let conn = 0; conn < numConnections; conn++) {
- const promise = (async (connectionId) => {
- const client = new KVDBClient();
- await client.connect();
-
- let success = 0;
- let errors = 0;
- let validationErrors = 0;
- const readConnStart = Date.now();
- for (let i = 0; i < recordsPerConnection; i++) {
- try {
- const key = generateKey(`conn${connectionId}`, i);
- const expectedValue = `conn${connectionId}_value_${i}`;
- const actualValue = await client.read(key);
-
- if (actualValue !== expectedValue) {
- validationErrors++;
- if (validationErrors <= 5) {
- console.error(`\n [Conn ${connectionId}] Validação falhou para ${key}: esperado "${expectedValue}", recebido "${actualValue}"`);
- }
- }
-
- success++;
-
- if ((i + 1) % 10000 === 0) {
- const elapsed = (Date.now() - readConnStart) / 1000;
- const rate = Math.floor((i + 1) / elapsed);
- const progress = ((i + 1) / recordsPerConnection * 100).toFixed(0);
- console.log(` [Conn ${connectionId}] ${(i + 1).toLocaleString()} leituras | ${rate.toLocaleString()} reads/s | ${progress}% | validação: ${validationErrors}`);
- }
- } catch (err) {
- errors++;
- if (!client.connected) {
- await client.connect();
- }
- }
- }
- const readConnTime = (Date.now() - readConnStart) / 1000;
- const readConnRate = Math.floor(success / readConnTime);
- readStats.push({ id: connectionId, time: readConnTime, rate: readConnRate, errors, validationErrors });
- console.log(`✓ Conexão ${connectionId}: ${success.toLocaleString()} leituras | ${readConnRate.toLocaleString()} reads/s | validação: ${validationErrors}`);
-
- client.disconnect();
- })(conn);
- readPromises.push(promise);
- }
- await Promise.all(readPromises);
- const readTime = (Date.now() - readStart) / 1000;
- const totalReads = numConnections * recordsPerConnection;
- const readTotalRate = Math.floor(totalReads / readTime);
- const totalValidationErrors = readStats.reduce((sum, stat) => sum + stat.validationErrors, 0);
- console.log('\n' + '─'.repeat(60));
- console.log(`RESULTADOS DE LEITURA E VALIDAÇÃO:`);
- console.log(` Total de leituras: ${totalReads.toLocaleString()}`);
- console.log(` Tempo total: ${readTime.toFixed(2)}s`);
- console.log(` Throughput agregado: ${readTotalRate.toLocaleString()} reads/s`);
- console.log(` Erros de validação: ${totalValidationErrors}`);
- console.log('─'.repeat(60));
- // Ranking de leituras
- console.log('\nRanking de Performance (leitura):');
- readStats.sort((a, b) => b.rate - a.rate);
- readStats.forEach((stat, idx) => {
- const position = `${idx + 1}º`.padEnd(4);
- console.log(` ${position} Conexão ${stat.id}: ${stat.rate.toLocaleString().padStart(8)} reads/s | validação: ${stat.validationErrors}`);
- });
- console.log('\n✓ Teste de concorrência concluído com sucesso');
- return true;
- } catch (err) {
- console.error(`✗ ERRO: ${err.message}`);
- return false;
- }
- }
- async function runAllTests() {
- console.log('\n╔════════════════════════════════════════════════════╗');
- console.log('║ PIZZAKV - SUITE DE TESTES DE PERFORMANCE ║');
- console.log('║ Porta: 8080 ║');
- console.log('╚════════════════════════════════════════════════════╝');
- const features = await testBasicFeatures();
-
- if (!features) {
- console.log('\n✗ Erro fatal nos testes básicos. Abortando...');
- process.exit(1);
- }
-
- if (!features.write || !features.read) {
- console.log('\n✗ WRITE e READ são obrigatórios. Abortando...');
- process.exit(1);
- }
-
- console.log('\n\nIniciando teste de 1 milhão em 3 segundos...');
- await new Promise(resolve => setTimeout(resolve, 3000));
-
- const success2 = await testMillionRecords();
-
- if (!success2) {
- console.log('\n⚠ Teste de 1M falhou, mas continuando...');
- }
-
- console.log('\n\nIniciando teste de leituras múltiplas em 3 segundos...');
- await new Promise(resolve => setTimeout(resolve, 3000));
-
- const success3 = await testMultipleReads(features);
-
- if (!success3 && features.reads) {
- console.log('\n⚠ Teste de leituras múltiplas falhou');
- }
-
- console.log('\n\nIniciando teste de concorrência em 3 segundos...');
- await new Promise(resolve => setTimeout(resolve, 3000));
-
- const success4 = await testConcurrency();
- if (!success4) {
- console.log('\n⚠ Teste de concorrência falhou');
- }
- console.log('\n╔════════════════════════════════════════════════════╗');
- console.log('║ ✓ TODOS OS TESTES CONCLUÍDOS ║');
- console.log('╚════════════════════════════════════════════════════╝\n');
- }
- runAllTests().catch(console.error);
|