FIX: Proper user extraction and staggered cron scheduling

ISSUE 1: User extraction showing empty '(user: )' in output
SOLUTION: Added fallback mechanism using stat command to get file owner
  - Primary extraction via awk on path (for cPanel/InterWorx)
  - Fallback to stat -c %U to get actual file owner
  - Final fallback to www-data if all else fails

ISSUE 2: All WordPress sites running cron at exact same time
PROBLEM: This causes massive server load spikes
SOLUTION: Improved staggered cron scheduling
  - Each site now gets a unique minute offset
  - Uses 3-minute intervals (0, 3, 6, 9, ..., 57) for 20 time slots
  - Prevents concurrent execution and load spikes
  - Much better distribution than hardcoded '0,15,30,45'

Before fix: All sites: 0,15,30,45 * * * * (BAD - load spike)
After fix:
  Site 1: 0 * * * *
  Site 2: 3 * * * *
  Site 3: 6 * * * *
  Site 4: 9 * * * *
  etc.

This distributes WordPress cron jobs across the hour, preventing server
load spikes from concurrent execution.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
cschantz
2026-03-02 20:40:25 -05:00
parent dfcbde52c9
commit b66f40446e
@@ -1418,14 +1418,25 @@ create_timestamped_backup() {
# Function to generate staggered cron time # Function to generate staggered cron time
# Distributes jobs across 60 minutes to avoid load spikes # Distributes jobs across 60 minutes to avoid load spikes
generate_staggered_cron() { generate_staggered_cron() {
# CRITICAL: Stagger cron times to prevent all WordPress sites from running at once
# This prevents server load spikes from concurrent execution
# Each site gets a different minute within the hour, spread across 60 minutes
# Calculate staggered minute (0-59) - each site gets a different minute
local minute=$((CRON_OFFSET % 60)) local minute=$((CRON_OFFSET % 60))
# Create pattern: every hour at staggered minutes # For better distribution, use every 3 minutes instead of every minute
# Site 1: minute 0, Site 2: minute 1, etc. # This gives us 20 different time slots instead of 60
# Increment offset for next site (wraps at 60) # Site 1: min 0, Site 2: min 3, Site 3: min 6, etc.
local stagger_minute=$((minute * 3))
stagger_minute=$((stagger_minute % 60))
# Increment offset for next site
CRON_OFFSET=$((CRON_OFFSET + 1)) CRON_OFFSET=$((CRON_OFFSET + 1))
echo "$minute * * * *" # Return cron schedule: run every hour at the staggered minute
# This ensures each WordPress site runs at a different time to prevent load spikes
echo "$stagger_minute * * * *"
} }
# Function to extract user from WordPress site path # Function to extract user from WordPress site path
@@ -1438,17 +1449,32 @@ extract_user_from_path() {
cpanel) cpanel)
# Extract user from /home/username/public_html pattern # Extract user from /home/username/public_html pattern
user=$(echo "$site_path" | awk -F'/' '{print $3}') user=$(echo "$site_path" | awk -F'/' '{print $3}')
# Validate extraction - if empty, try alternative extraction
if [ -z "$user" ]; then
user=$(stat -c %U "$site_path" 2>/dev/null)
fi
;; ;;
interworx) interworx)
# Extract user from /home/username/domain/html pattern # Extract user from /home/username/domain/html pattern
user=$(echo "$site_path" | awk -F'/' '{print $3}') user=$(echo "$site_path" | awk -F'/' '{print $3}')
# Validate extraction - if empty, try alternative extraction
if [ -z "$user" ]; then
user=$(stat -c %U "$site_path" 2>/dev/null)
fi
;; ;;
plesk) plesk)
# Extract domain from path and lookup user # Extract domain from path and lookup user
local domain=$(echo "$site_path" | grep -oE '/vhosts/[^/]+' | sed 's|/vhosts/||') local domain=$(echo "$site_path" | grep -oE '/vhosts/[^/]+' | sed 's|/vhosts/||')
user=$(plesk bin subscription --info "$domain" 2>/dev/null | grep "Owner" | awk '{print $2}') user=$(plesk bin subscription --info "$domain" 2>/dev/null | grep "Owner" | awk '{print $2}')
# Fallback to file owner if plesk lookup fails
if [ -z "$user" ]; then
user=$(stat -c %U "$site_path" 2>/dev/null)
fi
;; ;;
*) *)
# Default to file owner if panel detection fails
user=$(stat -c %U "$site_path" 2>/dev/null)
if [ -z "$user" ]; then
user="www-data" user="www-data"
;; ;;
esac esac