244 lines
10 KiB
Bash
Executable File
244 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
||
# ==============================================================================================
|
||
# ============================= PHP-FPM Max Children ===========================================
|
||
# ==============================================================================================
|
||
#
|
||
# PURPOSE
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Raises PHP-FPM pm.max_children to prevent WebGUI slowdowns under load. Run
|
||
# once at array start via ARRAY_START_SCRIPTS. Idempotent — silent when the
|
||
# value is already correct, no restart on clean boot.
|
||
#
|
||
# unRAID's WebGUI runs through PHP-FPM. The default pm.max_children is very
|
||
# low (4–8). Under load — multiple users, Docker operations, heavy dashboard
|
||
# usage — all PHP workers saturate and new requests queue. The WebGUI becomes
|
||
# slow or unresponsive.
|
||
#
|
||
# PHP_MAX_CHILDREN=250 is appropriate for 128GB RAM: ~2MB per worker = ~500MB
|
||
# total. Too high wastes RAM; too low causes slowdowns.
|
||
#
|
||
# ==============================================================================================
|
||
# DESIGN PRINCIPLES
|
||
# ==============================================================================================
|
||
#
|
||
# Idempotent Content Check
|
||
# Reads the current pm.max_children value before writing. If already at
|
||
# target → silent exit, no PHP-FPM restart. Restarting PHP-FPM unnecessarily
|
||
# disrupts active WebGUI sessions on every boot.
|
||
#
|
||
# Pattern Match Before Write
|
||
# Verifies the sed pattern finds pm.max_children in the config before
|
||
# applying any change. Prevents silent failures where sed succeeds but
|
||
# writes nothing because the key was missing or commented out.
|
||
#
|
||
# Apply Sequence
|
||
# 1. Read current pm.max_children from PHP_CONF
|
||
# 2. If already at target → exit silently
|
||
# 3. Verify sed pattern matches before writing
|
||
# 4. Apply sed replacement
|
||
# 5. Restart PHP-FPM via rc.php-fpm
|
||
# 6. Verify PHP-FPM process running after restart
|
||
# 7. Read back config to confirm value applied
|
||
#
|
||
# ==============================================================================================
|
||
# OPERATIONAL SAFEGUARDS
|
||
# ==============================================================================================
|
||
#
|
||
# Root Required
|
||
# Writing to /etc/php83/ requires root.
|
||
#
|
||
# Single Instance Lock
|
||
# acquire_lock prevents concurrent runs at array start.
|
||
#
|
||
# Process Verify
|
||
# Confirms PHP-FPM running after restart — errors if it failed to start.
|
||
#
|
||
# Config Verify
|
||
# Reads back config after restart to confirm the value was actually applied.
|
||
#
|
||
# Silent on Success
|
||
# Runs every boot — no noise when already correct.
|
||
#
|
||
# ==============================================================================================
|
||
# CONFIGURATION
|
||
# ==============================================================================================
|
||
#
|
||
# master.conf
|
||
#
|
||
# PHP_MAX_CHILDREN
|
||
# Target pm.max_children value. (default: 250)
|
||
#
|
||
# PHP_CONF
|
||
# Path to PHP-FPM www.conf. (default: /etc/php83/php-fpm.d/www.conf)
|
||
#
|
||
# ==============================================================================================
|
||
# RUNTIME MODES
|
||
# ==============================================================================================
|
||
#
|
||
# php_fpm_max_children.sh
|
||
# Read current value. Update and restart PHP-FPM only if changed. Silent if correct.
|
||
#
|
||
# php_fpm_max_children.sh --dry-run
|
||
# Show current vs target value. No config write or restart.
|
||
#
|
||
# php_fpm_max_children.sh --status
|
||
# Show current pm.max_children, target, and PHP-FPM process state.
|
||
#
|
||
# php_fpm_max_children.sh --log
|
||
# Verbose output showing idempotent check, config write, and restart result.
|
||
#
|
||
# ==============================================================================================
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
||
source "$SCRIPT_DIR/../load_config.sh"
|
||
|
||
parse_args "$@"
|
||
|
||
# ==============================================================================================
|
||
# ━━━ Setup ━━━
|
||
# ==============================================================================================
|
||
if [[ "$EUID" -ne 0 ]]; then
|
||
error "Must be run as root — writing system config requires root"
|
||
exit 1
|
||
fi
|
||
|
||
validate_unraid_cmd \
|
||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||
"" "" \
|
||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||
|
||
validate_int PHP_MAX_CHILDREN "$PHP_MAX_CHILDREN"
|
||
require_var PHP_CONF
|
||
|
||
acquire_lock
|
||
|
||
detect_hosts
|
||
|
||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
|
||
|
||
# ==============================================================================================
|
||
# ━━━ Status ━━━
|
||
# ==============================================================================================
|
||
if [[ "$SHOW_STATUS" == true ]]; then
|
||
echo ""
|
||
echo "━━━━━ $ICON_SUMMARY PHP-FPM STATUS ━━━━━"
|
||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||
echo "$ICON_GEAR Config file: $PHP_CONF"
|
||
echo "$ICON_PHP Target: pm.max_children = $PHP_MAX_CHILDREN"
|
||
echo ""
|
||
|
||
if [[ -f "$PHP_CONF" ]]; then
|
||
CURRENT_VAL=$(grep -E "^pm\.max_children" "$PHP_CONF" 2>/dev/null | \
|
||
awk '{print $NF}')
|
||
if [[ "${CURRENT_VAL:-0}" -eq "$PHP_MAX_CHILDREN" ]]; then
|
||
echo " $ICON_SUCCESS Current: pm.max_children = $CURRENT_VAL (correct ✅)"
|
||
else
|
||
echo " $ICON_WARN Current: pm.max_children = ${CURRENT_VAL:-not set} (would update)"
|
||
fi
|
||
else
|
||
echo " $ICON_ERROR Config file not found: $PHP_CONF"
|
||
fi
|
||
|
||
echo ""
|
||
if pgrep -f "php-fpm" >/dev/null 2>&1; then
|
||
FPM_COUNT=$(pgrep -fc "php-fpm" 2>/dev/null || echo "?")
|
||
echo " $ICON_SUCCESS PHP-FPM: running ($FPM_COUNT worker(s))"
|
||
else
|
||
echo " $ICON_ERROR PHP-FPM: NOT running"
|
||
fi
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||
exit 0
|
||
fi
|
||
|
||
# ==============================================================================================
|
||
# ━━━ PHP-FPM Config ━━━
|
||
# ==============================================================================================
|
||
START=$(date +%s)
|
||
|
||
if [[ ! -f "$PHP_CONF" ]]; then
|
||
error "PHP config file not found: $PHP_CONF"
|
||
notify "PHP-FPM config not found on $(hostname) ($MY_ID) — $PHP_CONF missing" \
|
||
"PHP-FPM" "warning"
|
||
exit 1
|
||
fi
|
||
|
||
# ── Idempotent check ─────────────────────────────────────────────────────────────────────────
|
||
CURRENT_VAL=$(grep -E "^pm\.max_children" "$PHP_CONF" 2>/dev/null | awk '{print $NF}')
|
||
if [[ "${CURRENT_VAL:-0}" -eq "$PHP_MAX_CHILDREN" ]]; then
|
||
echo "pm.max_children already $PHP_MAX_CHILDREN ✅"
|
||
exit 0
|
||
fi
|
||
|
||
warn "pm.max_children: ${CURRENT_VAL:-not set} → $PHP_MAX_CHILDREN"
|
||
|
||
# ── Verify pattern exists before writing ─────────────────────────────────────────────────────
|
||
if ! grep -qE "^pm\.max_children" "$PHP_CONF" 2>/dev/null; then
|
||
error "pm.max_children not found in $PHP_CONF — cannot apply"
|
||
error "Add 'pm.max_children = $PHP_MAX_CHILDREN' to $PHP_CONF manually"
|
||
notify "PHP-FPM pm.max_children not found in config on $(hostname) ($MY_ID)" \
|
||
"PHP-FPM" "warning"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
warn "DRY RUN — would set pm.max_children = $PHP_MAX_CHILDREN in $PHP_CONF"
|
||
warn "DRY RUN — would restart PHP-FPM"
|
||
exit 0
|
||
fi
|
||
|
||
# ── Apply setting ─────────────────────────────────────────────────────────────────────────────
|
||
log "Applying pm.max_children = $PHP_MAX_CHILDREN..."
|
||
if ! sed -i "s/^pm\.max_children.*/pm.max_children = $PHP_MAX_CHILDREN/" "$PHP_CONF"; then
|
||
error "Failed to update $PHP_CONF"
|
||
notify "PHP-FPM config update failed on $(hostname) ($MY_ID)" \
|
||
"PHP-FPM" "warning"
|
||
exit 1
|
||
fi
|
||
|
||
log "Config updated"
|
||
|
||
# ── Restart PHP-FPM ──────────────────────────────────────────────────────────────────────────
|
||
log "Restarting PHP-FPM..."
|
||
if ! /etc/rc.d/rc.php-fpm restart >/dev/null 2>&1; then
|
||
error "PHP-FPM restart command failed"
|
||
notify "PHP-FPM restart failed on $(hostname) ($MY_ID)" \
|
||
"PHP-FPM" "warning"
|
||
exit 1
|
||
fi
|
||
|
||
sleep 3 # Allow PHP-FPM workers to initialise
|
||
|
||
# ── Verify process running ────────────────────────────────────────────────────────────────────
|
||
if ! pgrep -f "php-fpm" >/dev/null 2>&1; then
|
||
error "PHP-FPM not running after restart — WebGUI may be broken"
|
||
notify "PHP-FPM failed to start after config update on $(hostname) ($MY_ID)" \
|
||
"PHP-FPM" "warning"
|
||
exit 1
|
||
fi
|
||
|
||
# ── Verify config reflects target ────────────────────────────────────────────────────────────
|
||
APPLIED_VAL=$(grep -E "^pm\.max_children" "$PHP_CONF" 2>/dev/null | awk '{print $NF}')
|
||
if [[ "${APPLIED_VAL:-0}" -ne "$PHP_MAX_CHILDREN" ]]; then
|
||
warn "Config reads pm.max_children = ${APPLIED_VAL:-unknown} — expected $PHP_MAX_CHILDREN"
|
||
warn "Check $PHP_CONF manually"
|
||
else
|
||
log "Verified: pm.max_children = $APPLIED_VAL ✅"
|
||
fi
|
||
|
||
END=$(date +%s)
|
||
|
||
# ==============================================================================================
|
||
# ━━━ Summary ━━━
|
||
# ==============================================================================================
|
||
echo ""
|
||
echo "━━━━━ $ICON_SUMMARY PHP-FPM SUMMARY ━━━━━"
|
||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||
echo "$ICON_GEAR Config file: $PHP_CONF"
|
||
echo "$ICON_PHP Applied: pm.max_children = $PHP_MAX_CHILDREN"
|
||
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
||
echo ""
|
||
echo "$ICON_DONE Status: done ✅"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
exit 0 |