Finishes the pass: every script now documents its safeguards, and the deliberate absences in the sourced libraries are recorded so they are not "corrected" later.
260 lines
11 KiB
Bash
Executable File
260 lines
11 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 MODEL
|
||
# ==============================================================================================
|
||
#
|
||
# 1. Read the current pm.max_children from the PHP-FPM pool config
|
||
# 2. Already at or above the target → exit silently, no write, no restart
|
||
# 3. Otherwise rewrite the value and restart PHP-FPM via the adapter
|
||
# 4. Verify PHP-FPM came back up
|
||
#
|
||
# Runs at array start, before the WebGUI sees real load. The setting does not survive an
|
||
# unRAID update — the OS replaces the pool config — which is why this reapplies every boot
|
||
# rather than being a one-time install step.
|
||
#
|
||
# ==============================================================================================
|
||
# 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
|
||
|
||
platform_require_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
|
||
|
||
echo "Config updated"
|
||
|
||
# ── Restart PHP-FPM ──────────────────────────────────────────────────────────────────────────
|
||
log "Restarting PHP-FPM..."
|
||
if ! platform_restart_service php-fpm; 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
|
||
echo "Verified: pm.max_children = $APPLIED_VAL ✅"
|
||
fi
|
||
|
||
FPM_WORKERS=$(pgrep -fc "php-fpm" 2>/dev/null || echo "?")
|
||
log "$ICON_PHP Workers running: $FPM_WORKERS"
|
||
|
||
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 |