Performance Optimization #
Complete guide to optimizing Server Monitor plugin performance for high-scale deployments.
Performance Overview #
The Server Monitor plugin is designed to handle thousands of endpoints efficiently through:
- Queue-based processing for non-blocking operations
- Plan-based prioritization (premium vs free)
- Worker scaling for high-throughput monitoring
- Database optimization with proper indexing
- Caching strategies for reduced database load
Queue Performance #
Worker Configuration #
Optimal Worker Allocation:
# /etc/supervisor/conf.d/servermonitor-workers.conf
# Premium workers (1-minute SLA)
[program:servermonitor-premium]
numprocs=4 # Scale based on premium server count
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/site/artisan queue:work --queue=premium --sleep=3 --tries=3 --timeout=90 --max-time=3600 --max-jobs=1000
autostart=true
autorestart=true
user=www-data
# Free workers (5-minute SLA)
[program:servermonitor-free]
numprocs=2 # Scale based on free server count
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/site/artisan queue:work --queue=free --sleep=3 --tries=3 --timeout=300 --max-time=3600 --max-jobs=1000
autostart=true
autorestart=true
user=www-data
# Calculation workers
[program:servermonitor-calculations]
numprocs=1 # Usually 1 is sufficient
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/site/artisan queue:work --queue=calculations --sleep=5 --tries=3 --timeout=300 --max-time=1800 --max-jobs=100
autostart=true
autorestart=true
user=www-data
Worker Scaling Guidelines #
Premium Workers:
- Capacity: ~60 checks per worker per minute
- Formula:
Workers = (Premium Servers รท 60) ร 1.5 - Example: 200 premium servers = 5 workers minimum
Free Workers:
- Capacity: ~12 checks per worker per minute
- Formula:
Workers = (Free Servers รท 300) ร 1.2 - Example: 600 free servers = 3 workers minimum
Queue Driver Optimization #
Redis (Recommended for Production):
# .env
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=null
# Redis memory optimization
REDIS_DATABASE=1 # Use dedicated database
Redis Configuration (/etc/redis/redis.conf):
# Memory optimization
maxmemory 512mb
maxmemory-policy allkeys-lru
# Persistence (optional for queue data)
save "" # Disable RDB snapshots
appendonly no # Disable AOF
# Connection limits
tcp-keepalive 60
timeout 300
Database Queue Optimization:
// config/queue.php
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 3600,
'after_commit' => false, # Improve performance
],
Database Performance #
Index Optimization #
Critical Indexes:
-- Server queries
CREATE INDEX idx_servers_org_status ON servers(organization_id, status);
CREATE INDEX idx_servers_org_deleted ON servers(organization_id, deleted_at);
-- Log queries
CREATE INDEX idx_server_logs_server_created ON server_logs(server_id, created_at);
CREATE INDEX idx_server_logs_created ON server_logs(created_at);
-- Performance monitoring
CREATE INDEX idx_check_logs_plan_created ON server_check_logs(plan_type, created_at);
CREATE INDEX idx_check_logs_delayed ON server_check_logs(is_delayed, created_at);
-- Communication logs
CREATE INDEX idx_alert_logs_org_type ON alert_logs(organization_id, type);
CREATE INDEX idx_alert_logs_server_created ON alert_logs(server_id, created_at);
Add Missing Indexes:
-- Check for missing indexes
EXPLAIN SELECT * FROM servers WHERE organization_id = 123 AND status = 200;
-- Add composite indexes for common queries
ALTER TABLE servers ADD INDEX idx_org_status_updated (organization_id, status, updated_at);
Query Optimization #
Efficient Queries:
// โ
Good: Use relationships and select specific fields
$servers = Server::with('organization:id,name,selected_plan')
->select('id', 'title', 'status', 'organization_id', 'percent_uptime')
->where('organization_id', $orgId)
->get();
// โ Avoid: N+1 queries
foreach ($servers as $server) {
echo $server->organization->name; // This causes N+1
}
// โ
Good: Eager loading
$servers = Server::with('organization')->get();
foreach ($servers as $server) {
echo $server->organization->name; // Already loaded
}
Chunked Processing:
// Process large datasets in chunks
Server::where('require_calculation', true)
->chunk(100, function ($servers) {
foreach ($servers as $server) {
$server->updatePercentUptime();
}
});
Database Maintenance #
Regular Maintenance:
-- Analyze table statistics
ANALYZE TABLE servers, server_logs, server_check_logs, alert_logs;
-- Optimize table structure
OPTIMIZE TABLE servers, server_logs, server_check_logs, alert_logs;
-- Check table health
CHECK TABLE servers, server_logs, server_check_logs, alert_logs;
Automated Maintenance Script:
#!/bin/bash
# db-maintenance.sh - Run weekly
mysql -u root -p <<EOF
USE your_database;
ANALYZE TABLE servers, server_logs, server_check_logs, alert_logs;
OPTIMIZE TABLE servers, server_logs, server_check_logs, alert_logs;
EOF
# Purge old logs
curl -s "https://yoursite.com/api/servermonitor/purge-logs/YOUR_API_KEY"
Caching Strategies #
Application Caching #
Cache Configuration (config/cache.php):
'default' => 'redis',
'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'prefix' => env('CACHE_PREFIX', 'servermonitor'),
],
],
// Disable request cache for queue workers
'disableRequestCache' => true,
Cached Queries:
// Cache server counts for dashboard
$onlineCount = Cache::remember('servers.online.' . $orgId, 300, function () use ($orgId) {
return Server::where('organization_id', $orgId)
->whereIn('status', Server::getSuccessCodes())
->count();
});
// Cache uptime calculations
$avgUptime = Cache::remember('uptime.avg.' . $orgId, 3600, function () use ($orgId) {
return Server::where('organization_id', $orgId)
->avg('percent_uptime');
});
Redis Optimization #
Redis Memory Management:
# Monitor Redis memory usage
redis-cli info memory
# Key expiration monitoring
redis-cli info keyspace
# Flush specific cache patterns
redis-cli --scan --pattern "cache:*" | xargs redis-cli del
Cache Warming:
// Warm important caches during off-peak hours
Artisan::command('cache:warm', function () {
$organizations = Organization::all();
foreach ($organizations as $org) {
// Pre-calculate dashboard metrics
Cache::forget('servers.online.' . $org->id);
Cache::forget('servers.offline.' . $org->id);
Cache::forget('uptime.avg.' . $org->id);
// Trigger cache population
$this->call('servermonitor:dashboard-cache', ['--org' => $org->id]);
}
});
Server Resource Optimization #
Memory Management #
Worker Memory Limits:
# In supervisor config
command=php -d memory_limit=256M /var/www/site/artisan queue:work --max-jobs=1000
# Monitor memory usage
ps aux | grep "queue:work" | awk '{print $2, $4, $6}' # PID, %MEM, RSS
PHP Configuration (php.ini):
# Memory settings
memory_limit = 256M
max_execution_time = 90
# Opcache optimization
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 4000
opcache.validate_timestamps = 0 # Production only
opcache.enable_file_override = 1
CPU Optimization #
Process Distribution:
# Distribute workers across CPU cores
# For 4-core server with 8 premium workers:
taskset -c 0,1 supervisorctl start servermonitor-premium:00-03
taskset -c 2,3 supervisorctl start servermonitor-premium:04-07
Connection Pooling:
// Use persistent database connections
'mysql' => [
// ... other config
'options' => [
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_TIMEOUT => 30,
],
],
Network Performance #
HTTP Client Optimization #
cURL Settings for Server Checks:
// In CheckServerJob
$ch = curl_init($domain);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 10, # Reasonable timeout
CURLOPT_CONNECTTIMEOUT => 5, # Quick connection timeout
CURLOPT_USERAGENT => 'ServerMonitor/1.0',
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS, # HTTP/2 if available
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_MAXREDIRS => 3, # Limit redirects
]);
DNS Optimization #
DNS Caching:
# Install and configure dnsmasq for local DNS caching
sudo apt install dnsmasq
# Configure dnsmasq
echo "cache-size=1000" >> /etc/dnsmasq.conf
echo "local-ttl=300" >> /etc/dnsmasq.conf
sudo systemctl restart dnsmasq
DNS Resolution in PHP:
// Pre-resolve frequently checked domains
$domains = ['example.com', 'google.com', 'github.com'];
foreach ($domains as $domain) {
gethostbyname($domain); // Populate DNS cache
}
Monitoring and Alerting #
Performance Metrics #
Key Performance Indicators:
- Queue Processing Time: Jobs should complete within SLA
- Worker CPU Usage: Should stay below 70%
- Memory Usage: Monitor for memory leaks
- Database Response Time: Queries should complete < 100ms
- Uptime Calculation Time: Should complete within 5 minutes
Monitoring Dashboard:
Access /albrightlabs/servermonitor/workerperformance for real-time metrics.
Automated Scaling #
Auto-scaling Script:
#!/bin/bash
# auto-scale-workers.sh - Run every 5 minutes
API_KEY="your-api-key"
SITE_URL="https://yoursite.com"
# Get worker status
STATUS=$(curl -s "${SITE_URL}/api/servermonitor/worker-status/${API_KEY}")
QUEUE_SIZE=$(echo $STATUS | jq -r '.queue_status.premium')
# Scale up if queue is backing up
if [ "$QUEUE_SIZE" -gt 50 ]; then
echo "Scaling up premium workers (queue size: $QUEUE_SIZE)"
supervisorctl start servermonitor-premium:04
supervisorctl start servermonitor-premium:05
fi
# Scale down during low usage
if [ "$QUEUE_SIZE" -lt 10 ]; then
echo "Scaling down premium workers (queue size: $QUEUE_SIZE)"
supervisorctl stop servermonitor-premium:04
supervisorctl stop servermonitor-premium:05
fi
Load Testing #
Simulated Load Testing #
Generate Test Servers:
// Create test servers for load testing
php artisan tinker
$org = Organization::first();
for ($i = 0; $i < 1000; $i++) {
Server::create([
'title' => "Test Server {$i}",
'endpoint' => "https://httpbin.org/status/200",
'organization_id' => $org->id,
]);
}
Load Test Script:
#!/bin/bash
# load-test.sh
API_KEY="your-api-key"
SITE_URL="https://yoursite.com"
# Simulate high load
for i in {1..10}; do
curl -s "${SITE_URL}/api/servermonitor/check/premium/${API_KEY}" &
curl -s "${SITE_URL}/api/servermonitor/check/free/${API_KEY}" &
done
wait
echo "Load test completed"
Performance Benchmarks #
Target Performance:
- Premium Checks: 95% completed within 60 seconds
- Free Checks: 95% completed within 300 seconds
- Dashboard Load: < 2 seconds
- API Response: < 500ms
- Uptime Calculation: < 10 seconds per server
Benchmark Script:
// Benchmark uptime calculation
$start = microtime(true);
$server = Server::find(123);
$server->updatePercentUptime();
$duration = microtime(true) - $start;
echo "Uptime calculation took: {$duration} seconds\n";
Troubleshooting Performance Issues #
Common Bottlenecks #
Queue Backlog:
# Check queue sizes
redis-cli llen queues:premium
redis-cli llen queues:free
# Solution: Add more workers or optimize job processing
Database Slow Queries:
-- Enable slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
-- Check slow queries
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;
Memory Leaks:
# Monitor worker memory over time
watch 'ps aux | grep "queue:work" | grep -v grep | awk "{print \$6}" | sort -nr'
# Restart workers if memory usage is high
supervisorctl restart servermonitor-premium:*
Performance Profiling #
Database Query Profiling:
// Enable query logging
DB::enableQueryLog();
// Perform operation
$servers = Server::with('organization')->get();
// View queries
$queries = DB::getQueryLog();
foreach ($queries as $query) {
echo $query['query'] . "\n";
echo "Time: " . $query['time'] . "ms\n\n";
}
Memory Profiling:
// Track memory usage
$memStart = memory_get_usage();
$memPeakStart = memory_get_peak_usage();
// Perform operation
$servers = Server::all();
echo "Memory used: " . (memory_get_usage() - $memStart) . " bytes\n";
echo "Peak memory: " . (memory_get_peak_usage() - $memPeakStart) . " bytes\n";
Production Optimization Checklist #
Pre-Deployment #
- [ ] Opcache enabled and configured
- [ ] Redis cache configured and tested
- [ ] Database indexes verified
- [ ] Queue workers scaled appropriately
- [ ] Supervisor configuration optimized
- [ ] Log rotation configured
- [ ] Monitoring alerts set up
Post-Deployment #
- [ ] Performance baseline established
- [ ] Load testing completed
- [ ] Memory usage monitored
- [ ] Queue performance verified
- [ ] Database performance optimized
- [ ] Alert thresholds configured
- [ ] Scaling procedures documented
Ongoing Maintenance #
- [ ] Weekly performance review
- [ ] Monthly worker scaling assessment
- [ ] Quarterly database optimization
- [ ] Annual architecture review
Previous: โ Branding | Next: Back to Documentation โ