#!/bin/bash ################################################################################ # PHP-FPM Calculator - Improved Algorithm # Purpose: Calculate optimal PHP-FPM pool settings based on: # - Available server memory # - Actual traffic patterns (peak concurrent requests) # - Other service memory usage (MySQL, Redis, etc) # - PM mode recommendations # - Safe allocation buffers based on traffic stability ################################################################################ # Source guard - prevent re-sourcing if [ -n "${_PHP_CALCULATOR_LOADED:-}" ]; then return 0 fi readonly _PHP_CALCULATOR_LOADED=1 # Dependencies - only source if not already loaded if [ -z "${_PHP_DETECTOR_LOADED:-}" ]; then _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$_LIB_DIR/php-detector.sh" 2>/dev/null || { echo "ERROR: php-detector.sh not found"; return 1; } fi if [ -z "${_SYSTEM_DETECT_LOADED:-}" ]; then _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$_LIB_DIR/system-detect.sh" 2>/dev/null || { echo "ERROR: system-detect.sh not found"; return 1; } fi # ============================================================================ # HELPER FUNCTION - Extract field from pipe-delimited string # ============================================================================ get_field() { local input="$1" local field_num="$2" local temp="$input" local i=1 while [ $i -lt "$field_num" ]; do temp="${temp#*|}" i=$((i + 1)) done echo "${temp%%|*}" } # ============================================================================ # IMPROVED: SYSTEM RESERVE CALCULATION # ============================================================================ # Calculate system reserve based on total RAM (percentage-based, not hardcoded) # Usage: calculate_system_reserve # Returns: reserved_mb|reason calculate_system_reserve() { local total_ram_mb="$1" if [ -z "$total_ram_mb" ] || [ "$total_ram_mb" -lt 512 ]; then echo "256|Minimal system (< 512MB RAM)" return fi local reserved_mb # Dynamic reserve based on total RAM: # Small servers (< 2GB): 15% reserve (keep base system stable) # Medium servers (2-8GB): 20% reserve (typical workload) # Large servers (8-32GB): 25% reserve (headroom for spikes) # Very large servers (> 32GB): 30% reserve (accommodate multiple services) if [ "$total_ram_mb" -lt 2048 ]; then # Small VPS: 15% reserve reserved_mb=$((total_ram_mb * 15 / 100)) [ "$reserved_mb" -lt 256 ] && reserved_mb=256 echo "$reserved_mb|Small server reserve (15% of ${total_ram_mb}MB)" elif [ "$total_ram_mb" -lt 8192 ]; then # Medium: 20% reserve reserved_mb=$((total_ram_mb * 20 / 100)) echo "$reserved_mb|Medium server reserve (20% of ${total_ram_mb}MB)" elif [ "$total_ram_mb" -lt 32768 ]; then # Large: 25% reserve reserved_mb=$((total_ram_mb * 25 / 100)) echo "$reserved_mb|Large server reserve (25% of ${total_ram_mb}MB)" else # Very large: 30% reserve reserved_mb=$((total_ram_mb * 30 / 100)) echo "$reserved_mb|Very large server reserve (30% of ${total_ram_mb}MB)" fi } # ============================================================================ # IMPROVED: MEMORY-BASED MAX_CHILDREN (Refined Algorithm) # ============================================================================ # Calculate max_children based on available memory and safety buffer # Usage: calculate_max_children_memory_based # Returns: max_children|reason calculate_max_children_memory_based() { local username="$1" local total_ram_mb="$2" if [ -z "$total_ram_mb" ] || [ -z "$username" ]; then echo "20|Invalid parameters" return fi # Get average memory per process local avg_kb avg_kb=$(get_fpm_memory_usage "$username" 2>/dev/null || echo "0") if [ "$avg_kb" -eq 0 ]; then # No active processes detected (ondemand mode, or low traffic) # Use safe default: 20 processes with assumed 50MB per process echo "20|No active processes, using safe default" return fi # Calculate system reserve (dynamic percentage-based) local reserve_result reserve_result=$(calculate_system_reserve "$total_ram_mb") local reserved_mb reserved_mb=$(get_field "$reserve_result" 1) # Account for MySQL memory (critical on shared hosting!) local mysql_memory_mb=0 local mysql_info mysql_info=$(detect_mysql_memory_usage 2>/dev/null) if [ $? -eq 0 ]; then # FIX: detect_mysql_memory_usage returns: memory|status (only 2 fields) mysql_memory_mb=$(echo "$mysql_info" | cut -d'|' -f1) fi # Available memory for PHP-FPM (after system + MySQL reserves) # CRITICAL: This is shared across ALL domains, not per-domain! local available_mb=$((total_ram_mb - reserved_mb - mysql_memory_mb)) # Safety check: never allow PHP-FPM to use more than 60% of RAM local max_php_fpm=$((total_ram_mb * 60 / 100)) if [ "$available_mb" -gt "$max_php_fpm" ]; then available_mb=$max_php_fpm fi # Convert average KB to MB local avg_mb=$((avg_kb / 1024)) if [ "$avg_mb" -eq 0 ]; then avg_mb=20 # More realistic default (not 1MB) fi # Theoretical maximum without safety buffer local theoretical_max=$((available_mb / avg_mb)) # Apply safety buffer (50% - much more conservative for shared hosting!) # This accounts for peak traffic spikes and other processes local safety_buffer=50 local recommended=$((theoretical_max * (100 - safety_buffer) / 100)) # Hard cap at traffic-realistic limits if [ "$recommended" -lt 5 ]; then echo "5|Minimum safe value (insufficient memory)" elif [ "$recommended" -gt 150 ]; then # CRITICAL: Cap at 150 max per domain on shared hosting # Higher values require dedicated servers echo "150|Capped at safe maximum for shared hosting (would be $recommended)" else local reason="Memory-based: ${avg_mb}MB per process, ${available_mb}MB available (after MySQL: ${mysql_memory_mb}MB), ${safety_buffer}% safety buffer" echo "$recommended|$reason" fi } # ============================================================================ # NEW: TRAFFIC-BASED MAX_CHILDREN CALCULATION # ============================================================================ # Calculate max_children based on actual peak concurrent requests # Usage: calculate_peak_concurrent_requests # Returns: peak_concurrent|stability_factor calculate_peak_concurrent_requests_improved() { local username="$1" local days="${2:-7}" # Find access logs local access_logs access_logs=$(find /home/"$username"/*/logs -name "access_log*" -o -name "access.log*" 2>/dev/null | head -5) if [ -z "$access_logs" ]; then echo "0|0.8|No access logs found" return fi # Analyze access logs to find peak concurrent requests # Strategy: Use combined timestamp analysis for better accuracy local peak_concurrent=0 local total_samples=0 local high_traffic_periods=0 local traffic_variance=0 # Sample each log and find peaks while IFS= read -r log_file; do [ ! -f "$log_file" ] && continue # Get logs from last N days local temp_processed temp_processed=$(find "$log_file" -mtime -"$days" -exec tail -n 10000 {} \; 2>/dev/null | \ awk '{print $4}' | sed 's/\[//' | sort | uniq -c | sort -rn | head -1) if [ -n "$temp_processed" ]; then local sample_count sample_count=$(echo "$temp_processed" | awk '{print $1}') if [ "$sample_count" -gt "$peak_concurrent" ]; then peak_concurrent=$sample_count fi total_samples=$((total_samples + 1)) fi done <<< "$access_logs" # If no samples, estimate from HTTP status codes if [ "$total_samples" -eq 0 ]; then # Estimate: count 200 responses per second at peak peak_concurrent=$(tail -n 100000 "$log_file" 2>/dev/null | grep " 200 " | wc -l | awk '{print int($1/100)}') if [ "$peak_concurrent" -lt 1 ]; then peak_concurrent=1 fi fi # Estimate traffic stability (0.6 = unstable, 0.8 = stable, 0.9 = very stable) # This is used to adjust safety buffer local stability_factor=0.8 if [ "$total_samples" -lt 3 ]; then stability_factor=0.6 # Very limited data, assume unstable elif [ "$total_samples" -ge 10 ]; then stability_factor=0.9 # Good data, assume stable fi echo "$peak_concurrent|$stability_factor|Based on $total_samples access logs" } # ============================================================================ # NEW: RECOMMEND MAX_CHILDREN from TRAFFIC PATTERNS # ============================================================================ # Calculate recommended max_children based on peak concurrent requests # Usage: calculate_max_children_traffic_based # Returns: recommended_max_children|reason calculate_max_children_traffic_based() { local peak_concurrent="$1" local stability_factor="${2:-0.8}" if [ "$peak_concurrent" -lt 1 ]; then echo "5|Insufficient traffic data, using minimum" return fi # Formula: recommended = peak_concurrent * (1.0 + headroom_factor) * stability_factor # headroom_factor: extra capacity for unexpected spikes (default 0.3 = 30%) local headroom_factor=0.3 local recommended=$(echo "$peak_concurrent (1 + $headroom_factor) * $stability_factor" | bc | awk '{print int($1)}') # Sanity bounds if [ "$recommended" -lt 5 ]; then recommended=5 elif [ "$recommended" -gt 200 ]; then recommended=200 # Most domains don't need more than 200 concurrent processes fi local reason="Traffic-based: $peak_concurrent peak concurrent requests" if [ "$stability_factor" != "0.8" ]; then reason="$reason (stability factor: $stability_factor)" fi echo "$recommended|$reason" } # ============================================================================ # NEW: DETECT MYSQL MEMORY USAGE # ============================================================================ # Get MySQL memory usage to account for in PHP-FPM allocation # Usage: detect_mysql_memory_usage # Returns: mysql_memory_mb|status detect_mysql_memory_usage() { if ! command -v mysql &>/dev/null && ! command -v mysqld &>/dev/null; then echo "0|MySQL not installed" return fi # Try to get MySQL process memory usage local mysql_mem mysql_mem=$(ps aux | grep "[m]ysqld" | awk '{print int($6/1024)}') if [ -z "$mysql_mem" ] || [ "$mysql_mem" -eq 0 ]; then # Fallback: estimate from MySQL variables if command -v mysql &>/dev/null; then mysql_mem=$(mysql -e "SHOW VARIABLES LIKE '%buffer%'" 2>/dev/null | grep -i "buffer" | \ awk -F'\t' '{gsub(/[KM]/,"",$3); if($3 ~ /K/) $3=$3/1024; print $3}' | \ awk '{sum+=$1} END {print int(sum)}') fi fi if [ -z "$mysql_mem" ] || [ "$mysql_mem" -eq 0 ]; then # Safe default estimate: 300MB for typical MySQL echo "300|Estimated default" else echo "$mysql_mem|Detected from process" fi } # ============================================================================ # NEW: CALCULATE SERVER TOTAL CAPACITY # ============================================================================ # NEW: Measure actual memory per process across all active FPM pools # Usage: get_actual_memory_per_process # Returns: memory_mb (in MB, or 140 if can't measure) # This ensures capacity calculations use REAL data, not assumptions get_actual_memory_per_process() { # Get ALL active php-fpm processes and their RSS memory # ps aux format: USER PID %CPU %MEM VSZ RSS STAT START TIME COMMAND # RSS is field 6 (in KB) local total_kb=0 local count=0 while read -r line; do if [ -z "$line" ]; then continue fi # Extract RSS (field 6) from ps aux output local rss_kb rss_kb=$(echo "$line" | awk '{print $6}') if [ -n "$rss_kb" ] && [ "$rss_kb" -gt 0 ]; then total_kb=$((total_kb + rss_kb)) count=$((count + 1)) fi done < <(ps aux | grep -E 'php-fpm.*pool' | grep -v grep || true) # If we found active processes, calculate average if [ "$count" -gt 0 ]; then local avg_kb=$((total_kb / count)) local avg_mb=$((avg_kb / 1024)) # Sanity check: per-process memory should be 10MB-500MB if [ "$avg_mb" -lt 10 ]; then avg_mb=10 elif [ "$avg_mb" -gt 500 ]; then avg_mb=500 fi echo "$avg_mb" return 0 fi # No active processes detected # Use user-provided measurement or conservative default of 140MB (based on actual data) echo "140" return 0 } # ============================================================================ # Calculate the total max_children the entire server can support # Usage: calculate_server_capacity # Returns: total_capacity|available_memory|memory_per_process|reason calculate_server_capacity() { local total_ram_mb="$1" if [ -z "$total_ram_mb" ] || [ "$total_ram_mb" -lt 512 ]; then echo "0|0|140|Insufficient RAM for calculation" return fi # Calculate system reserve (dynamic percentage-based) local reserve_result reserve_result=$(calculate_system_reserve "$total_ram_mb") local reserved_mb reserved_mb=$(get_field "$reserve_result" 1) # Account for MySQL memory (critical on shared hosting!) local mysql_memory_mb=0 local mysql_info mysql_info=$(detect_mysql_memory_usage 2>/dev/null) if [ $? -eq 0 ]; then # FIX: detect_mysql_memory_usage returns: memory|status (only 2 fields) mysql_memory_mb=$(echo "$mysql_info" | cut -d'|' -f1) fi # Available memory for PHP-FPM (after system + MySQL reserves) local available_mb=$((total_ram_mb - reserved_mb - mysql_memory_mb)) # Safety check: never allow PHP-FPM to use more than 60% of RAM local max_php_fpm=$((total_ram_mb * 60 / 100)) if [ "$available_mb" -gt "$max_php_fpm" ]; then available_mb=$max_php_fpm fi # CRITICAL: Never allow negative available memory if [ "$available_mb" -lt 0 ]; then available_mb=0 fi # Use 140MB per process (confirmed from actual PHP-FPM workers) # This is the realistic baseline for production PHP workloads local memory_per_process=140 # Total capacity = available memory / memory per process local total_capacity=$((available_mb / memory_per_process)) # Sanity checks [ "$total_capacity" -lt 5 ] && total_capacity=5 [ "$total_capacity" -gt 500 ] && total_capacity=500 echo "$total_capacity|$available_mb|$memory_per_process|Server can support $total_capacity total max_children" } # ============================================================================ # NEW: GET DOMAIN TRAFFIC PERCENTAGE # ============================================================================ # Calculate what percentage of total server traffic this domain handles # Usage: get_domain_traffic_percentage # Returns: percentage|request_count|reason get_domain_traffic_percentage() { local username="$1" local domain="$2" local all_domains="$3" if [ -z "$domain" ] || [ -z "$all_domains" ]; then echo "50|0|Insufficient data" return fi # Count domains to determine equal share local domain_count domain_count=$(echo "$all_domains" | grep -v "^$" | wc -l) [ "$domain_count" -lt 1 ] && domain_count=1 # CRITICAL FIX: Use peak concurrent to estimate traffic percentage # (Access log parsing is unreliable across control panels) # Peak concurrent is a reliable indicator of traffic intensity # Get this domain's peak concurrent local domain_peak domain_peak=$(get_domain_peak_concurrent "$domain" 2>/dev/null || echo "0") [ -z "$domain_peak" ] && domain_peak=0 # Calculate total peak concurrent across ALL domains local total_peak=0 local domain_check while IFS= read -r domain_check; do [ -z "$domain_check" ] && continue local peak_val peak_val=$(get_domain_peak_concurrent "$domain_check" 2>/dev/null || echo "0") [ -z "$peak_val" ] && peak_val=0 total_peak=$((total_peak + peak_val)) done <<< "$all_domains" # Calculate percentage based on peak concurrent if [ "$total_peak" -gt 0 ]; then local percentage=$((domain_peak * 100 / total_peak)) [ "$percentage" -lt 1 ] && percentage=1 [ "$percentage" -gt 99 ] && percentage=99 echo "$percentage|$domain_peak|Based on peak concurrent (traffic intensity)" return fi # Fallback: equal distribution among all domains # This is the SAFEST approach when we can't calculate percentages local equal_share=$((100 / domain_count)) echo "$equal_share|0|Using equal distribution ($domain_count domains) - safest assumption" } # ============================================================================ # NEW: CALCULATE FAIR SHARE BASED ON TRAFFIC # ============================================================================ # Calculate this domain's fair share of server capacity based on traffic percentage # Usage: calculate_max_children_fair_share # Returns: fair_share_max|reason calculate_max_children_fair_share() { local total_capacity="$1" local traffic_percentage="$2" if [ -z "$total_capacity" ] || [ -z "$traffic_percentage" ]; then echo "20|Invalid parameters" return fi # Calculate fair share: total capacity × traffic percentage local fair_share=$((total_capacity * traffic_percentage / 100)) # Apply hard limits if [ "$fair_share" -lt 5 ]; then echo "5|Fair share is very small (minimum enforced)" elif [ "$fair_share" -gt 150 ]; then echo "150|Fair share exceeds shared hosting limit (capped at 150)" else echo "$fair_share|Fair share: $traffic_percentage% of $total_capacity total" fi } # ============================================================================ # NEW: RECOMMEND PM MODE (static/dynamic/ondemand) # ============================================================================ # Recommend most appropriate PHP-FPM pm mode based on traffic pattern # Usage: recommend_pm_mode # Returns: pm_mode|min_spare|max_spare|reason recommend_pm_mode() { local peak_concurrent="$1" local average_concurrent="${2:-$(echo "$peak_concurrent / 2" | bc)}" local stability_factor="${3:-0.8}" # Determine stability level local traffic_pattern if [ "$(echo "$stability_factor < 0.65" | bc)" -eq 1 ]; then traffic_pattern="UNSTABLE" elif [ "$(echo "$stability_factor < 0.85" | bc)" -eq 1 ]; then traffic_pattern="MODERATE" else traffic_pattern="STABLE" fi # Recommend mode based on traffic characteristics local pm_mode min_spare max_spare reason if [ "$peak_concurrent" -lt 5 ]; then # Very low traffic: ondemand saves memory pm_mode="ondemand" min_spare=0 max_spare=3 reason="Very low traffic ($peak_concurrent peak concurrent)" elif [ "$traffic_pattern" = "UNSTABLE" ]; then # Unstable traffic: dynamic gives best balance pm_mode="dynamic" min_spare=$((peak_concurrent / 4)) max_spare=$((peak_concurrent * 3 / 4)) reason="Unstable traffic pattern (stability: $stability_factor)" elif [ "$traffic_pattern" = "STABLE" ]; then # Stable high traffic: static for performance pm_mode="static" min_spare=$((peak_concurrent - 2)) max_spare=$((peak_concurrent + 2)) reason="Stable traffic pattern (peak: $peak_concurrent concurrent)" else # Moderate/mixed traffic: dynamic is good default pm_mode="dynamic" min_spare=$((peak_concurrent / 3)) max_spare=$((peak_concurrent * 2 / 3)) reason="Moderate traffic ($traffic_pattern)" fi # Sanity bounds [ "$min_spare" -lt 1 ] && min_spare=1 [ "$max_spare" -lt "$min_spare" ] && max_spare=$((min_spare + 2)) [ "$max_spare" -gt 100 ] && max_spare=100 echo "$pm_mode|$min_spare|$max_spare|$reason" } # ============================================================================ # NEW: COMPREHENSIVE RECOMMENDATION # ============================================================================ # Calculate optimal settings combining memory and traffic analysis # Usage: calculate_optimal_php_settings # Returns: max_children|pm_mode|min_spare|max_spare|reason calculate_optimal_php_settings() { local username="$1" local total_ram_mb="$2" if [ -z "$username" ] || [ -z "$total_ram_mb" ]; then echo "0|dynamic|1|5|Invalid parameters" return fi # Calculate memory-based recommendation local memory_result memory_result=$(calculate_max_children_memory_based "$username" "$total_ram_mb") local memory_based_max memory_based_max=$(get_field "$memory_result" 1) # Calculate traffic-based recommendation local traffic_result traffic_result=$(calculate_peak_concurrent_requests_improved "$username" 7) local peak_concurrent stability_factor peak_concurrent=$(get_field "$traffic_result" 1) stability_factor=$(get_field "$traffic_result" 2) local traffic_based_max=0 if [ "$peak_concurrent" -gt 0 ]; then local traffic_calc traffic_calc=$(calculate_max_children_traffic_based "$peak_concurrent" "$stability_factor") traffic_based_max=$(get_field "$traffic_calc" 1) fi # Combine both recommendations (use lower value for safety) local final_max_children="$memory_based_max" local reason_prefix="Memory-based" if [ "$traffic_based_max" -gt 0 ] && [ "$traffic_based_max" -lt "$memory_based_max" ]; then final_max_children="$traffic_based_max" reason_prefix="Traffic-based (constrained by memory)" elif [ "$traffic_based_max" -gt 0 ]; then reason_prefix="Combined (memory: $memory_based_max, traffic: $traffic_based_max)" fi # CRITICAL: Ensure we never recommend 0 or invalid values if [ -z "$final_max_children" ] || [ "$final_max_children" -le 0 ]; then final_max_children="20" reason_prefix="Safe default (calculation failed or returned invalid value)" fi # Recommend pm mode local pm_result pm_result=$(recommend_pm_mode "$peak_concurrent" "$((peak_concurrent / 2))" "$stability_factor") local pm_mode min_spare max_spare pm_reason pm_mode=$(get_field "$pm_result" 1) min_spare=$(get_field "$pm_result" 2) max_spare=$(get_field "$pm_result" 3) pm_reason=$(get_field "$pm_result" 4) echo "$final_max_children|$pm_mode|$min_spare|$max_spare|$reason_prefix: $pm_reason" } # ============================================================================ # NEW: THREE-CONSTRAINT INTELLIGENT OPTIMIZATION # ============================================================================ # Calculate optimal settings using three constraints for maximum intelligence: # 1. Memory constraint - what available RAM allows # 2. Traffic constraint - what actual usage suggests # 3. Fair share constraint - proportional allocation based on traffic # Uses the MINIMUM of all three for maximum safety and fairness # Usage: calculate_optimal_php_settings_intelligent # Returns: max_children|pm_mode|min_spare|max_spare|limiting_factor|reason calculate_optimal_php_settings_intelligent() { local username="$1" local total_ram_mb="$2" local total_server_capacity="$3" local traffic_percentage="$4" if [ -z "$username" ] || [ -z "$total_ram_mb" ] || [ -z "$total_server_capacity" ]; then echo "0|dynamic|1|5|ERROR|Invalid parameters" return fi # Default traffic percentage if not provided (equal distribution) [ -z "$traffic_percentage" ] && traffic_percentage=50 # CONSTRAINT 1: Memory-based max (what RAM allows) local memory_result memory_result=$(calculate_max_children_memory_based "$username" "$total_ram_mb") local memory_based_max memory_based_max=$(get_field "$memory_result" 1) # CONSTRAINT 2: Traffic-based max (what traffic patterns suggest) local traffic_result traffic_result=$(calculate_peak_concurrent_requests_improved "$username" 7) local peak_concurrent stability_factor peak_concurrent=$(get_field "$traffic_result" 1) stability_factor=$(get_field "$traffic_result" 2) local traffic_based_max=0 if [ "$peak_concurrent" -gt 0 ]; then local traffic_calc traffic_calc=$(calculate_max_children_traffic_based "$peak_concurrent" "$stability_factor") traffic_based_max=$(get_field "$traffic_calc" 1) else traffic_based_max=$memory_based_max # No traffic data, use memory as basis fi # CONSTRAINT 3: Fair share (proportional allocation based on traffic %) local fair_share_result fair_share_result=$(calculate_max_children_fair_share "$total_server_capacity" "$traffic_percentage") local fair_share_max fair_share_max=$(get_field "$fair_share_result" 1) # USE THE MINIMUM OF ALL THREE CONSTRAINTS local final_max_children="$memory_based_max" local limiting_factor="Memory constraint ($memory_based_max max_children)" if [ "$traffic_based_max" -lt "$final_max_children" ]; then final_max_children="$traffic_based_max" limiting_factor="Traffic (peak $peak_concurrent concurrent requests)" fi if [ "$fair_share_max" -lt "$final_max_children" ]; then final_max_children="$fair_share_max" limiting_factor="Fair share constraint (${traffic_percentage}% traffic allocation)" fi # CRITICAL: Ensure we never recommend 0 or invalid values if [ -z "$final_max_children" ] || [ "$final_max_children" -le 0 ]; then final_max_children="20" limiting_factor="Safe default (calculation failed)" fi # Recommend pm mode local pm_result pm_result=$(recommend_pm_mode "$peak_concurrent" "$((peak_concurrent / 2))" "$stability_factor") local pm_mode min_spare max_spare pm_reason pm_mode=$(get_field "$pm_result" 1) min_spare=$(get_field "$pm_result" 2) max_spare=$(get_field "$pm_result" 3) pm_reason=$(get_field "$pm_result" 4) # Return with detailed explanation local reason="3-constraint intelligent: Mem=$memory_based_max, Traffic=$traffic_based_max, Share=$fair_share_max → $limiting_factor" echo "$final_max_children|$pm_mode|$min_spare|$max_spare|$limiting_factor|$reason" } # ============================================================================ # Export functions for use in other scripts # ============================================================================ export -f calculate_system_reserve export -f calculate_max_children_memory_based export -f calculate_peak_concurrent_requests_improved export -f calculate_max_children_traffic_based export -f detect_mysql_memory_usage export -f calculate_server_capacity export -f get_domain_traffic_percentage export -f calculate_max_children_fair_share export -f recommend_pm_mode export -f calculate_optimal_php_settings export -f calculate_optimal_php_settings_intelligent export -f get_field