c71b2ecf8e
Now OPcache memory is automatically calculated to fit within the 60% RAM safety threshold: 1. PHP-FPM capacity validation now reserves 256MB for OPcache - max_safe_php_fpm = (60% RAM) - 256MB - Prevents PHP-FPM+OPcache from exceeding safe limits 2. OPcache memory calculation now dynamic: - Accepts optional available_memory parameter - Won't exceed available limits - Minimum 32MB, maximum 256MB (typical servers) 3. Level 5 (Optimize Everything): - Calculates available memory after PHP-FPM allocation - Passes available memory to OPcache calculation - OPcache automatically scales down on low-memory servers Result: Option 5 now automatically balances PHP-FPM + OPcache within safe limits without manual configuration.
3652 lines
138 KiB
Bash
Executable File
3652 lines
138 KiB
Bash
Executable File
#!/bin/bash
|
|
# PHP & Server Performance Optimizer
|
|
# Interactive tool for analyzing and optimizing PHP-FPM configurations
|
|
# Part of Server Toolkit - Phase 3: Main Interactive Script
|
|
|
|
# Source required libraries
|
|
PHP_TOOLKIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../.. && pwd)"
|
|
source "$PHP_TOOLKIT_DIR/lib/common-functions.sh" || { echo "ERROR: common-functions.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/system-detect.sh" || { echo "ERROR: system-detect.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/user-manager.sh" || { echo "ERROR: user-manager.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-detector.sh" || { echo "ERROR: php-detector.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-analyzer.sh" || { echo "ERROR: php-analyzer.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-config-manager.sh" || { echo "ERROR: php-config-manager.sh not found"; exit 1; }
|
|
source "$PHP_TOOLKIT_DIR/lib/php-calculator-improved.sh" || { echo "ERROR: php-calculator-improved.sh not found"; exit 1; }
|
|
|
|
# Phase 3 Modular Architecture - NEW (optional but recommended for batch operations)
|
|
source "$PHP_TOOLKIT_DIR/lib/php-ui.sh" 2>/dev/null || true
|
|
source "$PHP_TOOLKIT_DIR/lib/php-scanner.sh" 2>/dev/null || true
|
|
source "$PHP_TOOLKIT_DIR/lib/php-action-executor.sh" 2>/dev/null || true
|
|
source "$PHP_TOOLKIT_DIR/lib/php-server-manager.sh" 2>/dev/null || true
|
|
|
|
# True Optimization - Analytics Library for real data-driven decisions
|
|
source "$PHP_TOOLKIT_DIR/lib/php-analytics.sh" 2>/dev/null || true
|
|
|
|
# Color codes (using safe echo -e)
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
MAGENTA='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
WHITE='\033[1;37m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Safe color echo function
|
|
cecho() {
|
|
echo -e "$@"
|
|
}
|
|
|
|
# ============================================================================
|
|
# BANNER & DISPLAY FUNCTIONS
|
|
# ============================================================================
|
|
|
|
show_banner() {
|
|
clear
|
|
cecho "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
|
cecho "${CYAN}║${WHITE} PHP & SERVER PERFORMANCE OPTIMIZER ${CYAN}║${NC}"
|
|
cecho "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
}
|
|
|
|
show_main_menu() {
|
|
cecho "${WHITE}${BOLD}MAIN MENU${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}1${NC}) Analyze Single Domain"
|
|
cecho " ${GREEN}2${NC}) Analyze All Domains (Server-Wide)"
|
|
cecho " ${GREEN}3${NC}) Quick Health Check (All Domains)"
|
|
cecho " ${GREEN}4${NC}) Optimize Domain PHP Settings"
|
|
cecho " ${GREEN}5${NC}) Optimize Server-Wide PHP Settings"
|
|
cecho " ${GREEN}6${NC}) View OPcache Statistics"
|
|
cecho " ${GREEN}7${NC}) View PHP-FPM Process Stats"
|
|
cecho " ${GREEN}8${NC}) Check for Configuration Issues"
|
|
cecho " ${GREEN}9${NC}) Check Server Memory Capacity (OOM Risk)"
|
|
echo ""
|
|
cecho " ${YELLOW}b${NC}) Backup Current Configurations"
|
|
cecho " ${YELLOW}r${NC}) Restore from Backup"
|
|
echo ""
|
|
cecho " ${RED}0${NC}) Exit"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
}
|
|
|
|
# ============================================================================
|
|
# DOMAIN SELECTION
|
|
# ============================================================================
|
|
|
|
select_domain() {
|
|
local action="${1:-analyze}"
|
|
|
|
cecho "${WHITE}${BOLD}SELECT DOMAIN${NC}"
|
|
echo ""
|
|
|
|
# Ask about filtering
|
|
cecho "${CYAN}Filter options:${NC}"
|
|
cecho " ${GREEN}n${NC}) No filter (show all)"
|
|
cecho " ${GREEN}s${NC}) Search by name"
|
|
cecho " ${GREEN}t${NC}) Show high-traffic domains"
|
|
cecho " ${GREEN}o${NC}) Show domains needing optimization"
|
|
cecho " ${RED}q${NC}) Cancel"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
read -p "Select filter: " filter_choice
|
|
filter_choice=$(echo "${filter_choice,,}")
|
|
|
|
case "$filter_choice" in
|
|
q)
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
# Get all users with domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
if [ -z "$users" ]; then
|
|
cecho "${RED}ERROR: No users found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return 1
|
|
fi
|
|
|
|
# Build domain list
|
|
declare -a domains
|
|
declare -A domain_to_user
|
|
|
|
while IFS= read -r username; do
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
domains+=("$domain")
|
|
domain_to_user["$domain"]="$username"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
if [ ${#domains[@]} -eq 0 ]; then
|
|
cecho "${RED}ERROR: No domains found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return 1
|
|
fi
|
|
|
|
# Apply filters
|
|
declare -a filtered_domains
|
|
|
|
case "$filter_choice" in
|
|
s)
|
|
# Search by name
|
|
echo ""
|
|
read -p "Search pattern: " search_pattern
|
|
for domain in "${domains[@]}"; do
|
|
if [[ "$domain" =~ $search_pattern ]]; then
|
|
filtered_domains+=("$domain")
|
|
fi
|
|
done
|
|
;;
|
|
t)
|
|
# High-traffic domains
|
|
for domain in "${domains[@]}"; do
|
|
local peak=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
if [[ "$peak" =~ ^[0-9]+$ ]] && [ "$peak" -ge 10 ]; then
|
|
filtered_domains+=("$domain")
|
|
fi
|
|
done
|
|
# Sort by peak traffic (highest first)
|
|
;;
|
|
o)
|
|
# Domains needing optimization
|
|
for domain in "${domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
local issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
|
|
local has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
if [ "$has_high_issues" -gt 0 ]; then
|
|
filtered_domains+=("$domain")
|
|
fi
|
|
done
|
|
;;
|
|
*)
|
|
# No filter
|
|
filtered_domains=("${domains[@]}")
|
|
;;
|
|
esac
|
|
|
|
if [ ${#filtered_domains[@]} -eq 0 ]; then
|
|
cecho "${YELLOW}No domains match filter criteria${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return 1
|
|
fi
|
|
|
|
# Display numbered list with optimization status
|
|
cecho "${CYAN}Available domains:${NC}"
|
|
echo ""
|
|
|
|
local index=1
|
|
for domain in "${filtered_domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
local php_version
|
|
php_version=$(detect_php_version_for_domain "$username" "$domain" 2>/dev/null || echo "unknown")
|
|
|
|
# Check optimization status (only for optimize action to reduce noise)
|
|
local status_indicator=""
|
|
if [[ "$action" == "optimize" ]]; then
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
|
|
local has_high_issues
|
|
has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
|
|
if [ "$has_high_issues" -gt 0 ]; then
|
|
status_indicator="${YELLOW}[NEEDS OPTIMIZATION]${NC}"
|
|
else
|
|
status_indicator="${GREEN}[OK]${NC}"
|
|
fi
|
|
fi
|
|
|
|
# Show peak traffic if available
|
|
local traffic_info=""
|
|
local peak=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "")
|
|
if [ -n "$peak" ] && [ "$peak" != "?" ]; then
|
|
traffic_info="${CYAN}(peak: ${peak})${NC} "
|
|
fi
|
|
|
|
printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} ${YELLOW}(${php_version})${NC} %s%s\n" "$index" "$domain" "$(echo -e "$traffic_info")" "$(echo -e "$status_indicator")"
|
|
index=$((index + 1))
|
|
done
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
# Validate domain selection with retry loop
|
|
while true; do
|
|
read -p "Select domain number (or 'q' to cancel): " selection
|
|
|
|
if [[ "$selection" == "q" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt ${#filtered_domains[@]} ]; then
|
|
echo ""
|
|
cecho "${RED}Invalid selection. Please enter a number 1-${#filtered_domains[@]}${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
|
|
break
|
|
done
|
|
|
|
# Return selected domain and username
|
|
local selected_domain="${filtered_domains[$((selection - 1))]}"
|
|
local selected_user="${domain_to_user[$selected_domain]}"
|
|
|
|
echo "$selected_domain|$selected_user"
|
|
return 0
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 1: ANALYZE SINGLE DOMAIN
|
|
# ============================================================================
|
|
|
|
analyze_single_domain() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "analyze")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Analyzing: ${GREEN}$domain${WHITE} (user: ${CYAN}$username${WHITE})${NC}"
|
|
echo ""
|
|
|
|
# Run comprehensive analysis
|
|
analyze_domain_php "$username" "$domain"
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 2: ANALYZE ALL DOMAINS
|
|
# ============================================================================
|
|
|
|
analyze_all_domains() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}SERVER-WIDE ANALYSIS${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will show domain-by-domain PHP-FPM configuration analysis${NC}"
|
|
cecho "${YELLOW}including memory impact and combined server load...${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
return
|
|
fi
|
|
|
|
# Call the dedicated batch analyzer script
|
|
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
local batch_analyzer="$script_dir/php-fpm-batch-analyzer.sh"
|
|
|
|
if [ -f "$batch_analyzer" ]; then
|
|
bash "$batch_analyzer"
|
|
else
|
|
cecho "${RED}ERROR: Batch analyzer script not found at $batch_analyzer${NC}"
|
|
read -p "Press Enter to continue..."
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 3: QUICK HEALTH CHECK
|
|
# ============================================================================
|
|
|
|
quick_health_check() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}QUICK HEALTH CHECK${NC}"
|
|
echo ""
|
|
|
|
# Get all users
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
declare -A issue_counts
|
|
issue_counts["CRITICAL"]=0
|
|
issue_counts["HIGH"]=0
|
|
issue_counts["MEDIUM"]=0
|
|
issue_counts["LOW"]=0
|
|
|
|
local total_domains=0
|
|
|
|
while IFS= read -r username; do
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
total_domains=$((total_domains + 1))
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain")
|
|
|
|
# Count by severity
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ -z "$severity" ] && continue
|
|
|
|
issue_counts["$severity"]=$((issue_counts["$severity"] + 1))
|
|
done <<< "$issues"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Display results
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}HEALTH CHECK RESULTS${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Total Domains: ${WHITE}$total_domains${NC}"
|
|
echo ""
|
|
cecho " ${RED}CRITICAL${NC} issues: ${issue_counts["CRITICAL"]}"
|
|
cecho " ${YELLOW}HIGH${NC} issues: ${issue_counts["HIGH"]}"
|
|
cecho " ${BLUE}MEDIUM${NC} issues: ${issue_counts["MEDIUM"]}"
|
|
cecho " ${GREEN}LOW${NC} issues: ${issue_counts["LOW"]}"
|
|
echo ""
|
|
|
|
# Overall health score
|
|
local health_score
|
|
health_score=$((100 - (issue_counts["CRITICAL"] * 20) - (issue_counts["HIGH"] * 10) - (issue_counts["MEDIUM"] * 5) - (issue_counts["LOW"] * 2)))
|
|
[ "$health_score" -lt 0 ] && health_score=0
|
|
|
|
if [ "$health_score" -ge 90 ]; then
|
|
cecho " Overall Health: ${GREEN}${BOLD}$health_score/100 - EXCELLENT${NC}"
|
|
elif [ "$health_score" -ge 70 ]; then
|
|
cecho " Overall Health: ${YELLOW}${BOLD}$health_score/100 - GOOD${NC}"
|
|
elif [ "$health_score" -ge 50 ]; then
|
|
cecho " Overall Health: ${YELLOW}${BOLD}$health_score/100 - FAIR${NC}"
|
|
else
|
|
cecho " Overall Health: ${RED}${BOLD}$health_score/100 - POOR${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 4: OPTIMIZE DOMAIN
|
|
# ============================================================================
|
|
|
|
optimize_domain() {
|
|
show_banner
|
|
|
|
cecho "${WHITE}${BOLD}DOMAIN OPTIMIZATION${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}s${NC}) Optimize single domain"
|
|
cecho " ${GREEN}m${NC}) Optimize multiple domains (batch)"
|
|
cecho " ${RED}q${NC}) Cancel"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
read -p "Select option: " batch_choice
|
|
|
|
case "${batch_choice,,}" in
|
|
s)
|
|
optimize_single_domain_wrapper
|
|
;;
|
|
m)
|
|
optimize_multiple_domains_wrapper
|
|
;;
|
|
q)
|
|
return
|
|
;;
|
|
*)
|
|
cecho "${RED}Invalid selection${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
;;
|
|
esac
|
|
}
|
|
|
|
optimize_single_domain_wrapper() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "optimize")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}OPTIMIZE: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Detect issues
|
|
cecho "${YELLOW}Detecting issues...${NC}"
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain")
|
|
|
|
# Display issues
|
|
local has_issues=false
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ -z "$issue_type" ] && continue
|
|
|
|
if [ "$issue_type" != "NONE" ]; then
|
|
has_issues=true
|
|
|
|
case "$severity" in
|
|
CRITICAL)
|
|
cecho "${RED}[CRITICAL]${NC} $message"
|
|
;;
|
|
HIGH)
|
|
cecho "${YELLOW}[HIGH]${NC} $message"
|
|
;;
|
|
MEDIUM)
|
|
cecho "${BLUE}[MEDIUM]${NC} $message"
|
|
;;
|
|
LOW)
|
|
cecho "${GREEN}[LOW]${NC} $message"
|
|
;;
|
|
esac
|
|
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
fi
|
|
done <<< "$issues"
|
|
|
|
if [ "$has_issues" = false ]; then
|
|
cecho "${GREEN}${BOLD}✓ No issues detected - configuration is optimal!${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server-wide memory capacity first
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}SERVER MEMORY ANALYSIS${NC}"
|
|
echo ""
|
|
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
cecho " Total Server RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM Capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}% of RAM)"
|
|
|
|
case "$status" in
|
|
CRITICAL)
|
|
cecho " Server Status: ${RED}${BOLD}CRITICAL${NC} - Server at risk of OOM!"
|
|
;;
|
|
WARNING)
|
|
cecho " Server Status: ${YELLOW}${BOLD}WARNING${NC} - High memory pressure"
|
|
;;
|
|
CAUTION)
|
|
cecho " Server Status: ${YELLOW}CAUTION${NC} - Approaching limits"
|
|
;;
|
|
HEALTHY)
|
|
cecho " Server Status: ${GREEN}HEALTHY${NC} - Sufficient headroom"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
|
|
# Check for max_children errors
|
|
cecho "${WHITE}${BOLD}MAX_CHILDREN ERROR ANALYSIS${NC}"
|
|
echo ""
|
|
|
|
local max_children_errors
|
|
max_children_errors=$(analyze_max_children_errors "$username" 7)
|
|
local error_count last_error_time
|
|
error_count=$(echo "$max_children_errors" | cut -d'|' -f1)
|
|
last_error_time=$(echo "$max_children_errors" | cut -d'|' -f2)
|
|
|
|
if [ "$error_count" -gt 0 ]; then
|
|
cecho " ${RED}✗${NC} Found ${RED}${BOLD}$error_count${NC} max_children errors in last 7 days"
|
|
cecho " ${YELLOW}Last error: $last_error_time${NC}"
|
|
cecho " ${CYAN}→${NC} This domain is hitting process limits and rejecting requests!"
|
|
else
|
|
cecho " ${GREEN}✓${NC} No max_children errors in last 7 days"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Get optimization recommendations
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}RECOMMENDED OPTIMIZATIONS${NC}"
|
|
echo ""
|
|
|
|
# Get total system memory for improved calculation
|
|
local total_ram_mb
|
|
total_ram_mb=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
# IMPROVED: Calculate using new algorithm
|
|
local improved_result
|
|
improved_result=$(calculate_optimal_php_settings "$username" "$total_ram_mb")
|
|
local improved_max_children improved_pm_mode improved_min_spare improved_max_spare improved_reason
|
|
improved_max_children=$(get_field "$improved_result" 1)
|
|
improved_pm_mode=$(get_field "$improved_result" 2)
|
|
improved_min_spare=$(get_field "$improved_result" 3)
|
|
improved_max_spare=$(get_field "$improved_result" 4)
|
|
improved_reason=$(get_field "$improved_result" 5)
|
|
|
|
# OLD: Calculate using legacy algorithm (for comparison)
|
|
local optimal_result
|
|
optimal_result=$(calculate_optimal_max_children "$username" 1024)
|
|
local legacy_max_children legacy_reason
|
|
legacy_max_children=$(echo "$optimal_result" | cut -d'|' -f1)
|
|
legacy_reason=$(echo "$optimal_result" | cut -d'|' -f2)
|
|
|
|
# Get current max_children
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain")
|
|
|
|
# Track available optimizations
|
|
declare -A opt_available
|
|
declare -A opt_description
|
|
local opt_count=0
|
|
|
|
local current_max_children current_pm_mode
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max_children=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
current_pm_mode=$(grep "^pm =" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ -n "$current_max_children" ] && [ "$improved_max_children" -ne "$current_max_children" ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["max_children"]="true"
|
|
opt_description["max_children"]="Adjust pm.max_children from $current_max_children to $improved_max_children"
|
|
|
|
# Display comprehensive recommendation
|
|
cecho "${GREEN}$opt_count.${NC} Adjust ${BOLD}pm.max_children${NC} from ${RED}$current_max_children${NC} to ${GREEN}$improved_max_children${NC}"
|
|
cecho " ${CYAN}Improved Algorithm:${NC} $improved_max_children (${improved_reason})"
|
|
cecho " ${YELLOW}Legacy Algorithm:${NC} $legacy_max_children (${legacy_reason})"
|
|
|
|
# Show comparison if different
|
|
if [ "$improved_max_children" -ne "$legacy_max_children" ]; then
|
|
local diff=$((improved_max_children - legacy_max_children))
|
|
if [ "$diff" -gt 0 ]; then
|
|
cecho " ${GREEN}✓ Improved: +$diff processes (safer)${NC}"
|
|
else
|
|
cecho " ${GREEN}✓ Improved: $diff processes (more efficient)${NC}"
|
|
fi
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# Recommend PM mode if different
|
|
if [ -n "$current_pm_mode" ] && [ "$current_pm_mode" != "$improved_pm_mode" ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["pm_mode"]="true"
|
|
opt_description["pm_mode"]="Change pm mode from $current_pm_mode to $improved_pm_mode"
|
|
cecho "${GREEN}$opt_count.${NC} Change ${BOLD}pm${NC} mode from ${RED}$current_pm_mode${NC} to ${GREEN}$improved_pm_mode${NC}"
|
|
cecho " Recommended: $improved_pm_mode with min_spare=$improved_min_spare, max_spare=$improved_max_spare"
|
|
echo ""
|
|
fi
|
|
fi
|
|
|
|
# Check OPcache
|
|
local opcache_status
|
|
opcache_status=$(analyze_opcache_effectiveness "$username")
|
|
local opcache_state hit_rate opcache_rec
|
|
opcache_state=$(echo "$opcache_status" | cut -d'|' -f1)
|
|
hit_rate=$(echo "$opcache_status" | cut -d'|' -f2)
|
|
opcache_rec=$(echo "$opcache_status" | cut -d'|' -f5)
|
|
|
|
if [ "$opcache_state" = "DISABLED" ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["opcache"]="true"
|
|
opt_description["opcache"]="Enable OPcache for 40-70% performance boost"
|
|
cecho "${GREEN}$opt_count.${NC} ${BOLD}Enable OPcache${NC} for 40-70% performance boost"
|
|
echo ""
|
|
else
|
|
# Check if hit_rate is numeric before using awk
|
|
if [ -n "$hit_rate" ] && [[ "$hit_rate" =~ ^[0-9]+$ ]]; then
|
|
local hit_rate_low=$(awk "BEGIN {print ($hit_rate < 90 ? 1 : 0)}" 2>/dev/null || echo 0)
|
|
if [ "$hit_rate_low" -eq 1 ]; then
|
|
opt_count=$((opt_count + 1))
|
|
opt_available["opcache"]="true"
|
|
opt_description["opcache"]="Increase opcache.memory_consumption (current hit rate: ${hit_rate}%)"
|
|
cecho "${GREEN}$opt_count.${NC} ${BOLD}Increase opcache.memory_consumption${NC} (current hit rate: ${hit_rate}%)"
|
|
echo ""
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Check if there are any optimizations to apply
|
|
if [ "$opt_count" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All settings are optimal - no changes needed!${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
# Ask user which optimizations to apply
|
|
cecho "${WHITE}${BOLD}SELECT OPTIMIZATIONS TO APPLY${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}a${NC}) Apply all $opt_count recommendations"
|
|
[ "${opt_available[max_children]}" = "true" ] && cecho " ${GREEN}1${NC}) Apply max_children optimization only"
|
|
[ "${opt_available[pm_mode]}" = "true" ] && cecho " ${GREEN}2${NC}) Apply PM mode optimization only"
|
|
[ "${opt_available[opcache]}" = "true" ] && cecho " ${GREEN}3${NC}) Apply OPcache optimization only"
|
|
cecho " ${RED}n${NC}) Cancel - don't apply any changes"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
# Validate optimization selection with retry loop
|
|
while true; do
|
|
read -p "Select option: " apply_choice
|
|
|
|
if ! [[ "$apply_choice" =~ ^[a123nNA]$ ]]; then
|
|
echo ""
|
|
cecho "${RED}Invalid selection. Please enter a, 1, 2, 3, or n${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
break
|
|
done
|
|
|
|
# Determine which optimizations to apply
|
|
local apply_max_children=false
|
|
local apply_pm_mode=false
|
|
local apply_opcache=false
|
|
|
|
# Convert to lowercase for consistent case matching
|
|
apply_choice=${apply_choice,,}
|
|
|
|
case "$apply_choice" in
|
|
a)
|
|
apply_max_children=${opt_available[max_children]:-false}
|
|
apply_pm_mode=${opt_available[pm_mode]:-false}
|
|
apply_opcache=${opt_available[opcache]:-false}
|
|
;;
|
|
1)
|
|
apply_max_children=${opt_available[max_children]:-false}
|
|
;;
|
|
2)
|
|
apply_pm_mode=${opt_available[pm_mode]:-false}
|
|
;;
|
|
3)
|
|
apply_opcache=${opt_available[opcache]:-false}
|
|
;;
|
|
n)
|
|
cecho "${YELLOW}Optimization cancelled - no changes made${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
;;
|
|
esac
|
|
|
|
# Check if nothing was selected
|
|
if [ "$apply_max_children" = "false" ] && [ "$apply_pm_mode" = "false" ] && [ "$apply_opcache" = "false" ]; then
|
|
cecho "${YELLOW}No optimizations selected${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Create backup first
|
|
cecho "${CYAN}Creating backup before making changes...${NC}"
|
|
local backup_dir
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
cecho "${RED}${BOLD}✗ Backup failed - aborting changes${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho "${GREEN}✓ Backup created: $(basename "$backup_dir")${NC}"
|
|
echo ""
|
|
|
|
# Apply changes
|
|
cecho "${CYAN}Applying selected optimizations...${NC}"
|
|
echo ""
|
|
|
|
local changes_made=0
|
|
local changes_failed=0
|
|
|
|
# Apply max_children if selected (uses improved algorithm)
|
|
if [ "$apply_max_children" = "true" ]; then
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
if [ -n "$improved_max_children" ] && [ -n "$current_max_children" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$improved_max_children" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm.max_children = $improved_max_children"
|
|
changes_made=$((changes_made + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} Failed to set pm.max_children"
|
|
changes_failed=$((changes_failed + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply PM mode if selected (STATIC/DYNAMIC/ONDEMAND)
|
|
if [ "$apply_pm_mode" = "true" ]; then
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
if [ -n "$improved_pm_mode" ]; then
|
|
# Apply pm mode
|
|
if modify_fpm_pool_setting "$pool_config" "pm" "$improved_pm_mode" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm = $improved_pm_mode"
|
|
|
|
# For DYNAMIC and ONDEMAND modes, also set min/max spare servers
|
|
if [[ "$improved_pm_mode" == "dynamic" ]] || [[ "$improved_pm_mode" == "ondemand" ]]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.min_spare_servers" "$improved_min_spare" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm.min_spare_servers = $improved_min_spare"
|
|
fi
|
|
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_spare_servers" "$improved_max_spare" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Set pm.max_spare_servers = $improved_max_spare"
|
|
fi
|
|
fi
|
|
|
|
changes_made=$((changes_made + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} Failed to set pm mode"
|
|
changes_failed=$((changes_failed + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply OPcache if selected
|
|
if [ "$apply_opcache" = "true" ]; then
|
|
# Note: OPcache settings are in php.ini, not FPM pool config
|
|
# This would require php.ini modification (not implemented yet)
|
|
cecho " ${YELLOW}⚠${NC} OPcache optimization requires php.ini modification (not yet implemented)"
|
|
# TODO: Implement php.ini modification for OPcache
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
if [ "$changes_made" -gt 0 ]; then
|
|
# Validate the pool config before reloading
|
|
cecho "${CYAN}Validating configuration...${NC}"
|
|
local config_valid=true
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
if ! validate_pool_config "$pool_config" >/dev/null 2>&1; then
|
|
cecho "${RED}✗ Configuration validation failed!${NC}"
|
|
cecho "${YELLOW}The modified configuration has syntax errors.${NC}"
|
|
config_valid=false
|
|
|
|
# Offer to rollback
|
|
if confirm "Rollback changes?"; then
|
|
cecho "${CYAN}Rolling back configuration...${NC}"
|
|
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ Configuration restored from backup${NC}"
|
|
else
|
|
cecho "${RED}✗ Rollback failed - manual recovery may be needed${NC}"
|
|
cecho "${YELLOW}Backup location: $backup_dir${NC}"
|
|
fi
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
else
|
|
cecho "${GREEN}✓ Configuration is valid${NC}"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ Applied $changes_made optimization(s)${NC}"
|
|
|
|
if [ "$changes_failed" -gt 0 ]; then
|
|
cecho "${YELLOW}⚠ Failed to apply $changes_failed optimization(s)${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${YELLOW}Changes have been applied. Restart PHP-FPM for changes to take effect.${NC}"
|
|
echo ""
|
|
|
|
if confirm "Restart PHP-FPM now?"; then
|
|
# Detect PHP version
|
|
local php_version
|
|
php_version=$(detect_php_version_for_domain "$username" "$domain")
|
|
|
|
cecho "${CYAN}Restarting PHP-FPM ($php_version)...${NC}"
|
|
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ PHP-FPM reloaded${NC}"
|
|
|
|
# Verify it's running
|
|
sleep 2
|
|
if verify_php_fpm_running "$php_version" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ PHP-FPM is running and accepting connections${NC}"
|
|
|
|
# Verify applied changes - check if new config loaded
|
|
local verify_result
|
|
verify_result=$(verify_applied_changes "$username" "$domain" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$verify_result" ]; then
|
|
cecho "${GREEN}✓ New configuration loaded successfully${NC}"
|
|
fi
|
|
else
|
|
cecho "${RED}✗ WARNING: PHP-FPM is not responding!${NC}"
|
|
cecho "${YELLOW}Service may have crashed due to invalid configuration.${NC}"
|
|
cecho "${YELLOW}Attempting rollback...${NC}"
|
|
|
|
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ Configuration rolled back${NC}"
|
|
cecho "${YELLOW}Restart PHP-FPM manually once this script completes${NC}"
|
|
else
|
|
cecho "${RED}✗ Rollback failed - manual recovery needed${NC}"
|
|
cecho "${YELLOW}Backup location: $backup_dir${NC}"
|
|
fi
|
|
fi
|
|
else
|
|
cecho "${RED}✗ Failed to restart PHP-FPM${NC}"
|
|
cecho "${YELLOW}Attempting rollback...${NC}"
|
|
|
|
if rollback_domain_config "$username" "$domain" >/dev/null 2>&1; then
|
|
cecho "${GREEN}✓ Configuration rolled back to previous state${NC}"
|
|
else
|
|
cecho "${RED}✗ Rollback failed - manual recovery may be needed${NC}"
|
|
cecho "${YELLOW}Backup location: $backup_dir${NC}"
|
|
fi
|
|
fi
|
|
else
|
|
cecho "${YELLOW}Skipping restart - remember to restart PHP-FPM manually!${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}Backup location: ${WHITE}$(basename "$backup_dir")${NC}"
|
|
cecho "${CYAN}To rollback: Use Option 'r' (Restore from Backup)${NC}"
|
|
else
|
|
cecho "${YELLOW}No changes were applied${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
optimize_multiple_domains_wrapper() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}SELECT DOMAINS TO OPTIMIZE${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Batch operation: Optimize multiple domains in sequence${NC}"
|
|
echo ""
|
|
|
|
# Get all users with domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
if [ -z "$users" ]; then
|
|
cecho "${RED}ERROR: No users found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Build domain list with optimization status
|
|
declare -a domains
|
|
declare -A domain_to_user
|
|
|
|
while IFS= read -r username; do
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
domains+=("$domain")
|
|
domain_to_user["$domain"]="$username"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
if [ ${#domains[@]} -eq 0 ]; then
|
|
cecho "${RED}ERROR: No domains found on system${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Display numbered list with optimization status
|
|
cecho "${CYAN}Available domains (select ones to optimize):${NC}"
|
|
echo ""
|
|
|
|
local index=1
|
|
for domain in "${domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "NONE|NONE|None")
|
|
local has_high_issues
|
|
has_high_issues=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
|
|
local status_indicator=""
|
|
if [ "$has_high_issues" -gt 0 ]; then
|
|
status_indicator="${YELLOW}[NEEDS OPTIMIZATION]${NC}"
|
|
else
|
|
status_indicator="${GREEN}[OK]${NC}"
|
|
fi
|
|
|
|
printf " ${GREEN}%-3d${NC}) %-40s ${CYAN}[${username}]${NC} %s\n" "$index" "$domain" "$(echo -e "$status_indicator")"
|
|
index=$((index + 1))
|
|
done
|
|
|
|
echo ""
|
|
cecho "${CYAN}Enter numbers separated by spaces (e.g., '1 3 5') or 'all' for all domains, 'none' to cancel${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
read -p "Selection: " user_selection
|
|
|
|
# Parse selection
|
|
user_selection=$(echo "$user_selection" | tr '[:upper:]' '[:lower:]')
|
|
|
|
declare -a selected_domains
|
|
|
|
if [[ "$user_selection" == "all" ]]; then
|
|
selected_domains=("${domains[@]}")
|
|
elif [[ "$user_selection" =~ ^(none|n)$ ]]; then
|
|
return
|
|
else
|
|
# Parse individual numbers
|
|
for num in $user_selection; do
|
|
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#domains[@]} ]; then
|
|
selected_domains+=("${domains[$((num - 1))]}")
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ ${#selected_domains[@]} -eq 0 ]; then
|
|
cecho "${YELLOW}No domains selected${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Confirm
|
|
echo ""
|
|
cecho "${CYAN}Selected ${#selected_domains[@]} domain(s) for optimization${NC}"
|
|
if ! confirm "Continue?"; then
|
|
return
|
|
fi
|
|
|
|
# Optimize each selected domain
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}BATCH OPTIMIZATION IN PROGRESS${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
|
|
for domain in "${selected_domains[@]}"; do
|
|
local username="${domain_to_user[$domain]}"
|
|
|
|
cecho "${CYAN}Optimizing: ${WHITE}$domain${NC} ${CYAN}[$username]${NC}"
|
|
|
|
# Run single domain optimization (simplified)
|
|
optimize_domain_direct "$domain" "$username"
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
optimized=$((optimized + 1))
|
|
else
|
|
failed=$((failed + 1))
|
|
fi
|
|
|
|
echo ""
|
|
done
|
|
|
|
# Display summary
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}BATCH OPTIMIZATION COMPLETE${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho " Successfully optimized: ${GREEN}$optimized${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}$failed${NC}"
|
|
fi
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
optimize_domain_direct() {
|
|
local domain="$1"
|
|
local username="$2"
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain" 2>/dev/null || echo "")
|
|
|
|
# Check if there are issues
|
|
local critical_high_count
|
|
critical_high_count=$(echo "$issues" | grep -cE "^[^|]*\|(CRITICAL|HIGH)\|" 2>/dev/null || echo "0")
|
|
|
|
if [ "$critical_high_count" -eq 0 ]; then
|
|
cecho " ${GREEN}✓ No optimization needed${NC}"
|
|
return 0
|
|
fi
|
|
|
|
# Get pool config
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
|
cecho " ${YELLOW}⊘ No pool config found - skipping${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Get recommendations
|
|
local total_ram_mb
|
|
total_ram_mb=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
local improved_result
|
|
improved_result=$(calculate_optimal_php_settings "$username" "$total_ram_mb" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$improved_result" ]; then
|
|
cecho " ${YELLOW}⊘ Could not calculate recommendations - skipping${NC}"
|
|
return 1
|
|
fi
|
|
|
|
local improved_max=$(get_field "$improved_result" 1)
|
|
local current_max=$(grep "^pm.max_children" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ -z "$current_max" ] || [ -z "$improved_max" ] || [ "$current_max" = "$improved_max" ]; then
|
|
cecho " ${GREEN}✓ Already optimized${NC}"
|
|
return 0
|
|
fi
|
|
|
|
# Create backup
|
|
local backup_dir
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
cecho " ${RED}✗ Backup failed - skipping${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Apply optimization
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$improved_max" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓ Updated max_children: $current_max → $improved_max${NC}"
|
|
return 0
|
|
else
|
|
cecho " ${RED}✗ Optimization failed${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS FOR TIERED OPTIMIZATION
|
|
# ============================================================================
|
|
|
|
# Calculate optimal memory_limit based on domain traffic and type
|
|
calculate_optimal_memory_limit() {
|
|
local username="$1"
|
|
local domain="$2"
|
|
local avg_rpm="$3"
|
|
|
|
# Get current memory_limit
|
|
local current_memory
|
|
current_memory=$(get_effective_php_setting "$username" "memory_limit")
|
|
|
|
# Default recommendation based on traffic
|
|
local recommended="128M"
|
|
|
|
if [ "$avg_rpm" -ge 100 ]; then
|
|
recommended="256M"
|
|
elif [ "$avg_rpm" -ge 50 ]; then
|
|
recommended="192M"
|
|
elif [ "$avg_rpm" -ge 20 ]; then
|
|
recommended="128M"
|
|
else
|
|
recommended="64M"
|
|
fi
|
|
|
|
echo "$recommended"
|
|
}
|
|
|
|
# Find all php.ini files for a domain
|
|
find_php_ini_files() {
|
|
local username="$1"
|
|
local domain="$2"
|
|
|
|
local ini_files=""
|
|
|
|
# cPanel locations
|
|
if [ -d "/home/$username/public_html" ]; then
|
|
# Check for .user.ini in document root
|
|
if [ -f "/home/$username/public_html/.user.ini" ]; then
|
|
ini_files+="/home/$username/public_html/.user.ini "
|
|
fi
|
|
fi
|
|
|
|
# Check all PHP versions' pool configs
|
|
for php_conf in /opt/cpanel/ea-php*/root/etc/php.ini; do
|
|
if [ -f "$php_conf" ]; then
|
|
ini_files+="$php_conf "
|
|
fi
|
|
done
|
|
|
|
# Check /etc/php.ini
|
|
if [ -f "/etc/php.ini" ]; then
|
|
ini_files+="/etc/php.ini "
|
|
fi
|
|
|
|
echo "$ini_files" | tr -s ' '
|
|
}
|
|
|
|
# Modify php.ini setting safely
|
|
modify_php_ini_setting() {
|
|
local ini_file="$1"
|
|
local setting="$2"
|
|
local value="$3"
|
|
|
|
if [ ! -f "$ini_file" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# Backup before modifying
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null || return 1
|
|
|
|
# Check if setting exists
|
|
if grep -q "^$setting" "$ini_file"; then
|
|
# Replace existing setting
|
|
sed -i "s/^$setting.*/$setting = $value/" "$ini_file" 2>/dev/null || {
|
|
mv "$ini_file.backup.$$" "$ini_file"
|
|
return 1
|
|
}
|
|
else
|
|
# Add new setting (find appropriate section)
|
|
if grep -q "^\[PHP\]" "$ini_file"; then
|
|
sed -i "/^\[PHP\]/a $setting = $value" "$ini_file" 2>/dev/null || {
|
|
mv "$ini_file.backup.$$" "$ini_file"
|
|
return 1
|
|
}
|
|
else
|
|
# Just append to end
|
|
echo "$setting = $value" >> "$ini_file" 2>/dev/null || {
|
|
mv "$ini_file.backup.$$" "$ini_file"
|
|
return 1
|
|
}
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Validate php.ini syntax
|
|
validate_php_ini() {
|
|
local ini_file="$1"
|
|
|
|
if [ ! -f "$ini_file" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# Use php -i to check for syntax errors (basic validation)
|
|
php -d "display_errors=0" -r "return 0;" 2>&1 | grep -q "Parse error\|Fatal error" && return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
# Calculate optimal pm.max_requests
|
|
calculate_optimal_max_requests() {
|
|
local avg_rpm="$1"
|
|
|
|
# Base recommendation: every 500 requests = 1 process recycle
|
|
# This prevents memory leaks from accumulating
|
|
local max_requests=500
|
|
|
|
if [ "$avg_rpm" -ge 100 ]; then
|
|
max_requests=1000
|
|
elif [ "$avg_rpm" -ge 50 ]; then
|
|
max_requests=750
|
|
elif [ "$avg_rpm" -ge 20 ]; then
|
|
max_requests=500
|
|
else
|
|
max_requests=300
|
|
fi
|
|
|
|
echo "$max_requests"
|
|
}
|
|
|
|
# Check if OPcache is enabled
|
|
is_opcache_enabled() {
|
|
local username="$1"
|
|
|
|
local result=$(check_opcache_enabled "$username" 2>/dev/null)
|
|
[ "$result" = "1" ] && return 0 || return 1
|
|
}
|
|
|
|
# Calculate optimal OPcache memory
|
|
calculate_optimal_opcache_memory() {
|
|
local avg_rpm="$1"
|
|
local available_memory="${2:-}" # Optional: available memory limit
|
|
|
|
# Base recommendation in MB based on traffic
|
|
local memory="64"
|
|
|
|
if [ "$avg_rpm" -ge 100 ]; then
|
|
memory="256"
|
|
elif [ "$avg_rpm" -ge 50 ]; then
|
|
memory="192"
|
|
elif [ "$avg_rpm" -ge 20 ]; then
|
|
memory="128"
|
|
else
|
|
memory="64"
|
|
fi
|
|
|
|
# If available memory is specified, don't exceed it
|
|
if [ -n "$available_memory" ] && [ "$available_memory" -gt 0 ]; then
|
|
# Extract numeric value (remove 'M' if present)
|
|
local avail_num=${available_memory%M}
|
|
if [ "$memory" -gt "$avail_num" ]; then
|
|
memory=$avail_num
|
|
fi
|
|
# Minimum 32MB for OPcache
|
|
[ "$memory" -lt 32 ] && memory=32
|
|
fi
|
|
|
|
echo "${memory}M"
|
|
}
|
|
|
|
# Enable OPcache in php.ini
|
|
enable_opcache() {
|
|
local ini_file="$1"
|
|
|
|
# Check current status
|
|
if grep -q "^opcache.enable.*=.*0" "$ini_file" 2>/dev/null; then
|
|
modify_php_ini_setting "$ini_file" "opcache.enable" "1" || return 1
|
|
elif ! grep -q "^opcache.enable" "$ini_file" 2>/dev/null; then
|
|
echo "opcache.enable = 1" >> "$ini_file" || return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Rollback php.ini to backup
|
|
rollback_php_ini() {
|
|
local ini_file="$1"
|
|
local backup_file="$2"
|
|
|
|
if [ -f "$backup_file" ]; then
|
|
cp "$backup_file" "$ini_file" 2>/dev/null && return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# ============================================================================
|
|
# RUN DOMAIN ANALYZER - Collect real usage data
|
|
# ============================================================================
|
|
|
|
run_domain_analyzer() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}DOMAIN PRE-ANALYSIS${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will analyze real usage data from your domains.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}This process will:${NC}"
|
|
cecho " • Scan error logs for memory exhaustion patterns"
|
|
cecho " • Analyze current process memory usage"
|
|
cecho " • Parse traffic logs for peak concurrent requests"
|
|
cecho " • Detect potential memory leaks"
|
|
cecho " • Build accurate domain profiles"
|
|
echo ""
|
|
|
|
if ! confirm "Continue with domain analysis?"; then
|
|
cecho "${YELLOW}Analysis cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check if php-domain-analyzer.sh exists
|
|
local analyzer_script="/root/server-toolkit/modules/performance/php-domain-analyzer.sh"
|
|
if [ ! -f "$analyzer_script" ]; then
|
|
cecho "${RED}ERROR: Domain analyzer script not found${NC}"
|
|
cecho "${YELLOW}Expected: $analyzer_script${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Run the analyzer
|
|
bash "$analyzer_script"
|
|
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ Domain analysis complete!${NC}"
|
|
cecho "${YELLOW}Profiles are ready for optimization.${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# PROFILE-BASED OPTIMIZATION FUNCTIONS (True Optimization)
|
|
# ============================================================================
|
|
|
|
# Get recommendation for memory_limit using actual usage data
|
|
get_memory_limit_recommendation() {
|
|
local domain="$1"
|
|
local username="$2"
|
|
|
|
# Try to get from stored profile first (real data)
|
|
if [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
# Calculate based on ACTUAL memory seen in logs
|
|
local peak_mem_seen=$(echo "$profile" | cut -d'|' -f11)
|
|
|
|
if [ "$peak_mem_seen" -gt 0 ]; then
|
|
# Use observed peak + 20% buffer
|
|
local recommended=$((peak_mem_seen + (peak_mem_seen / 5)))
|
|
[ "$recommended" -gt 1024 ] && recommended=1024
|
|
[ "$recommended" -lt 64 ] && recommended=64
|
|
echo "${recommended}M"
|
|
return 0
|
|
else
|
|
# No memory data detected - use safe default
|
|
echo "128M"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback to old method if no profile
|
|
calculate_optimal_memory_limit "$username" "$domain" "50"
|
|
}
|
|
|
|
# Get recommendation for max_children using actual traffic data
|
|
get_max_children_recommendation() {
|
|
local domain="$1"
|
|
local username="$2"
|
|
|
|
# Try to get from stored profile first (real traffic data)
|
|
if [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
# Use ACTUAL peak concurrent from logs
|
|
local peak_concurrent=$(echo "$profile" | cut -d'|' -f3)
|
|
|
|
if [ "$peak_concurrent" -gt 0 ]; then
|
|
# Add 30% safety margin for spikes
|
|
local recommended=$((peak_concurrent + (peak_concurrent / 3)))
|
|
[ "$recommended" -gt 100 ] && recommended=100
|
|
[ "$recommended" -lt 5 ] && recommended=5
|
|
echo "$recommended"
|
|
return 0
|
|
else
|
|
# No traffic detected - use safe minimum
|
|
echo "5"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback to old method if no profile exists
|
|
local traffic_rpm
|
|
traffic_rpm=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0")
|
|
[ "$traffic_rpm" = "?" ] && traffic_rpm="0"
|
|
local recommended=$((traffic_rpm > 5 ? traffic_rpm + 10 : 5))
|
|
[ "$recommended" -gt 100 ] && recommended=100
|
|
[ "$recommended" -lt 5 ] && recommended=5
|
|
echo "$recommended"
|
|
}
|
|
|
|
# Get recommendation for max_requests using memory leak analysis
|
|
get_max_requests_recommendation() {
|
|
local domain="$1"
|
|
|
|
# Try to get from stored profile first
|
|
if [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
# Check for memory leak pattern
|
|
local leak_type=$(echo "$profile" | cut -d'|' -f12)
|
|
|
|
if [ "$leak_type" = "LIKELY_LEAK" ]; then
|
|
echo "250" # Recycle frequently for leak-prone domains
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Default
|
|
echo "500"
|
|
}
|
|
|
|
# Display profile data if available
|
|
show_profile_analysis() {
|
|
local domain="$1"
|
|
|
|
[ ! -f "/tmp/php-domain-profiles/$domain.profile" ] && return
|
|
|
|
local profile
|
|
profile=$(cat "/tmp/php-domain-profiles/$domain.profile")
|
|
|
|
local peak_concurrent=$(echo "$profile" | cut -d'|' -f3)
|
|
local avg_concurrent=$(echo "$profile" | cut -d'|' -f4)
|
|
local min_mem=$(echo "$profile" | cut -d'|' -f6)
|
|
local max_mem=$(echo "$profile" | cut -d'|' -f7)
|
|
local avg_mem=$(echo "$profile" | cut -d'|' -f8)
|
|
local mem_exhausted=$(echo "$profile" | cut -d'|' -f10)
|
|
local peak_mem=$(echo "$profile" | cut -d'|' -f11)
|
|
local leak_status=$(echo "$profile" | cut -d'|' -f12)
|
|
|
|
cecho "${CYAN}REAL USAGE DATA (from profile analysis):${NC}"
|
|
cecho " Peak Concurrent: ${WHITE}${peak_concurrent}${NC} | Avg: ${avg_concurrent}"
|
|
cecho " Process Memory: ${WHITE}${min_mem}MB${NC}-${max_mem}MB (avg: ${avg_mem}MB)"
|
|
if [ "$mem_exhausted" -gt 0 ]; then
|
|
cecho " Memory Exhaustion Errors: ${RED}${mem_exhausted}${NC}"
|
|
fi
|
|
cecho " Peak Memory Seen: ${WHITE}${peak_mem}MB${NC}"
|
|
cecho " Memory Status: ${leak_status}"
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZE ALL DOMAINS (SERVER-WIDE) - TIERED OPTIMIZATION
|
|
# ============================================================================
|
|
|
|
optimize_all_domains() {
|
|
show_banner
|
|
|
|
cecho "${WHITE}${BOLD}SERVER-WIDE OPTIMIZATION MENU${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Choose optimization level for all domains on the server${NC}"
|
|
echo ""
|
|
|
|
cecho "${CYAN}PRE-ANALYSIS (Recommended for accurate optimization):${NC}"
|
|
cecho " ${GREEN}0${NC}) Pre-analyze domains"
|
|
cecho " └─ Collect real usage data for data-driven optimization"
|
|
echo ""
|
|
|
|
cecho "${CYAN}OPTIMIZATION LEVELS:${NC}"
|
|
cecho " ${GREEN}1${NC}) Optimize pm.max_children only"
|
|
cecho " └─ Adjust process limits based on traffic"
|
|
echo ""
|
|
cecho " ${GREEN}2${NC}) Optimize pm.max_children + memory_limit"
|
|
cecho " └─ Add PHP memory settings for each domain"
|
|
echo ""
|
|
cecho " ${GREEN}3${NC}) Optimize max_children + memory_limit + pm.max_requests"
|
|
cecho " └─ Add process recycling to prevent memory leaks"
|
|
echo ""
|
|
cecho " ${GREEN}4${NC}) Optimize OPcache only"
|
|
cecho " └─ Enable/increase OPcache for performance"
|
|
echo ""
|
|
cecho " ${GREEN}5${NC}) Optimize EVERYTHING"
|
|
cecho " └─ All of the above (safest, most thorough)"
|
|
echo ""
|
|
cecho " ${RED}b${NC}) Back to main menu"
|
|
cecho " ${RED}q${NC}) Cancel"
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
while true; do
|
|
read -p "Select option (0-5, b, or q): " opt_choice
|
|
opt_choice=${opt_choice,,}
|
|
|
|
if [[ "$opt_choice" =~ ^[0-5bq]$ ]]; then
|
|
break
|
|
fi
|
|
echo ""
|
|
cecho "${RED}Invalid choice. Please enter 0-5, b, or q${NC}"
|
|
echo ""
|
|
done
|
|
|
|
case "$opt_choice" in
|
|
0)
|
|
run_domain_analyzer
|
|
;;
|
|
1)
|
|
optimize_level_1_max_children
|
|
;;
|
|
2)
|
|
optimize_level_2_memory
|
|
;;
|
|
3)
|
|
optimize_level_3_advanced
|
|
;;
|
|
4)
|
|
optimize_level_4_opcache
|
|
;;
|
|
5)
|
|
optimize_level_5_everything
|
|
;;
|
|
b)
|
|
return
|
|
;;
|
|
q)
|
|
cecho "${YELLOW}Optimization cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 1: max_children ONLY
|
|
# ============================================================================
|
|
|
|
optimize_level_1_max_children() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 1: Optimize pm.max_children (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will adjust pm.max_children based on available memory and traffic.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will happen:${NC}"
|
|
cecho " • Analyze memory capacity across all domains"
|
|
cecho " • Calculate optimal max_children per domain"
|
|
cecho " • Apply changes and validate configuration"
|
|
cecho " • Restart PHP-FPM if changes needed"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This will modify PHP-FPM pool configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server capacity
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Checking server memory capacity...${NC}"
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo ""
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data from domain analysis${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
# Calculate server capacity for intelligent three-constraint model
|
|
local server_capacity_result
|
|
server_capacity_result=$(calculate_server_capacity "$total_ram_mb")
|
|
local server_capacity
|
|
server_capacity=$(echo "$server_capacity_result" | cut -d'|' -f1)
|
|
local server_memory_per_process
|
|
server_memory_per_process=$(echo "$server_capacity_result" | cut -d'|' -f3)
|
|
cecho " Server PHP-FPM capacity: ${WHITE}${server_capacity}${NC} max_children"
|
|
cecho " Using intelligent three-constraint model: Memory + Traffic + Fair Share"
|
|
echo ""
|
|
|
|
# Get recommendations
|
|
cecho "${CYAN}Step 2: Calculating optimal settings...${NC}"
|
|
echo ""
|
|
|
|
declare -A recommended_values
|
|
declare -A domain_to_username
|
|
local changes_needed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
# CRITICAL FIX: Build list of ALL domains on server FIRST
|
|
# This is needed for accurate traffic percentage calculation
|
|
all_domains_string=""
|
|
while IFS= read -r u; do
|
|
[ -z "$u" ] && continue
|
|
u_domains=$(get_user_domains "$u")
|
|
while IFS= read -r d; do
|
|
[ -z "$d" ] && continue
|
|
all_domains_string="$all_domains_string$d"$'\n'
|
|
done <<< "$u_domains"
|
|
done <<< "$users"
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
# Get recommendations - use profile if available, otherwise use intelligent three-constraint model
|
|
local recommended_max
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_max=$(get_max_children_recommendation "$domain" "$username")
|
|
else
|
|
# Use intelligent three-constraint model (same as Level 5)
|
|
local traffic_pct
|
|
traffic_pct=$(get_domain_traffic_percentage "$username" "$domain" "$all_domains_string" 2>/dev/null | cut -d'|' -f1)
|
|
traffic_pct=${traffic_pct:-50}
|
|
|
|
# Call intelligent three-constraint function
|
|
local intel_result
|
|
intel_result=$(calculate_optimal_php_settings_intelligent "$username" "$total_ram_mb" "$server_capacity" "$traffic_pct" 2>/dev/null || echo "20|dynamic|1|5|ERROR|Failed")
|
|
|
|
recommended_max=$(echo "$intel_result" | cut -d'|' -f1)
|
|
fi
|
|
|
|
recommended_values["$domain"]="$recommended_max"
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$recommended_max" ]; then
|
|
changes_needed=$((changes_needed + 1))
|
|
cecho " ${GREEN}✓${NC} $domain: $current_max → ${GREEN}$recommended_max${NC}"
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# CRITICAL VALIDATION: Check if combined recommendations exceed safe limits
|
|
cecho "${CYAN}Step 2b: Validating capacity...${NC}"
|
|
echo ""
|
|
|
|
local total_recommended_max_children=0
|
|
local avg_memory_per_process=$server_memory_per_process
|
|
local total_recommended_memory=0
|
|
|
|
for domain in "${!recommended_values[@]}"; do
|
|
local rec_max="${recommended_values[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
total_recommended_max_children=$((total_recommended_max_children + rec_max))
|
|
total_recommended_memory=$((total_recommended_memory + (rec_max * avg_memory_per_process)))
|
|
done
|
|
|
|
# Determine if recommendations are safe
|
|
local max_safe_php_fpm=$((total_ram_mb * 60 / 100))
|
|
|
|
if [ "$total_recommended_memory" -gt "$max_safe_php_fpm" ]; then
|
|
cecho "${RED}${BOLD}⚠ WARNING: Combined recommendations exceed safe limits!${NC}"
|
|
cecho "${RED}Recommended total: ${total_recommended_memory}MB (${total_recommended_max_children} max_children combined)${NC}"
|
|
cecho "${RED}Safe maximum: ${max_safe_php_fpm}MB${NC}"
|
|
cecho "${YELLOW}Applying safety caps to prevent OOM crashes...${NC}"
|
|
echo ""
|
|
|
|
# Scale down all recommendations proportionally
|
|
local scale_factor
|
|
scale_factor=$((max_safe_php_fpm * 100 / total_recommended_memory))
|
|
|
|
for domain in "${!recommended_values[@]}"; do
|
|
local rec_max="${recommended_values[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
rec_max=$((rec_max * scale_factor / 100))
|
|
[ "$rec_max" -lt 5 ] && rec_max=5
|
|
recommended_values["$domain"]="$rec_max"
|
|
done
|
|
cecho "${CYAN}Applied proportional scaling (${scale_factor}%)${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
if [ "$changes_needed" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All domains already optimized - no changes needed${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Total changes needed: ${YELLOW}${BOLD}$changes_needed${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Apply these $changes_needed changes?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Apply changes
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Applying optimizations...${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended="${recommended_values[$domain]}"
|
|
[ -z "$recommended" ] && continue
|
|
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
|
|
continue
|
|
fi
|
|
|
|
local current
|
|
current=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ "$current" != "$recommended" ]; then
|
|
# Create backup
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
# Apply change
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: $current → $recommended"
|
|
optimized=$((optimized + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} $domain: Failed to apply"
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}SUMMARY${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Applied: ${GREEN}${optimized}${NC} changes"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC} changes"
|
|
fi
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 2: max_children + memory_limit
|
|
# ============================================================================
|
|
|
|
optimize_level_2_memory() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 2: Optimize pm.max_children + memory_limit (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will adjust both process limits and PHP memory settings.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will be optimized:${NC}"
|
|
cecho " • pm.max_children (based on real traffic data if available)"
|
|
cecho " • memory_limit (PHP memory per domain)"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This will modify PHP-FPM and php.ini configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server capacity
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Checking server memory capacity...${NC}"
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo ""
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# Calculate server capacity for intelligent three-constraint model
|
|
local server_capacity_result
|
|
server_capacity_result=$(calculate_server_capacity "$total_ram_mb")
|
|
local server_capacity
|
|
server_capacity=$(echo "$server_capacity_result" | cut -d'|' -f1)
|
|
local server_memory_per_process
|
|
server_memory_per_process=$(echo "$server_capacity_result" | cut -d'|' -f3)
|
|
cecho " Server PHP-FPM capacity: ${WHITE}${server_capacity}${NC} max_children"
|
|
cecho " Using intelligent three-constraint model: Memory + Traffic + Fair Share"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data from domain analysis${NC}"
|
|
echo ""
|
|
else
|
|
cecho "${YELLOW}${BOLD}⚠ No domain profiles found${NC}"
|
|
cecho "${CYAN}For more accurate optimization, run pre-analysis first:${NC}"
|
|
cecho " ${WHITE}php-optimizer.sh${NC} → Option 0 (Pre-analyze domains)"
|
|
echo ""
|
|
fi
|
|
|
|
# Get recommendations
|
|
cecho "${CYAN}Step 2: Calculating optimal settings...${NC}"
|
|
echo ""
|
|
|
|
declare -A recommended_max_children
|
|
declare -A recommended_memory_limit
|
|
declare -A domain_to_username
|
|
local changes_needed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
# CRITICAL FIX: Build list of ALL domains on server FIRST
|
|
# This is needed for accurate traffic percentage calculation
|
|
all_domains_string=""
|
|
while IFS= read -r u; do
|
|
[ -z "$u" ] && continue
|
|
u_domains=$(get_user_domains "$u")
|
|
while IFS= read -r d; do
|
|
[ -z "$d" ] && continue
|
|
all_domains_string="$all_domains_string$d"$'\n'
|
|
done <<< "$u_domains"
|
|
done <<< "$users"
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
# Get recommendations - use profile if available, otherwise use intelligent three-constraint model
|
|
local recommended_max
|
|
local recommended_memory
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_max=$(get_max_children_recommendation "$domain" "$username")
|
|
recommended_memory=$(get_memory_limit_recommendation "$domain" "$username")
|
|
else
|
|
# Use intelligent three-constraint model (same as Level 5)
|
|
local traffic_pct
|
|
traffic_pct=$(get_domain_traffic_percentage "$username" "$domain" "$all_domains_string" 2>/dev/null | cut -d'|' -f1)
|
|
traffic_pct=${traffic_pct:-50}
|
|
|
|
# Call intelligent three-constraint function
|
|
local intel_result
|
|
intel_result=$(calculate_optimal_php_settings_intelligent "$username" "$total_ram_mb" "$server_capacity" "$traffic_pct" 2>/dev/null || echo "20|dynamic|1|5|ERROR|Failed")
|
|
|
|
recommended_max=$(echo "$intel_result" | cut -d'|' -f1)
|
|
recommended_memory=$(calculate_optimal_memory_limit "$username" "$domain" "$recommended_max" 2>/dev/null || echo "128M")
|
|
fi
|
|
|
|
recommended_max_children["$domain"]="$recommended_max"
|
|
recommended_memory_limit["$domain"]="$recommended_memory"
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$recommended_max" ]; then
|
|
changes_needed=$((changes_needed + 1))
|
|
cecho " ${GREEN}✓${NC} $domain:"
|
|
cecho " pm.max_children: $current_max → ${GREEN}$recommended_max${NC}"
|
|
cecho " memory_limit: → ${GREEN}$recommended_memory${NC}"
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# CRITICAL VALIDATION: Check if combined recommendations exceed safe limits
|
|
cecho "${CYAN}Step 2b: Validating capacity...${NC}"
|
|
echo ""
|
|
|
|
local total_recommended_max_children=0
|
|
local avg_memory_per_process=$server_memory_per_process
|
|
local total_recommended_memory=0
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local rec_max="${recommended_max_children[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
total_recommended_max_children=$((total_recommended_max_children + rec_max))
|
|
total_recommended_memory=$((total_recommended_memory + (rec_max * avg_memory_per_process)))
|
|
done
|
|
|
|
# Determine if recommendations are safe
|
|
local max_safe_php_fpm=$((total_ram_mb * 60 / 100))
|
|
|
|
if [ "$total_recommended_memory" -gt "$max_safe_php_fpm" ]; then
|
|
cecho "${RED}${BOLD}⚠ WARNING: Combined recommendations exceed safe limits!${NC}"
|
|
cecho "${RED}Recommended total: ${total_recommended_memory}MB (${total_recommended_max_children} max_children combined)${NC}"
|
|
cecho "${RED}Safe maximum: ${max_safe_php_fpm}MB${NC}"
|
|
cecho "${YELLOW}Applying safety caps to prevent OOM crashes...${NC}"
|
|
echo ""
|
|
|
|
# Scale down all recommendations proportionally
|
|
local scale_factor
|
|
scale_factor=$((max_safe_php_fpm * 100 / total_recommended_memory))
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local rec_max="${recommended_max_children[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
rec_max=$((rec_max * scale_factor / 100))
|
|
[ "$rec_max" -lt 5 ] && rec_max=5
|
|
recommended_max_children["$domain"]="$rec_max"
|
|
done
|
|
cecho "${CYAN}Applied proportional scaling (${scale_factor}%)${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
if [ "$changes_needed" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All domains already optimized${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Total domains to optimize: ${YELLOW}${BOLD}$changes_needed${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Apply these changes?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Apply changes
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Applying Level 2 Optimizations...${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
declare -a changes_log
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended_max="${recommended_max_children[$domain]}"
|
|
local recommended_mem="${recommended_memory_limit[$domain]}"
|
|
|
|
[ -z "$recommended_max" ] && continue
|
|
|
|
# 1. Optimize pm.max_children (FPM pool config)
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
local current_max
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ "$current_max" != "$recommended_max" ]; then
|
|
# Backup FPM config
|
|
cp "$pool_config" "$pool_config.backup.$$" 2>/dev/null
|
|
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: pm.max_children → $recommended_max"
|
|
changes_log+=("$domain: pm.max_children $current_max→$recommended_max")
|
|
optimized=$((optimized + 1))
|
|
else
|
|
cecho " ${RED}✗${NC} $domain: Failed to update pm.max_children"
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# 2. Optimize memory_limit (php.ini files)
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
# Backup ini file
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
if modify_php_ini_setting "$ini_file" "memory_limit" "$recommended_mem" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: memory_limit → $recommended_mem ($ini_file)"
|
|
changes_log+=("$domain: memory_limit→$recommended_mem")
|
|
else
|
|
cecho " ${YELLOW}⚠${NC} $domain: PHP syntax error in $ini_file, rolling back"
|
|
rollback_php_ini "$ini_file" "$ini_file.backup.$$"
|
|
fi
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM services...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 2 OPTIMIZATION SUMMARY${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Optimizations applied: ${GREEN}${optimized}${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ "${#changes_log[@]}" -gt 0 ]; then
|
|
cecho "${WHITE}${BOLD}Changes Applied:${NC}"
|
|
for change in "${changes_log[@]}"; do
|
|
cecho " • $change"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 3: max_children + max_requests + memory_limit
|
|
# ============================================================================
|
|
|
|
optimize_level_3_advanced() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 3: Advanced Optimization (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will optimize multiple PHP-FPM settings for comprehensive improvement.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will be optimized:${NC}"
|
|
cecho " • pm.max_children (process limits)"
|
|
cecho " • pm.max_requests (memory leak prevention)"
|
|
cecho " • memory_limit (PHP memory allocation)"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This will modify PHP-FPM and php.ini configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Check server capacity
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Checking server memory capacity...${NC}"
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
echo ""
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# Calculate server capacity for intelligent three-constraint model
|
|
local server_capacity_result
|
|
server_capacity_result=$(calculate_server_capacity "$total_ram_mb")
|
|
local server_capacity
|
|
server_capacity=$(echo "$server_capacity_result" | cut -d'|' -f1)
|
|
local server_memory_per_process
|
|
server_memory_per_process=$(echo "$server_capacity_result" | cut -d'|' -f3)
|
|
cecho " Server PHP-FPM capacity: ${WHITE}${server_capacity}${NC} max_children"
|
|
cecho " Using intelligent three-constraint model: Memory + Traffic + Fair Share"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data and memory leak detection${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
# Get recommendations
|
|
cecho "${CYAN}Step 2: Calculating optimal settings...${NC}"
|
|
echo ""
|
|
|
|
declare -A recommended_max_children
|
|
declare -A recommended_memory_limit
|
|
declare -A recommended_max_requests
|
|
declare -A domain_to_username
|
|
local changes_needed=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
# CRITICAL FIX: Build list of ALL domains on server FIRST
|
|
# This is needed for accurate traffic percentage calculation
|
|
all_domains_string=""
|
|
while IFS= read -r u; do
|
|
[ -z "$u" ] && continue
|
|
u_domains=$(get_user_domains "$u")
|
|
while IFS= read -r d; do
|
|
[ -z "$d" ] && continue
|
|
all_domains_string="$all_domains_string$d"$'\n'
|
|
done <<< "$u_domains"
|
|
done <<< "$users"
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
recommended_max_children["$domain"]=0
|
|
recommended_memory_limit["$domain"]=0
|
|
recommended_max_requests["$domain"]=0
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
# Use profile-based if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
recommended_max_children["$domain"]=$(get_max_children_recommendation "$domain" "$username")
|
|
recommended_memory_limit["$domain"]=$(get_memory_limit_recommendation "$domain" "$username")
|
|
recommended_max_requests["$domain"]=$(get_max_requests_recommendation "$domain")
|
|
else
|
|
# Use intelligent three-constraint model (same as Level 5)
|
|
local traffic_pct
|
|
traffic_pct=$(get_domain_traffic_percentage "$username" "$domain" "$all_domains_string" 2>/dev/null | cut -d'|' -f1)
|
|
traffic_pct=${traffic_pct:-50}
|
|
|
|
# Call intelligent three-constraint function
|
|
local intel_result
|
|
intel_result=$(calculate_optimal_php_settings_intelligent "$username" "$total_ram_mb" "$server_capacity" "$traffic_pct" 2>/dev/null || echo "20|dynamic|1|5|ERROR|Failed")
|
|
|
|
recommended_max_children["$domain"]=$(echo "$intel_result" | cut -d'|' -f1)
|
|
recommended_memory_limit["$domain"]=$(calculate_optimal_memory_limit "$username" "$domain" "$recommended_max_children[$domain]" 2>/dev/null || echo "128M")
|
|
recommended_max_requests["$domain"]=$(calculate_optimal_max_requests "$recommended_max_children[$domain]" 2>/dev/null || echo "0")
|
|
fi
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
local rec_max_children=${recommended_max_children["$domain"]}
|
|
local rec_memory=${recommended_memory_limit["$domain"]}
|
|
local rec_requests=${recommended_max_requests["$domain"]}
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$rec_max_children" ]; then
|
|
changes_needed=$((changes_needed + 1))
|
|
cecho " ${GREEN}✓${NC} $domain:"
|
|
cecho " pm.max_children: $current_max → ${GREEN}$rec_max_children${NC}"
|
|
cecho " memory_limit: → ${GREEN}$rec_memory${NC}"
|
|
cecho " pm.max_requests: → ${GREEN}$rec_requests${NC} (prevent memory leaks)"
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# CRITICAL VALIDATION: Check if combined recommendations exceed safe limits
|
|
cecho "${CYAN}Step 2b: Validating capacity...${NC}"
|
|
echo ""
|
|
|
|
local total_recommended_max_children=0
|
|
local avg_memory_per_process=$server_memory_per_process
|
|
local total_recommended_memory=0
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local rec_max="${recommended_max_children[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
total_recommended_max_children=$((total_recommended_max_children + rec_max))
|
|
total_recommended_memory=$((total_recommended_memory + (rec_max * avg_memory_per_process)))
|
|
done
|
|
|
|
# Determine if recommendations are safe
|
|
local max_safe_php_fpm=$((total_ram_mb * 60 / 100))
|
|
|
|
if [ "$total_recommended_memory" -gt "$max_safe_php_fpm" ]; then
|
|
cecho "${RED}${BOLD}⚠ WARNING: Combined recommendations exceed safe limits!${NC}"
|
|
cecho "${RED}Recommended total: ${total_recommended_memory}MB (${total_recommended_max_children} max_children combined)${NC}"
|
|
cecho "${RED}Safe maximum: ${max_safe_php_fpm}MB${NC}"
|
|
cecho "${YELLOW}Applying safety caps to prevent OOM crashes...${NC}"
|
|
echo ""
|
|
|
|
# Scale down all recommendations proportionally
|
|
local scale_factor
|
|
scale_factor=$((max_safe_php_fpm * 100 / total_recommended_memory))
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local rec_max="${recommended_max_children[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
rec_max=$((rec_max * scale_factor / 100))
|
|
[ "$rec_max" -lt 5 ] && rec_max=5
|
|
recommended_max_children["$domain"]="$rec_max"
|
|
done
|
|
cecho "${CYAN}Applied proportional scaling (${scale_factor}%)${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
if [ "$changes_needed" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ All domains already optimized${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Total domains to optimize: ${YELLOW}${BOLD}$changes_needed${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Apply these changes?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Apply changes
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Applying Level 3 Optimizations (Advanced)...${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
declare -a changes_log
|
|
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended_max="${recommended_max_children[$domain]}"
|
|
local recommended_mem="${recommended_memory_limit[$domain]}"
|
|
local recommended_requests="${recommended_max_requests[$domain]}"
|
|
|
|
[ -z "$recommended_max" ] && continue
|
|
|
|
# 1. Optimize pm.max_children
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
local current_max
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
# Backup
|
|
cp "$pool_config" "$pool_config.backup.$$" 2>/dev/null
|
|
|
|
local pool_updated=0
|
|
|
|
# Update pm.max_children
|
|
if [ "$current_max" != "$recommended_max" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: pm.max_children → $recommended_max"
|
|
changes_log+=("$domain: pm.max_children $current_max→$recommended_max")
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
fi
|
|
|
|
# Update pm.max_requests
|
|
if [ -n "$recommended_requests" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_requests" "$recommended_requests" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: pm.max_requests → $recommended_requests"
|
|
changes_log+=("$domain: pm.max_requests→$recommended_requests")
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
fi
|
|
|
|
if [ "$pool_updated" -gt 0 ]; then
|
|
optimized=$((optimized + 1))
|
|
fi
|
|
fi
|
|
|
|
# 2. Optimize memory_limit
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
if modify_php_ini_setting "$ini_file" "memory_limit" "$recommended_mem" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: memory_limit → $recommended_mem"
|
|
changes_log+=("$domain: memory_limit→$recommended_mem")
|
|
else
|
|
cecho " ${YELLOW}⚠${NC} Rolling back $ini_file"
|
|
rollback_php_ini "$ini_file" "$ini_file.backup.$$"
|
|
fi
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM services...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 3 OPTIMIZATION SUMMARY (Advanced)${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Optimizations applied: ${GREEN}${optimized}${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ "${#changes_log[@]}" -gt 0 ]; then
|
|
cecho "${WHITE}${BOLD}Changes Applied:${NC}"
|
|
for change in "${changes_log[@]}"; do
|
|
cecho " • $change"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 4: OPcache ONLY
|
|
# ============================================================================
|
|
|
|
optimize_level_4_opcache() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 4: Optimize OPcache (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will enable/optimize OPcache for all domains.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will happen:${NC}"
|
|
cecho " • Enable OPcache if disabled"
|
|
cecho " • Adjust memory_consumption based on needs"
|
|
cecho " • Optimize hit rate and performance"
|
|
echo ""
|
|
|
|
if ! confirm "Continue?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Analyze current OPcache status
|
|
show_banner
|
|
cecho "${CYAN}Step 1: Analyzing current OPcache status...${NC}"
|
|
echo ""
|
|
|
|
declare -A opcache_enabled
|
|
declare -A opcache_needs_enable
|
|
local needs_enable_count=0
|
|
local already_enabled=0
|
|
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
if is_opcache_enabled "$username"; then
|
|
opcache_enabled["$domain"]="1"
|
|
already_enabled=$((already_enabled + 1))
|
|
else
|
|
opcache_needs_enable["$domain"]="1"
|
|
needs_enable_count=$((needs_enable_count + 1))
|
|
cecho " ${YELLOW}⚠${NC} $domain: OPcache is disabled"
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
echo ""
|
|
cecho " Domains with OPcache enabled: ${GREEN}${already_enabled}${NC}"
|
|
cecho " Domains needing OPcache: ${YELLOW}${needs_enable_count}${NC}"
|
|
echo ""
|
|
|
|
if [ "$needs_enable_count" -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ OPcache is already enabled on all domains${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
if ! confirm "Enable OPcache on $needs_enable_count domain(s)?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Enable OPcache
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}Enabling OPcache...${NC}"
|
|
echo ""
|
|
|
|
local enabled=0
|
|
local failed=0
|
|
declare -a changes_log
|
|
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
[ "${opcache_needs_enable[$domain]}" != "1" ] && continue
|
|
|
|
# Find php.ini files for this domain
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
# Backup
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
# Enable OPcache
|
|
if enable_opcache "$ini_file" >/dev/null 2>&1; then
|
|
# Calculate optimal memory for OPcache (safe limit: 256MB max)
|
|
local avg_rpm
|
|
avg_rpm=$(calculate_avg_requests_per_minute "$username" 24 | cut -d'|' -f1)
|
|
local optimal_memory
|
|
# Pass 256MB as max available (OPcache is global and shared across all domains)
|
|
optimal_memory=$(calculate_optimal_opcache_memory "$avg_rpm" "256")
|
|
|
|
# Set memory_consumption
|
|
if modify_php_ini_setting "$ini_file" "opcache.memory_consumption" "$optimal_memory" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: OPcache enabled (memory: $optimal_memory)"
|
|
changes_log+=("$domain: OPcache enabled with $optimal_memory")
|
|
enabled=$((enabled + 1))
|
|
else
|
|
cecho " ${YELLOW}⚠${NC} OPcache PHP error in $ini_file, rolling back"
|
|
rollback_php_ini "$ini_file" "$ini_file.backup.$$"
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
else
|
|
cecho " ${RED}✗${NC} $domain: Failed to enable OPcache"
|
|
failed=$((failed + 1))
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}Restarting PHP-FPM services...${NC}"
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 4 OPTIMIZATION SUMMARY (OPcache)${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " OPcache enabled: ${GREEN}${enabled}${NC}"
|
|
if [ "$failed" -gt 0 ]; then
|
|
cecho " Failed: ${RED}${failed}${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
if [ "${#changes_log[@]}" -gt 0 ]; then
|
|
cecho "${WHITE}${BOLD}Changes Applied:${NC}"
|
|
for change in "${changes_log[@]}"; do
|
|
cecho " • $change"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTIMIZATION LEVEL 5: EVERYTHING
|
|
# ============================================================================
|
|
|
|
optimize_level_5_everything() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 5: OPTIMIZE EVERYTHING (All Domains)${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This will apply all optimizations comprehensively.${NC}"
|
|
echo ""
|
|
cecho "${CYAN}What will be optimized:${NC}"
|
|
cecho " • pm.max_children (process limits)"
|
|
cecho " • pm.mode (STATIC/DYNAMIC/ONDEMAND)"
|
|
cecho " • pm.min_spare_servers & pm.max_spare_servers"
|
|
cecho " • pm.max_requests (memory leak prevention)"
|
|
cecho " • memory_limit (PHP memory allocation)"
|
|
cecho " • OPcache (enable & optimize)"
|
|
echo ""
|
|
cecho "${RED}${BOLD}WARNING:${NC} ${RED}This is the most comprehensive optimization!${NC}"
|
|
cecho "${YELLOW}This will take several minutes to complete.${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Continue with FULL optimization?"; then
|
|
cecho "${YELLOW}Operation cancelled${NC}"
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Run all levels in sequence
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}LEVEL 5: Complete Optimization Starting...${NC}"
|
|
echo ""
|
|
|
|
# Note: We'll run the core logic here instead of calling the functions
|
|
# to provide unified progress display
|
|
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}STEP 1: Analyzing Server Capacity${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
local capacity_result
|
|
capacity_result=$(calculate_server_memory_capacity 2>&1)
|
|
local total_required_mb total_ram_mb percentage status
|
|
total_required_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f1)
|
|
total_ram_mb=$(echo "$capacity_result" | head -1 | cut -d'|' -f2)
|
|
percentage=$(echo "$capacity_result" | head -1 | cut -d'|' -f3)
|
|
status=$(echo "$capacity_result" | head -1 | cut -d'|' -f4)
|
|
|
|
cecho " Total RAM: ${WHITE}${total_ram_mb}MB${NC}"
|
|
cecho " Current FPM capacity: ${WHITE}${total_required_mb}MB${NC} (${percentage}%)"
|
|
cecho " Status: ${WHITE}${status}${NC}"
|
|
echo ""
|
|
|
|
# CRITICAL SAFETY CHECK: If current usage is already > 80%, warn user
|
|
if [ "$percentage" -gt 80 ]; then
|
|
cecho "${RED}${BOLD}⚠ CRITICAL: Server is running with insufficient PHP-FPM headroom!${NC}"
|
|
cecho "${RED}Current allocation is ${percentage}% of RAM.${NC}"
|
|
cecho "${RED}Optimization may not be sufficient - consider:${NC}"
|
|
cecho "${RED} 1. Upgrading server RAM${NC}"
|
|
cecho "${RED} 2. Disabling problematic domains${NC}"
|
|
cecho "${RED} 3. Migrating heavy sites to dedicated servers${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
cecho "${CYAN}STEP 2: Calculating Intelligent Recommendations${NC}"
|
|
echo ""
|
|
|
|
# Calculate server capacity for fair share allocation
|
|
local server_capacity_result
|
|
server_capacity_result=$(calculate_server_capacity "$TOTAL_RAM_MB")
|
|
local server_capacity
|
|
server_capacity=$(echo "$server_capacity_result" | cut -d'|' -f1)
|
|
local server_memory_per_process
|
|
server_memory_per_process=$(echo "$server_capacity_result" | cut -d'|' -f3)
|
|
cecho " Server PHP-FPM capacity: ${WHITE}${server_capacity}${NC} max_children"
|
|
cecho " Using three-constraint model: Memory + Traffic + Fair Share"
|
|
echo ""
|
|
|
|
# Check if profiles exist
|
|
local profiles_exist=0
|
|
if [ -d "/tmp/php-domain-profiles" ] && [ "$(ls -1 /tmp/php-domain-profiles/*.profile 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
profiles_exist=1
|
|
cecho "${GREEN}${BOLD}✓ Domain profiles detected!${NC}"
|
|
cecho "${YELLOW}Using REAL usage data for comprehensive optimization${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
declare -A recommended_max_children
|
|
declare -A recommended_memory_limit
|
|
declare -A recommended_max_requests
|
|
declare -A domain_to_username
|
|
declare -A opcache_needs_enable
|
|
local changes_count=0
|
|
local opcache_count=0
|
|
|
|
# Get all users and domains
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
# CRITICAL FIX: Build list of ALL domains on server FIRST
|
|
# This is needed for accurate traffic percentage calculation (same as batch analyzer)
|
|
all_domains_string=""
|
|
while IFS= read -r u; do
|
|
[ -z "$u" ] && continue
|
|
u_domains=$(get_user_domains "$u")
|
|
while IFS= read -r d; do
|
|
[ -z "$d" ] && continue
|
|
all_domains_string="$all_domains_string$d"$'\n'
|
|
done <<< "$u_domains"
|
|
done <<< "$users"
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local current_max
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
[ -z "$current_max" ] && current_max="?"
|
|
else
|
|
current_max="?"
|
|
fi
|
|
|
|
# Get recommendations using THREE-CONSTRAINT INTELLIGENT MODEL
|
|
local recommended_max
|
|
local recommended_memory
|
|
local recommended_requests
|
|
local traffic_pct=50 # Default if no data
|
|
|
|
# Get traffic percentage for this domain
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
# Use profile data if available
|
|
recommended_max=$(get_max_children_recommendation "$domain" "$username")
|
|
recommended_memory=$(get_memory_limit_recommendation "$domain" "$username")
|
|
recommended_requests=$(get_max_requests_recommendation "$domain")
|
|
else
|
|
# Use intelligent three-constraint model
|
|
# CRITICAL FIX: Pass ALL server domains, not just user's domains!
|
|
traffic_pct=$(get_domain_traffic_percentage "$username" "$domain" "$all_domains_string" 2>/dev/null | cut -d'|' -f1)
|
|
traffic_pct=${traffic_pct:-50}
|
|
|
|
# Call intelligent three-constraint function
|
|
local intel_result
|
|
intel_result=$(calculate_optimal_php_settings_intelligent "$username" "$TOTAL_RAM_MB" "$server_capacity" "$traffic_pct" 2>/dev/null || echo "20|dynamic|1|5|ERROR|Failed")
|
|
|
|
recommended_max=$(echo "$intel_result" | cut -d'|' -f1)
|
|
recommended_memory=$(calculate_optimal_memory_limit "$username" "$domain" "$recommended_max" 2>/dev/null || echo "128M")
|
|
recommended_requests=$(calculate_optimal_max_requests "$recommended_max" 2>/dev/null || echo "0")
|
|
fi
|
|
|
|
recommended_max_children["$domain"]="$recommended_max"
|
|
recommended_memory_limit["$domain"]="$recommended_memory"
|
|
recommended_max_requests["$domain"]="$recommended_requests"
|
|
domain_to_username["$domain"]="$username"
|
|
|
|
if [ "$current_max" != "?" ] && [ "$current_max" != "$recommended_max" ]; then
|
|
changes_count=$((changes_count + 1))
|
|
fi
|
|
|
|
if ! is_opcache_enabled "$username"; then
|
|
opcache_needs_enable["$domain"]="1"
|
|
opcache_count=$((opcache_count + 1))
|
|
fi
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
cecho " Domains needing updates: ${YELLOW}${changes_count}${NC}"
|
|
cecho " Domains needing OPcache: ${YELLOW}${opcache_count}${NC}"
|
|
echo ""
|
|
|
|
# CRITICAL VALIDATION: Check if combined recommendations exceed safe limits
|
|
cecho "${CYAN}STEP 2b: Validating Combined Capacity${NC}"
|
|
echo ""
|
|
|
|
local total_recommended_max_children=0
|
|
local avg_memory_per_process=$server_memory_per_process # Use actual measured memory per process
|
|
local total_recommended_memory=0
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local rec_max="${recommended_max_children[$domain]}"
|
|
[ -z "$rec_max" ] && continue
|
|
total_recommended_max_children=$((total_recommended_max_children + rec_max))
|
|
total_recommended_memory=$((total_recommended_memory + (rec_max * avg_memory_per_process)))
|
|
done
|
|
|
|
# Determine if recommendations are safe
|
|
# Reserve up to 256MB for OPcache (will be allocated separately)
|
|
local max_opcache_reserved=256
|
|
local max_safe_php_fpm=$((total_ram_mb * 60 / 100 - max_opcache_reserved)) # 60% of RAM minus OPcache reserve
|
|
|
|
if [ "$total_recommended_memory" -gt "$max_safe_php_fpm" ]; then
|
|
cecho "${RED}${BOLD}⚠ WARNING: Combined recommendations exceed safe limits!${NC}"
|
|
cecho "${RED}Recommended total: ${total_recommended_memory}MB (${total_recommended_max_children} max_children combined)${NC}"
|
|
cecho "${RED}Safe maximum: ${max_safe_php_fpm}MB${NC}"
|
|
cecho "${YELLOW}Applying safety caps to prevent OOM crashes...${NC}"
|
|
|
|
# Scale down all recommendations proportionally
|
|
local scale_factor=$((max_safe_php_fpm * 100 / total_recommended_memory))
|
|
scale_factor=$((scale_factor / 100)) # Convert to percentage
|
|
|
|
for domain in "${!recommended_max_children[@]}"; do
|
|
local old_max="${recommended_max_children[$domain]}"
|
|
[ -z "$old_max" ] && continue
|
|
local new_max=$((old_max * scale_factor / 100))
|
|
[ "$new_max" -lt 5 ] && new_max=5
|
|
[ "$new_max" -gt 150 ] && new_max=150 # Hard cap for shared hosting
|
|
recommended_max_children["$domain"]="$new_max"
|
|
done
|
|
|
|
echo ""
|
|
cecho "${GREEN}✓ Safety caps applied${NC}"
|
|
else
|
|
cecho "${GREEN}✓ Combined capacity is safe: ${total_recommended_memory}MB (${total_recommended_max_children} total max_children)${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
cecho "${CYAN}STEP 3: Applying All Optimizations${NC}"
|
|
echo ""
|
|
|
|
local optimized=0
|
|
local failed=0
|
|
local opcache_enabled=0
|
|
declare -a changes_log
|
|
|
|
local users
|
|
users=$(list_all_users)
|
|
|
|
while IFS= read -r username; do
|
|
[ -z "$username" ] && continue
|
|
local user_domains
|
|
user_domains=$(get_user_domains "$username")
|
|
|
|
while IFS= read -r domain; do
|
|
[ -z "$domain" ] && continue
|
|
|
|
local recommended_max="${recommended_max_children[$domain]}"
|
|
local recommended_mem="${recommended_memory_limit[$domain]}"
|
|
local recommended_requests="${recommended_max_requests[$domain]}"
|
|
|
|
[ -z "$recommended_max" ] && continue
|
|
|
|
# Get pool config
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
cp "$pool_config" "$pool_config.backup.$$" 2>/dev/null
|
|
|
|
local pool_updated=0
|
|
|
|
# Apply FPM settings
|
|
local current_max
|
|
current_max=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
|
|
|
|
if [ "$current_max" != "$recommended_max" ]; then
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_children" "$recommended_max" >/dev/null 2>&1; then
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
fi
|
|
|
|
if modify_fpm_pool_setting "$pool_config" "pm.max_requests" "$recommended_requests" >/dev/null 2>&1; then
|
|
pool_updated=$((pool_updated + 1))
|
|
fi
|
|
|
|
if [ "$pool_updated" -gt 0 ]; then
|
|
cecho " ${GREEN}✓${NC} $domain: FPM settings optimized"
|
|
cecho " pm.max_children: ${recommended_max} | pm.max_requests: ${recommended_requests}"
|
|
optimized=$((optimized + 1))
|
|
|
|
# Show profile data if available
|
|
if [ "$profiles_exist" = "1" ] && [ -f "/tmp/php-domain-profiles/$domain.profile" ]; then
|
|
show_profile_analysis "$domain"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply memory_limit
|
|
local ini_files
|
|
ini_files=$(find_php_ini_files "$username" "$domain")
|
|
|
|
while IFS= read -r ini_file; do
|
|
[ -z "$ini_file" ] && continue
|
|
[ ! -f "$ini_file" ] && continue
|
|
|
|
cp "$ini_file" "$ini_file.backup.$$" 2>/dev/null
|
|
|
|
if modify_php_ini_setting "$ini_file" "memory_limit" "$recommended_mem" >/dev/null 2>&1; then
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
changes_log+=("$domain: memory_limit→$recommended_mem")
|
|
fi
|
|
fi
|
|
|
|
# Enable OPcache if needed
|
|
if [ "${opcache_needs_enable[$domain]}" = "1" ]; then
|
|
if enable_opcache "$ini_file" >/dev/null 2>&1; then
|
|
local avg_rpm
|
|
avg_rpm=$(calculate_avg_requests_per_minute "$username" 24 | cut -d'|' -f1)
|
|
# Calculate available memory for OPcache (remaining from 60% allocation minus PHP-FPM needs)
|
|
local available_for_opcache=$((total_ram_mb * 60 / 100 - total_recommended_memory))
|
|
[ "$available_for_opcache" -lt 32 ] && available_for_opcache=32
|
|
local optimal_opcache_mem
|
|
optimal_opcache_mem=$(calculate_optimal_opcache_memory "$avg_rpm" "$available_for_opcache")
|
|
|
|
modify_php_ini_setting "$ini_file" "opcache.memory_consumption" "$optimal_opcache_mem" >/dev/null 2>&1
|
|
|
|
if validate_php_ini "$ini_file" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} $domain: OPcache enabled (${optimal_opcache_mem})"
|
|
opcache_enabled=$((opcache_enabled + 1))
|
|
fi
|
|
fi
|
|
fi
|
|
done <<< "$ini_files"
|
|
done <<< "$user_domains"
|
|
done <<< "$users"
|
|
|
|
# Restart PHP-FPM
|
|
echo ""
|
|
cecho "${CYAN}STEP 4: Restarting PHP-FPM${NC}"
|
|
echo ""
|
|
|
|
local php_versions
|
|
php_versions=$(detect_installed_php_versions)
|
|
|
|
while IFS= read -r php_version; do
|
|
[ -z "$php_version" ] && continue
|
|
if reload_php_fpm "$php_version" >/dev/null 2>&1; then
|
|
cecho " ${GREEN}✓${NC} Reloaded $php_version"
|
|
fi
|
|
done <<< "$php_versions"
|
|
|
|
# Final Summary
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}LEVEL 5 OPTIMIZATION COMPLETE - COMPREHENSIVE SUMMARY${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " ${GREEN}Domains Optimized: ${optimized}${NC}"
|
|
cecho " ${GREEN}OPcache Enabled: ${opcache_enabled}${NC}"
|
|
cecho " ${GREEN}Changes Applied: ${#changes_log[@]}${NC}"
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ OPTIMIZATION COMPLETE - Server is now fully optimized!${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Key Improvements:${NC}"
|
|
cecho " • PHP-FPM process limits optimized for traffic"
|
|
cecho " • Memory limits set per domain"
|
|
cecho " • Process recycling enabled (pm.max_requests)"
|
|
cecho " • OPcache enabled for performance boost"
|
|
echo ""
|
|
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# OPTION 6: VIEW OPCACHE STATISTICS
|
|
# ============================================================================
|
|
|
|
view_opcache_stats() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "opcache")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}OPCACHE STATISTICS: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Get OPcache stats
|
|
local stats
|
|
stats=$(get_opcache_stats "$username")
|
|
|
|
if [ -z "$stats" ]; then
|
|
cecho "${RED}Unable to retrieve OPcache statistics${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Parse and display
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}OPCACHE STATUS${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
local enabled
|
|
enabled=$(check_opcache_enabled "$username")
|
|
|
|
if [ "$enabled" = "1" ]; then
|
|
cecho " Status: ${GREEN}ENABLED${NC}"
|
|
else
|
|
cecho " Status: ${RED}DISABLED${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Display stats
|
|
while IFS='=' read -r key value; do
|
|
[ -z "$key" ] && continue
|
|
|
|
case "$key" in
|
|
memory_usage_mb)
|
|
cecho " Memory Used: ${WHITE}${value}MB${NC}"
|
|
;;
|
|
hits)
|
|
cecho " Cache Hits: ${GREEN}$value${NC}"
|
|
;;
|
|
misses)
|
|
cecho " Cache Misses: ${YELLOW}$value${NC}"
|
|
;;
|
|
num_cached_scripts)
|
|
cecho " Cached Scripts: ${WHITE}$value${NC}"
|
|
;;
|
|
max_cached_keys)
|
|
cecho " Max Cached Keys: ${WHITE}$value${NC}"
|
|
;;
|
|
wasted_memory_mb)
|
|
cecho " Wasted Memory: ${RED}${value}MB${NC}"
|
|
;;
|
|
esac
|
|
done <<< "$stats"
|
|
|
|
# Calculate and display hit rate
|
|
local hit_rate
|
|
hit_rate=$(calculate_opcache_hit_rate "$username")
|
|
echo ""
|
|
cecho " Hit Rate: ${GREEN}${BOLD}${hit_rate}%${NC}"
|
|
|
|
# Recommendation
|
|
echo ""
|
|
# Check if hit_rate is numeric before using awk
|
|
if [ -n "$hit_rate" ] && [[ "$hit_rate" =~ ^[0-9]+$ ]]; then
|
|
local hit_rate_low=$(awk "BEGIN {print ($hit_rate < 90 ? 1 : 0)}" 2>/dev/null || echo 0)
|
|
if [ "$hit_rate_low" -eq 1 ]; then
|
|
cecho " ${YELLOW}⚠ Hit rate below 90% - Consider increasing opcache.memory_consumption${NC}"
|
|
else
|
|
cecho " ${GREEN}✓ Hit rate is excellent${NC}"
|
|
fi
|
|
else
|
|
cecho " ${YELLOW}⚠ Unable to determine OPcache hit rate${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 7: VIEW PHP-FPM PROCESS STATS
|
|
# ============================================================================
|
|
|
|
view_fpm_stats() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "fpm")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}PHP-FPM PROCESS STATISTICS: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Get process stats
|
|
local memory_stats
|
|
memory_stats=$(calculate_memory_per_process "$username")
|
|
|
|
local avg_kb process_count total_mb
|
|
avg_kb=$(echo "$memory_stats" | cut -d'|' -f1)
|
|
process_count=$(echo "$memory_stats" | cut -d'|' -f2)
|
|
total_mb=$(echo "$memory_stats" | cut -d'|' -f3)
|
|
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}CURRENT RESOURCE USAGE${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
if [ "$process_count" -eq 0 ]; then
|
|
cecho "${YELLOW}No active PHP-FPM processes found${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
cecho " Active Processes: ${WHITE}$process_count${NC}"
|
|
cecho " Avg Memory/Process: ${WHITE}$((avg_kb / 1024))MB${NC} (${avg_kb}KB)"
|
|
cecho " Total Memory: ${WHITE}${total_mb}MB${NC}"
|
|
echo ""
|
|
|
|
# Get pool config
|
|
local pool_config
|
|
pool_config=$(find_fpm_pool_config "$username")
|
|
|
|
if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}POOL CONFIGURATION${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
local pool_settings
|
|
pool_settings=$(parse_fpm_pool_config "$pool_config")
|
|
|
|
# Display key settings
|
|
while IFS='=' read -r key value; do
|
|
[ -z "$key" ] && continue
|
|
cecho " $key: ${WHITE}$value${NC}"
|
|
done <<< "$pool_settings"
|
|
fi
|
|
|
|
# Calculate optimal max_children using improved algorithm
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
cecho "${WHITE}${BOLD}RECOMMENDATION${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
# Get total system memory for improved calculation
|
|
local total_sys_ram
|
|
total_sys_ram=$(free -m | awk '/^Mem:/ {print $2}')
|
|
|
|
# NEW: Improved algorithm
|
|
local improved_opt
|
|
improved_opt=$(calculate_optimal_php_settings "$username" "$total_sys_ram")
|
|
local improved_max improved_pm improved_min improved_max_spare improved_opt_reason
|
|
improved_max=$(get_field "$improved_opt" 1)
|
|
improved_pm=$(get_field "$improved_opt" 2)
|
|
improved_min=$(get_field "$improved_opt" 3)
|
|
improved_max_spare=$(get_field "$improved_opt" 4)
|
|
improved_opt_reason=$(get_field "$improved_opt" 5)
|
|
|
|
# OLD: Legacy algorithm (for comparison)
|
|
local legacy_result
|
|
legacy_result=$(calculate_optimal_max_children "$username" 1024)
|
|
local legacy_recommended legacy_reason
|
|
legacy_recommended=$(echo "$legacy_result" | cut -d'|' -f1)
|
|
legacy_reason=$(echo "$legacy_result" | cut -d'|' -f2)
|
|
|
|
# Display comparison
|
|
cecho " ${GREEN}${BOLD}Improved Recommendation:${NC}"
|
|
cecho " pm.max_children: ${GREEN}$improved_max${NC}"
|
|
cecho " pm mode: ${GREEN}$improved_pm${NC}"
|
|
cecho " min_spare_servers: ${GREEN}$improved_min${NC}"
|
|
cecho " max_spare_servers: ${GREEN}$improved_max_spare${NC}"
|
|
cecho " Reason: $improved_opt_reason"
|
|
echo ""
|
|
|
|
if [ "$improved_max" -ne "$legacy_recommended" ]; then
|
|
cecho " ${YELLOW}Legacy Recommendation (for reference):${NC}"
|
|
cecho " pm.max_children: ${YELLOW}$legacy_recommended${NC} ($legacy_reason)"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 8: CHECK FOR ISSUES
|
|
# ============================================================================
|
|
|
|
check_config_issues() {
|
|
show_banner
|
|
|
|
local selection
|
|
selection=$(select_domain "check")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}CONFIGURATION ISSUES: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Detect issues
|
|
local issues
|
|
issues=$(detect_php_config_issues "$username" "$domain")
|
|
|
|
# Display by severity
|
|
local has_critical=false
|
|
local has_high=false
|
|
local has_medium=false
|
|
local has_low=false
|
|
|
|
# Check for each severity level
|
|
echo "$issues" | grep -q "CRITICAL" && has_critical=true
|
|
echo "$issues" | grep -q "HIGH" && has_high=true
|
|
echo "$issues" | grep -q "MEDIUM" && has_medium=true
|
|
echo "$issues" | grep -q "LOW" && has_low=true
|
|
|
|
# Display CRITICAL
|
|
if [ "$has_critical" = true ]; then
|
|
cecho "${RED}${BOLD}CRITICAL ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "CRITICAL" ] && continue
|
|
|
|
cecho " ${RED}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Display HIGH
|
|
if [ "$has_high" = true ]; then
|
|
cecho "${YELLOW}${BOLD}HIGH PRIORITY ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "HIGH" ] && continue
|
|
|
|
cecho " ${YELLOW}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Display MEDIUM
|
|
if [ "$has_medium" = true ]; then
|
|
cecho "${BLUE}${BOLD}MEDIUM PRIORITY ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "MEDIUM" ] && continue
|
|
|
|
cecho " ${BLUE}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# Display LOW
|
|
if [ "$has_low" = true ]; then
|
|
cecho "${GREEN}${BOLD}LOW PRIORITY ISSUES:${NC}"
|
|
echo ""
|
|
|
|
while IFS='|' read -r issue_type severity message recommendation; do
|
|
[ "$severity" != "LOW" ] && continue
|
|
|
|
cecho " ${GREEN}●${NC} $message"
|
|
cecho " ${CYAN}→${NC} $recommendation"
|
|
echo ""
|
|
done <<< "$issues"
|
|
fi
|
|
|
|
# No issues
|
|
if [ "$has_critical" = false ] && [ "$has_high" = false ] && [ "$has_medium" = false ] && [ "$has_low" = false ]; then
|
|
cecho "${GREEN}${BOLD}✓ No configuration issues detected!${NC}"
|
|
cecho "${GREEN}Configuration appears to be optimal.${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION 9: CHECK SERVER MEMORY CAPACITY
|
|
# ============================================================================
|
|
|
|
check_server_memory_capacity() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}SERVER MEMORY CAPACITY CHECK${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}This checks if all PHP-FPM pools hitting max_children would cause OOM...${NC}"
|
|
echo ""
|
|
|
|
# Run capacity analysis
|
|
cecho "${CYAN}Analyzing PHP-FPM memory requirements...${NC}"
|
|
echo ""
|
|
|
|
local result
|
|
result=$(calculate_server_memory_capacity 2>/dev/null)
|
|
|
|
# Parse result - first line is summary, remaining lines are details
|
|
local main_result
|
|
main_result=$(echo "$result" | head -1)
|
|
|
|
local total_required total_ram percentage status details
|
|
total_required=$(echo "$main_result" | cut -d'|' -f1)
|
|
total_ram=$(echo "$main_result" | cut -d'|' -f2)
|
|
percentage=$(echo "$main_result" | cut -d'|' -f3)
|
|
status=$(echo "$main_result" | cut -d'|' -f4)
|
|
|
|
# Get details (all lines after first)
|
|
details=$(echo "$result" | tail -n +2)
|
|
|
|
# Display summary
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
cecho "${WHITE}${BOLD}MEMORY CAPACITY ANALYSIS${NC}"
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
cecho " Total Server RAM: ${WHITE}${total_ram}MB${NC}"
|
|
cecho " Required if ALL pools at max_children: ${WHITE}${total_required}MB${NC}"
|
|
cecho " Percentage of RAM: ${WHITE}${percentage}%${NC}"
|
|
echo ""
|
|
|
|
# Display status with color
|
|
case "$status" in
|
|
CRITICAL)
|
|
cecho " Status: ${RED}${BOLD}CRITICAL - HIGH OOM RISK!${NC}"
|
|
cecho ""
|
|
cecho " ${RED}WARNING: If all PHP-FPM pools hit their max_children limit,${NC}"
|
|
cecho " ${RED}the server will likely run out of memory and kill processes!${NC}"
|
|
;;
|
|
WARNING)
|
|
cecho " Status: ${YELLOW}${BOLD}WARNING - MODERATE OOM RISK${NC}"
|
|
cecho ""
|
|
cecho " ${YELLOW}CAUTION: Memory usage is high. Some pools may need reduction.${NC}"
|
|
;;
|
|
CAUTION)
|
|
cecho " Status: ${YELLOW}${BOLD}CAUTION - WATCH MEMORY USAGE${NC}"
|
|
cecho ""
|
|
cecho " ${YELLOW}Memory usage is elevated but manageable.${NC}"
|
|
;;
|
|
HEALTHY)
|
|
cecho " Status: ${GREEN}${BOLD}HEALTHY - LOW OOM RISK${NC}"
|
|
cecho ""
|
|
cecho " ${GREEN}Memory allocation appears safe.${NC}"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
|
|
# Ask if user wants detailed breakdown
|
|
if confirm "Show detailed per-user breakdown?"; then
|
|
echo ""
|
|
cecho "${WHITE}${BOLD}PER-USER BREAKDOWN${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
|
|
printf "%-30s %-20s %12s %12s %12s\n" "DOMAIN" "USER" "MAX_CHILDREN" "AVG/PROCESS" "MAX_MEMORY"
|
|
printf "%-30s %-20s %12s %12s %12s\n" "------------------------------" "--------------------" "------------" "------------" "------------"
|
|
|
|
while IFS='|' read -r domain username max_children avg_mb pool_max_mb; do
|
|
[ -z "$domain" ] && continue
|
|
printf "%-30s %-20s %12s %12s %12s\n" "$domain" "$username" "$max_children" "$avg_mb" "$pool_max_mb"
|
|
done <<< "$details"
|
|
|
|
echo ""
|
|
fi
|
|
|
|
# Ask if user wants balanced recommendations
|
|
echo ""
|
|
|
|
if confirm "Calculate balanced memory allocation recommendations?"; then
|
|
echo ""
|
|
cecho "${WHITE}${BOLD}BALANCED MEMORY ALLOCATION RECOMMENDATIONS${NC}"
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Calculating optimal max_children based on traffic...${NC}"
|
|
echo ""
|
|
|
|
local recommendations
|
|
recommendations=$(calculate_balanced_memory_allocation 2>/dev/null)
|
|
|
|
# Display header
|
|
local header
|
|
header=$(echo "$recommendations" | head -1)
|
|
echo "$header" | awk -F'|' '{printf "%-15s %8s %10s %12s %12s %15s %20s\n", $1, $2, $3, $4, $5, $6, $7}'
|
|
echo "────────────────────────────────────────────────────────────────────────────────────────────────────────"
|
|
|
|
# Display recommendations
|
|
echo "$recommendations" | tail -n +2 | while IFS='|' read -r user current_max avg_mb traffic_rpm recommended_max allocated_mb reason; do
|
|
[ -z "$user" ] && continue
|
|
|
|
# Color code reason
|
|
if [[ "$reason" == *"REDUCE"* ]]; then
|
|
color="${RED}"
|
|
elif [[ "$reason" == *"INCREASE"* ]]; then
|
|
color="${GREEN}"
|
|
else
|
|
color="${WHITE}"
|
|
fi
|
|
|
|
printf "%-15s %8s %10s %12s ${color}%12s${NC} %15s %20s\n" "$user" "$current_max" "$avg_mb" "$traffic_rpm" "$recommended_max" "$allocated_mb" "$reason"
|
|
done
|
|
|
|
echo ""
|
|
cecho "${YELLOW}NOTE: These are recommendations based on proportional traffic.${NC}"
|
|
cecho "${YELLOW}Actual needs may vary. Always test changes carefully.${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
cecho "${CYAN}═══════════════════════════════════════════════════════════════════${NC}"
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION B: BACKUP CONFIGURATIONS
|
|
# ============================================================================
|
|
|
|
backup_configurations() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}BACKUP PHP CONFIGURATIONS${NC}"
|
|
echo ""
|
|
|
|
# Select domain
|
|
local selection
|
|
selection=$(select_domain "backup")
|
|
|
|
if [ $? -ne 0 ] || [ -z "$selection" ]; then
|
|
return
|
|
fi
|
|
|
|
local domain username
|
|
domain=$(echo "$selection" | cut -d'|' -f1)
|
|
username=$(echo "$selection" | cut -d'|' -f2)
|
|
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}BACKUP: ${GREEN}$domain${NC}"
|
|
echo ""
|
|
|
|
# Initialize backup system
|
|
initialize_backup_system
|
|
|
|
# Create backup
|
|
cecho "${YELLOW}Creating backup of PHP configurations...${NC}"
|
|
echo ""
|
|
|
|
local backup_dir
|
|
backup_dir=$(backup_user_php_configs "$username" "$domain" 2>&1)
|
|
|
|
if [ $? -eq 0 ]; then
|
|
cecho "${GREEN}${BOLD}✓ Backup created successfully!${NC}"
|
|
echo ""
|
|
cecho " Backup location: ${WHITE}$backup_dir${NC}"
|
|
echo ""
|
|
|
|
# Show what was backed up
|
|
if [ -f "$backup_dir/metadata.txt" ]; then
|
|
cecho "${CYAN}Files backed up:${NC}"
|
|
grep "^ /" "$backup_dir/metadata.txt" | while IFS= read -r line; do
|
|
local file=$(echo "$line" | awk '{print $1}')
|
|
cecho " ${GREEN}✓${NC} $file"
|
|
done
|
|
fi
|
|
else
|
|
cecho "${RED}${BOLD}✗ Backup failed${NC}"
|
|
echo ""
|
|
echo "$backup_dir"
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# OPTION R: RESTORE FROM BACKUP
|
|
# ============================================================================
|
|
|
|
restore_configurations() {
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}RESTORE FROM BACKUP${NC}"
|
|
echo ""
|
|
|
|
# List available backups
|
|
cecho "${CYAN}Available backups:${NC}"
|
|
echo ""
|
|
|
|
local backups
|
|
backups=$(list_backups)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
cecho "${YELLOW}No backups found${NC}"
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
# Display backups and store backup names in array for selection
|
|
local backup_array=()
|
|
mapfile -t backup_array < <(echo "$backups" | tail -n +2 | cut -d'|' -f1)
|
|
|
|
local index=1
|
|
echo "$backups" | tail -n +2 | while IFS='|' read -r backup_name created username domain file_count; do
|
|
printf "${GREEN}%3d${NC}) %-20s %s ${CYAN}[%s]${NC} ${YELLOW}(%s)${NC} %s files\n" \
|
|
"$index" "$backup_name" "$created" "$username" "$domain" "$file_count"
|
|
index=$((index + 1))
|
|
done
|
|
|
|
echo ""
|
|
cecho "${CYAN}─────────────────────────────────────────────────────────────────────${NC}"
|
|
|
|
# Validate backup selection with retry loop
|
|
while true; do
|
|
read -p "Select backup number to restore (or 'q' to cancel): " selection
|
|
|
|
if [[ "$selection" == "q" ]]; then
|
|
return
|
|
fi
|
|
|
|
if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt ${#backup_array[@]} ]; then
|
|
echo ""
|
|
cecho "${RED}Invalid selection. Please enter a number 1-${#backup_array[@]}${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
|
|
break
|
|
done
|
|
|
|
local selected_backup="${backup_array[$((selection - 1))]}"
|
|
|
|
# Confirm restoration
|
|
echo ""
|
|
cecho "${YELLOW}${BOLD}WARNING: This will overwrite current configurations!${NC}"
|
|
echo ""
|
|
|
|
if ! confirm "Are you sure you want to restore from $selected_backup?"; then
|
|
cecho "${YELLOW}Restore cancelled${NC}"
|
|
sleep 2
|
|
return
|
|
fi
|
|
|
|
# Perform restore
|
|
show_banner
|
|
cecho "${WHITE}${BOLD}RESTORING FROM BACKUP${NC}"
|
|
echo ""
|
|
|
|
if restore_from_backup "$selected_backup"; then
|
|
echo ""
|
|
cecho "${GREEN}${BOLD}✓ Restore completed successfully!${NC}"
|
|
echo ""
|
|
cecho "${YELLOW}Don't forget to restart PHP-FPM for changes to take effect!${NC}"
|
|
else
|
|
echo ""
|
|
cecho "${RED}${BOLD}✗ Restore failed${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
read -p "Press Enter to continue..."
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN LOOP
|
|
# ============================================================================
|
|
|
|
main() {
|
|
# Detect system
|
|
initialize_system_detection
|
|
|
|
# Check if running as root
|
|
if [ "$EUID" -ne 0 ]; then
|
|
cecho "${RED}ERROR: This script must be run as root${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for PHP-FPM
|
|
if ! is_using_php_fpm; then
|
|
cecho "${YELLOW}WARNING: PHP-FPM not detected. Some features may not work.${NC}"
|
|
read -p "Press Enter to continue anyway..."
|
|
fi
|
|
|
|
# Main loop
|
|
while true; do
|
|
show_banner
|
|
show_main_menu
|
|
|
|
# Validate choice input with retry loop
|
|
while true; do
|
|
read -p "Select option (0-9, b, r): " choice
|
|
|
|
if ! [[ "$choice" =~ ^([0-9]|[bBrR])$ ]]; then
|
|
echo ""
|
|
cecho "${RED}Invalid choice. Please enter 0-9, b, or r${NC}"
|
|
echo ""
|
|
continue
|
|
fi
|
|
break
|
|
done
|
|
|
|
# Convert uppercase to lowercase for case statement
|
|
choice=${choice,,}
|
|
|
|
case "$choice" in
|
|
1)
|
|
analyze_single_domain
|
|
;;
|
|
2)
|
|
analyze_all_domains
|
|
;;
|
|
3)
|
|
quick_health_check
|
|
;;
|
|
4)
|
|
optimize_domain
|
|
;;
|
|
5)
|
|
optimize_all_domains
|
|
;;
|
|
6)
|
|
view_opcache_stats
|
|
;;
|
|
7)
|
|
view_fpm_stats
|
|
;;
|
|
8)
|
|
check_config_issues
|
|
;;
|
|
9)
|
|
check_server_memory_capacity
|
|
;;
|
|
b)
|
|
backup_configurations
|
|
;;
|
|
r)
|
|
restore_configurations
|
|
;;
|
|
0)
|
|
cecho "${GREEN}Exiting PHP Optimizer...${NC}"
|
|
exit 0
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Run main
|
|
main "$@"
|