From 8ecae1901bf88667b6fe6ba67acd49debeb5f3d6 Mon Sep 17 00:00:00 2001 From: Boki Date: Tue, 10 Jun 2025 15:28:00 -0400 Subject: [PATCH] improved redis connection script --- scripts/get-redis-connections.sh | 112 --------------------- scripts/get-redis-connections.ts | 168 ++++++++++++++----------------- 2 files changed, 74 insertions(+), 206 deletions(-) delete mode 100644 scripts/get-redis-connections.sh diff --git a/scripts/get-redis-connections.sh b/scripts/get-redis-connections.sh deleted file mode 100644 index 54be3d2..0000000 --- a/scripts/get-redis-connections.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/bash - -# Simple script to get Redis/Dragonfly connections using redis-cli -# This provides a quick alternative to the TypeScript version - -set -e - -# Configuration -REDIS_HOST="${DRAGONFLY_HOST:-localhost}" -REDIS_PORT="${DRAGONFLY_PORT:-6379}" -REDIS_PASSWORD="${DRAGONFLY_PASSWORD:-}" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo -e "${BLUE}šŸ‰ Dragonfly/Redis Connection Monitor${NC}" -echo "========================================" - -# Check if redis-cli is available -if ! command -v redis-cli &> /dev/null; then - echo -e "${RED}āŒ redis-cli not found. Please install redis-tools:${NC}" - echo " Ubuntu/Debian: sudo apt-get install redis-tools" - echo " macOS: brew install redis" - echo " Or use the TypeScript version: bun run scripts/get-redis-connections.ts" - exit 1 -fi - -# Build redis-cli command -REDIS_CMD="redis-cli -h $REDIS_HOST -p $REDIS_PORT" -if [ -n "$REDIS_PASSWORD" ]; then - REDIS_CMD="$REDIS_CMD -a $REDIS_PASSWORD" -fi - -echo -e "${GREEN}šŸ“” Connecting to Dragonfly at $REDIS_HOST:$REDIS_PORT${NC}" - -# Test connection -if ! $REDIS_CMD ping > /dev/null 2>&1; then - echo -e "${RED}āŒ Cannot connect to Dragonfly/Redis${NC}" - echo " Make sure the Docker container is running:" - echo " docker ps | grep dragonfly" - exit 1 -fi - -echo -e "${GREEN}āœ… Connected successfully${NC}" -echo "" - -# Get client list -echo -e "${YELLOW}šŸ“‹ Active Client Connections:${NC}" -echo "----------------------------------------" - -CLIENT_LIST=$($REDIS_CMD CLIENT LIST) - -if [ -z "$CLIENT_LIST" ]; then - echo "No active connections found" - exit 0 -fi - -# Count total connections -TOTAL_CONNECTIONS=$(echo "$CLIENT_LIST" | wc -l) -echo -e "${BLUE}Total Connections: $TOTAL_CONNECTIONS${NC}" -echo "" - -# Group by client name (simple grouping) -echo -e "${YELLOW}šŸ” Connection Details:${NC}" -echo "$CLIENT_LIST" | while IFS= read -r line; do - if [ -n "$line" ]; then - # Extract key information - ID=$(echo "$line" | grep -o 'id=[0-9]*' | cut -d'=' -f2) - ADDR=$(echo "$line" | grep -o 'addr=[^[:space:]]*' | cut -d'=' -f2) - NAME=$(echo "$line" | grep -o 'name=[^[:space:]]*' | cut -d'=' -f2 || echo "unnamed") - AGE=$(echo "$line" | grep -o 'age=[0-9]*' | cut -d'=' -f2) - IDLE=$(echo "$line" | grep -o 'idle=[0-9]*' | cut -d'=' -f2) - CMD=$(echo "$line" | grep -o 'cmd=[^[:space:]]*' | cut -d'=' -f2 || echo "none") - - echo "ā”œā”€ Client ID: $ID | Name: $NAME | Address: $ADDR" - echo " Age: ${AGE}s | Idle: ${IDLE}s | Last Command: $CMD" - echo "" - fi -done - -# Server info -echo -e "${YELLOW}šŸ–„ļø Server Information:${NC}" -echo "----------------------------------------" -$REDIS_CMD INFO server | grep -E "(dragonfly_version|redis_version|connected_clients|uptime_in_seconds)" | head -10 - -echo "" -echo -e "${YELLOW}šŸ’¾ Memory Usage:${NC}" -echo "----------------------------------------" -$REDIS_CMD INFO memory | grep -E "(used_memory_human|maxmemory_human|used_memory_percentage)" | head -5 - -echo "" -echo -e "${GREEN}āœ… Connection analysis complete${NC}" - -# Optional: Monitor mode -if [ "$1" = "--monitor" ] || [ "$1" = "-m" ]; then - INTERVAL=${2:-5} - echo "" - echo -e "${BLUE}šŸ”„ Starting monitoring mode (refresh every ${INTERVAL}s)${NC}" - echo "Press Ctrl+C to stop..." - - while true; do - sleep $INTERVAL - clear - echo -e "${BLUE}šŸ• Last updated: $(date)${NC}" - echo "" - exec "$0" # Re-run this script - done -fi diff --git a/scripts/get-redis-connections.ts b/scripts/get-redis-connections.ts index 576c7ba..35f9871 100644 --- a/scripts/get-redis-connections.ts +++ b/scripts/get-redis-connections.ts @@ -1,18 +1,12 @@ #!/usr/bin/env bun /** - * Script to get all active connections to Dragonfly/Redis and group them by client name + * Redis/Dragonfly Connection Monitor - Simplified Version * - * This script connects to the actual Dragonfly Docker container and retrieves: - * - All active client connections - * - Groups them by client name/type (removes timestamp suffixes) - * - Shows connection details like IP, idle time, commands executed + * Shows active connections grouped by client name with basic stats * * Usage: - * bun run scripts/get-redis-connections.ts - * - * Requirements: - * - Dragonfly Docker container must be running - * - Script assumes default connection settings (localhost:6379) + * bun run scripts/get-redis-connections.ts # Show connections once + * bun run scripts/get-redis-connections.ts --monitor # Monitor in real-time */ import Redis from 'ioredis'; @@ -21,24 +15,23 @@ interface ClientInfo { id: string; name?: string; addr: string; - fd: string; age: string; idle: string; - flags: string; - db: string; - sub: string; - psub: string; - multi: string; - qbuf: string; - qbufFree: string; - obl: string; - oll: string; - omem: string; - events: string; cmd: string; - argv?: string; tot?: string; - [key: string]: string | undefined; + fd?: string; + flags?: string; + db?: string; + sub?: string; + psub?: string; + multi?: string; + qbuf?: string; + qbufFree?: string; + obl?: string; + oll?: string; + omem?: string; + events?: string; + [key: string]: string | undefined; // Index signature for dynamic properties } interface GroupedConnections { @@ -50,18 +43,18 @@ interface GroupedConnections { }; } -class DragonFlyConnectionMonitor { +class RedisConnectionMonitor { private redis: Redis; constructor() { - // Connect to Dragonfly using the same settings as the Docker setup this.redis = new Redis({ host: process.env.DRAGONFLY_HOST || 'localhost', port: parseInt(process.env.DRAGONFLY_PORT || '6379'), password: process.env.DRAGONFLY_PASSWORD || undefined, db: parseInt(process.env.DRAGONFLY_DATABASE || '0'), maxRetriesPerRequest: 3, - lazyConnect: false, + connectTimeout: 10000, + lazyConnect: true, }); } @@ -78,20 +71,8 @@ class DragonFlyConnectionMonitor { const client: ClientInfo = { id: '', addr: '', - fd: '', age: '', idle: '', - flags: '', - db: '', - sub: '', - psub: '', - multi: '', - qbuf: '', - qbufFree: '', - obl: '', - oll: '', - omem: '', - events: '', cmd: '', }; @@ -151,11 +132,11 @@ class DragonFlyConnectionMonitor { // If no name is set, try to infer from the connection characteristics if (clientName === 'unnamed' || !clientName) { - if (client.flags.includes('M')) { + if (client.flags?.includes('M')) { clientName = 'Master Connection'; - } else if (client.flags.includes('S')) { + } else if (client.flags?.includes('S')) { clientName = 'Slave Connection'; - } else if (client.flags.includes('P')) { + } else if (client.flags?.includes('P')) { clientName = 'PubSub Connection'; } else if (client.cmd === 'monitor') { clientName = 'Monitor Connection'; @@ -206,19 +187,12 @@ class DragonFlyConnectionMonitor { `Address: ${client.addr}`, `Age: ${client.age}s`, `Idle: ${client.idle}s`, - `DB: ${client.db}`, - `Flags: ${client.flags}`, - `Last Command: ${client.cmd || 'none'}`, ]; if (client.tot) { details.push(`Total Commands: ${client.tot}`); } - if (client.name) { - details.unshift(`Original Name: ${client.name}`); - } - return details.join(', '); } @@ -228,7 +202,11 @@ class DragonFlyConnectionMonitor { async getConnectionsGroupedByName(): Promise { try { console.log('šŸ” Connecting to Dragonfly...'); - await this.redis.connect(); + + // Check if already connected, if not connect + if (this.redis.status !== 'ready') { + await this.redis.connect(); + } console.log('šŸ“” Fetching client connections...'); const clientListOutput = await this.redis.client('LIST') as string; @@ -241,10 +219,37 @@ class DragonFlyConnectionMonitor { const clients = this.parseClientList(clientListOutput); const grouped = this.groupConnectionsByName(clients); + // Get server and memory info first + const info = await this.redis.info('server'); + const memoryInfo = await this.redis.info('memory'); + + // Parse server info + const infoLines = info.split('\n'); + let version = '', uptime = '', connectedClients = ''; + for (const line of infoLines) { + if (line.includes('dragonfly_version')) version = line.split(':')[1]?.trim() || ''; + if (line.includes('uptime_in_seconds')) { + const seconds = parseInt(line.split(':')[1]?.trim() || '0'); + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + uptime = `${days}d ${hours}h`; + } + if (line.includes('connected_clients')) connectedClients = line.split(':')[1]?.trim() || ''; + } + + // Parse memory info + const memoryLines = memoryInfo.split('\n'); + let usedMemory = '', maxMemory = '', memoryPercent = ''; + for (const line of memoryLines) { + if (line.includes('used_memory_human')) usedMemory = line.split(':')[1]?.trim() || ''; + if (line.includes('maxmemory_human')) maxMemory = line.split(':')[1]?.trim() || ''; + if (line.includes('used_memory_percentage')) memoryPercent = line.split(':')[1]?.trim() || ''; + } + console.log('\nšŸŽÆ DRAGONFLY CONNECTION SUMMARY'); console.log('═'.repeat(60)); - console.log(`Total Active Connections: ${clients.length}`); - console.log(`Connection Groups: ${Object.keys(grouped).length}`); + console.log(`šŸ”— Connections: ${clients.length} active (${Object.keys(grouped).length} groups) | šŸ–„ļø Version: ${version} | ā±ļø Uptime: ${uptime}`); + console.log(`šŸ’¾ Memory: ${usedMemory}${maxMemory ? ` / ${maxMemory}` : ''}${memoryPercent ? ` (${memoryPercent}%)` : ''}`); console.log(''); // Sort groups by count (most connections first) @@ -252,45 +257,11 @@ class DragonFlyConnectionMonitor { // Display grouped connections for (const [groupName, group] of sortedGroups) { - console.log(`šŸ“‚ ${groupName.toUpperCase()}`); - console.log(` Count: ${group.count}`); - console.log(` Total Commands Executed: ${group.totalCommands}`); - console.log(` Average Idle Time: ${group.avgIdleTime}s`); - console.log(' Connections:'); + console.log(`šŸ“‚ ${groupName.toUpperCase()} (${group.count} connections, ${group.totalCommands} commands, ${group.avgIdleTime}s avg idle)`); for (const client of group.connections) { console.log(` ā”œā”€ ${this.formatConnectionDetails(client)}`); } - console.log(''); - } - - // Get additional server info - console.log('šŸ–„ļø DRAGONFLY SERVER INFO'); - console.log('═'.repeat(60)); - const info = await this.redis.info('server'); - const infoLines = info.split('\n'); - - for (const line of infoLines) { - if (line.includes('dragonfly_version') || - line.includes('connected_clients') || - line.includes('used_memory_human') || - line.includes('uptime_in_seconds')) { - console.log(` ${line}`); - } - } - - // Get memory info - console.log('\nšŸ’¾ MEMORY USAGE'); - console.log('═'.repeat(60)); - const memoryInfo = await this.redis.info('memory'); - const memoryLines = memoryInfo.split('\n'); - - for (const line of memoryLines) { - if (line.includes('used_memory_human') || - line.includes('maxmemory_human') || - line.includes('used_memory_percentage')) { - console.log(` ${line}`); - } } } catch (error) { @@ -301,7 +272,10 @@ class DragonFlyConnectionMonitor { console.log(' - Verify environment variables if using custom connection settings'); console.log(' - Run: docker ps | grep dragonfly'); } finally { - await this.redis.disconnect(); + // Only disconnect if we're not in monitoring mode + if (this.redis.status === 'ready') { + await this.redis.disconnect(); + } } } @@ -313,9 +287,14 @@ class DragonFlyConnectionMonitor { console.log('Press Ctrl+C to stop...\n'); const monitor = async () => { - console.clear(); - console.log(`šŸ• Last updated: ${new Date().toLocaleTimeString()}\n`); - await this.getConnectionsGroupedByName(); + try { + console.clear(); + console.log(`šŸ• Last updated: ${new Date().toLocaleTimeString()}\n`); + await this.getConnectionsGroupedByName(); + } catch (error) { + console.error('āŒ Monitoring error:', error); + console.log('Retrying in next interval...'); + } }; // Initial run @@ -325,9 +304,10 @@ class DragonFlyConnectionMonitor { const interval = setInterval(monitor, intervalSeconds * 1000); // Handle graceful shutdown - process.on('SIGINT', () => { + process.on('SIGINT', async () => { clearInterval(interval); console.log('\nšŸ‘‹ Monitoring stopped'); + await this.redis.disconnect(); process.exit(0); }); } @@ -336,7 +316,7 @@ class DragonFlyConnectionMonitor { // Main execution async function main() { const args = process.argv.slice(2); - const monitor = new DragonFlyConnectionMonitor(); + const monitor = new RedisConnectionMonitor(); if (args.includes('--monitor') || args.includes('-m')) { const intervalArg = args.find(arg => arg.startsWith('--interval=')); @@ -350,7 +330,7 @@ async function main() { // Help text if (process.argv.includes('--help') || process.argv.includes('-h')) { console.log(` -šŸ‰ Dragonfly Connection Monitor +šŸ‰ Redis/Dragonfly Connection Monitor Usage: bun run scripts/get-redis-connections.ts [options]