improved redis connection script
This commit is contained in:
parent
fdb8823df7
commit
8ecae1901b
2 changed files with 74 additions and 206 deletions
|
|
@ -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
|
|
||||||
|
|
@ -1,18 +1,12 @@
|
||||||
#!/usr/bin/env bun
|
#!/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:
|
* Shows active connections grouped by client name with basic stats
|
||||||
* - All active client connections
|
|
||||||
* - Groups them by client name/type (removes timestamp suffixes)
|
|
||||||
* - Shows connection details like IP, idle time, commands executed
|
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* bun run scripts/get-redis-connections.ts
|
* bun run scripts/get-redis-connections.ts # Show connections once
|
||||||
*
|
* bun run scripts/get-redis-connections.ts --monitor # Monitor in real-time
|
||||||
* Requirements:
|
|
||||||
* - Dragonfly Docker container must be running
|
|
||||||
* - Script assumes default connection settings (localhost:6379)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
|
|
@ -21,24 +15,23 @@ interface ClientInfo {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
addr: string;
|
addr: string;
|
||||||
fd: string;
|
|
||||||
age: string;
|
age: string;
|
||||||
idle: 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;
|
cmd: string;
|
||||||
argv?: string;
|
|
||||||
tot?: 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 {
|
interface GroupedConnections {
|
||||||
|
|
@ -50,18 +43,18 @@ interface GroupedConnections {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class DragonFlyConnectionMonitor {
|
class RedisConnectionMonitor {
|
||||||
private redis: Redis;
|
private redis: Redis;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Connect to Dragonfly using the same settings as the Docker setup
|
|
||||||
this.redis = new Redis({
|
this.redis = new Redis({
|
||||||
host: process.env.DRAGONFLY_HOST || 'localhost',
|
host: process.env.DRAGONFLY_HOST || 'localhost',
|
||||||
port: parseInt(process.env.DRAGONFLY_PORT || '6379'),
|
port: parseInt(process.env.DRAGONFLY_PORT || '6379'),
|
||||||
password: process.env.DRAGONFLY_PASSWORD || undefined,
|
password: process.env.DRAGONFLY_PASSWORD || undefined,
|
||||||
db: parseInt(process.env.DRAGONFLY_DATABASE || '0'),
|
db: parseInt(process.env.DRAGONFLY_DATABASE || '0'),
|
||||||
maxRetriesPerRequest: 3,
|
maxRetriesPerRequest: 3,
|
||||||
lazyConnect: false,
|
connectTimeout: 10000,
|
||||||
|
lazyConnect: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,20 +71,8 @@ class DragonFlyConnectionMonitor {
|
||||||
const client: ClientInfo = {
|
const client: ClientInfo = {
|
||||||
id: '',
|
id: '',
|
||||||
addr: '',
|
addr: '',
|
||||||
fd: '',
|
|
||||||
age: '',
|
age: '',
|
||||||
idle: '',
|
idle: '',
|
||||||
flags: '',
|
|
||||||
db: '',
|
|
||||||
sub: '',
|
|
||||||
psub: '',
|
|
||||||
multi: '',
|
|
||||||
qbuf: '',
|
|
||||||
qbufFree: '',
|
|
||||||
obl: '',
|
|
||||||
oll: '',
|
|
||||||
omem: '',
|
|
||||||
events: '',
|
|
||||||
cmd: '',
|
cmd: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -151,11 +132,11 @@ class DragonFlyConnectionMonitor {
|
||||||
|
|
||||||
// If no name is set, try to infer from the connection characteristics
|
// If no name is set, try to infer from the connection characteristics
|
||||||
if (clientName === 'unnamed' || !clientName) {
|
if (clientName === 'unnamed' || !clientName) {
|
||||||
if (client.flags.includes('M')) {
|
if (client.flags?.includes('M')) {
|
||||||
clientName = 'Master Connection';
|
clientName = 'Master Connection';
|
||||||
} else if (client.flags.includes('S')) {
|
} else if (client.flags?.includes('S')) {
|
||||||
clientName = 'Slave Connection';
|
clientName = 'Slave Connection';
|
||||||
} else if (client.flags.includes('P')) {
|
} else if (client.flags?.includes('P')) {
|
||||||
clientName = 'PubSub Connection';
|
clientName = 'PubSub Connection';
|
||||||
} else if (client.cmd === 'monitor') {
|
} else if (client.cmd === 'monitor') {
|
||||||
clientName = 'Monitor Connection';
|
clientName = 'Monitor Connection';
|
||||||
|
|
@ -206,19 +187,12 @@ class DragonFlyConnectionMonitor {
|
||||||
`Address: ${client.addr}`,
|
`Address: ${client.addr}`,
|
||||||
`Age: ${client.age}s`,
|
`Age: ${client.age}s`,
|
||||||
`Idle: ${client.idle}s`,
|
`Idle: ${client.idle}s`,
|
||||||
`DB: ${client.db}`,
|
|
||||||
`Flags: ${client.flags}`,
|
|
||||||
`Last Command: ${client.cmd || 'none'}`,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (client.tot) {
|
if (client.tot) {
|
||||||
details.push(`Total Commands: ${client.tot}`);
|
details.push(`Total Commands: ${client.tot}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client.name) {
|
|
||||||
details.unshift(`Original Name: ${client.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return details.join(', ');
|
return details.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +202,11 @@ class DragonFlyConnectionMonitor {
|
||||||
async getConnectionsGroupedByName(): Promise<void> {
|
async getConnectionsGroupedByName(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.log('🔍 Connecting to Dragonfly...');
|
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...');
|
console.log('📡 Fetching client connections...');
|
||||||
const clientListOutput = await this.redis.client('LIST') as string;
|
const clientListOutput = await this.redis.client('LIST') as string;
|
||||||
|
|
@ -241,10 +219,37 @@ class DragonFlyConnectionMonitor {
|
||||||
const clients = this.parseClientList(clientListOutput);
|
const clients = this.parseClientList(clientListOutput);
|
||||||
const grouped = this.groupConnectionsByName(clients);
|
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('\n🎯 DRAGONFLY CONNECTION SUMMARY');
|
||||||
console.log('═'.repeat(60));
|
console.log('═'.repeat(60));
|
||||||
console.log(`Total Active Connections: ${clients.length}`);
|
console.log(`🔗 Connections: ${clients.length} active (${Object.keys(grouped).length} groups) | 🖥️ Version: ${version} | ⏱️ Uptime: ${uptime}`);
|
||||||
console.log(`Connection Groups: ${Object.keys(grouped).length}`);
|
console.log(`💾 Memory: ${usedMemory}${maxMemory ? ` / ${maxMemory}` : ''}${memoryPercent ? ` (${memoryPercent}%)` : ''}`);
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// Sort groups by count (most connections first)
|
// Sort groups by count (most connections first)
|
||||||
|
|
@ -252,45 +257,11 @@ class DragonFlyConnectionMonitor {
|
||||||
|
|
||||||
// Display grouped connections
|
// Display grouped connections
|
||||||
for (const [groupName, group] of sortedGroups) {
|
for (const [groupName, group] of sortedGroups) {
|
||||||
console.log(`📂 ${groupName.toUpperCase()}`);
|
console.log(`📂 ${groupName.toUpperCase()} (${group.count} connections, ${group.totalCommands} commands, ${group.avgIdleTime}s avg idle)`);
|
||||||
console.log(` Count: ${group.count}`);
|
|
||||||
console.log(` Total Commands Executed: ${group.totalCommands}`);
|
|
||||||
console.log(` Average Idle Time: ${group.avgIdleTime}s`);
|
|
||||||
console.log(' Connections:');
|
|
||||||
|
|
||||||
for (const client of group.connections) {
|
for (const client of group.connections) {
|
||||||
console.log(` ├─ ${this.formatConnectionDetails(client)}`);
|
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) {
|
} catch (error) {
|
||||||
|
|
@ -301,7 +272,10 @@ class DragonFlyConnectionMonitor {
|
||||||
console.log(' - Verify environment variables if using custom connection settings');
|
console.log(' - Verify environment variables if using custom connection settings');
|
||||||
console.log(' - Run: docker ps | grep dragonfly');
|
console.log(' - Run: docker ps | grep dragonfly');
|
||||||
} finally {
|
} 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');
|
console.log('Press Ctrl+C to stop...\n');
|
||||||
|
|
||||||
const monitor = async () => {
|
const monitor = async () => {
|
||||||
console.clear();
|
try {
|
||||||
console.log(`🕐 Last updated: ${new Date().toLocaleTimeString()}\n`);
|
console.clear();
|
||||||
await this.getConnectionsGroupedByName();
|
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
|
// Initial run
|
||||||
|
|
@ -325,9 +304,10 @@ class DragonFlyConnectionMonitor {
|
||||||
const interval = setInterval(monitor, intervalSeconds * 1000);
|
const interval = setInterval(monitor, intervalSeconds * 1000);
|
||||||
|
|
||||||
// Handle graceful shutdown
|
// Handle graceful shutdown
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', async () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
console.log('\n👋 Monitoring stopped');
|
console.log('\n👋 Monitoring stopped');
|
||||||
|
await this.redis.disconnect();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +316,7 @@ class DragonFlyConnectionMonitor {
|
||||||
// Main execution
|
// Main execution
|
||||||
async function main() {
|
async function main() {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const monitor = new DragonFlyConnectionMonitor();
|
const monitor = new RedisConnectionMonitor();
|
||||||
|
|
||||||
if (args.includes('--monitor') || args.includes('-m')) {
|
if (args.includes('--monitor') || args.includes('-m')) {
|
||||||
const intervalArg = args.find(arg => arg.startsWith('--interval='));
|
const intervalArg = args.find(arg => arg.startsWith('--interval='));
|
||||||
|
|
@ -350,7 +330,7 @@ async function main() {
|
||||||
// Help text
|
// Help text
|
||||||
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
||||||
console.log(`
|
console.log(`
|
||||||
🐉 Dragonfly Connection Monitor
|
🐉 Redis/Dragonfly Connection Monitor
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
bun run scripts/get-redis-connections.ts [options]
|
bun run scripts/get-redis-connections.ts [options]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue