Bump ramdisk to 10G; add ramdisk_stop.sh; fix info→log typo in common.sh

host1.conf: HOST1_RAMDISK_SIZE 8G→10G, WARN_GB 6.8→8.5, LOW_GB 5.5→7.
ramdisk_stop.sh: new Tools/ script — redirects symlink to SSD before unmount
  so Emby keeps writing during maintenance, auto-falls back to lazy umount
  when only directory handles are open (no active writes).
common.sh: fix `info` call at line 595 (undefined) → `log`.
Tools docs: added ramdisk_stop.sh to README table, categories, diagram, and Manual.
This commit is contained in:
Gmer4Lfe
2026-05-22 17:35:16 -04:00
parent 8a3e22c9b8
commit 16e3b7651d
5 changed files with 416 additions and 7 deletions
+68
View File
@@ -20,6 +20,7 @@ making any changes.
- [recreate_shares.sh](#recreate_sharessh) - [recreate_shares.sh](#recreate_sharessh)
- [continuous_scripts_status.sh](#continuous_scripts_statussh) - [continuous_scripts_status.sh](#continuous_scripts_statussh)
- [claude_startup.sh](#claude_startupsh) - [claude_startup.sh](#claude_startupsh)
- [ramdisk_stop.sh](#ramdisk_stopsh)
- [Adding a New Tool](#adding-a-new-tool) - [Adding a New Tool](#adding-a-new-tool)
--- ---
@@ -631,6 +632,73 @@ claude_startup.sh --setup # set up symlinks only — no launch (for array_star
--- ---
## ramdisk_stop.sh
Safely stops the transcode ramdisk: redirects the transcode symlink to the SSD
fallback first (so Emby continues writing without interruption), then unmounts the
tmpfs and updates the state file. Primary use case is stopping the current ramdisk
before re-running `ramdisk_setup.sh` with new size or threshold values.
### When to Use
```
Bumping RAMDISK_SIZE — setup script is idempotent, skips remount if already mounted
→ stop first, then re-run ramdisk_setup.sh with new HOST*_RAMDISK_SIZE value
Adjusting RAMDISK_WARN_GB / RAMDISK_LOW_GB thresholds
→ no need to stop for threshold changes (transcode_manager reads vars live)
→ only needed if you're also changing the size
Temporarily freeing ramdisk RAM — reclaim tmpfs back to general memory pool
→ stop, restart later with ramdisk_setup.sh
```
### Stop Sequence
```
1. Redirect symlink: TRANSCODE_LINK → TRANSCODE_SSD
Emby immediately writes to SSD — no broken-path window during unmount
2. Check for active transcode files on ramdisk (warn, don't block)
Files in progress on the ramdisk are lost on unmount — expected for maintenance
3. Unmount ramdisk
Regular umount first; if busy (directory handles only, no active writes)
falls back to lazy unmount automatically
4. Update /tmp/transcode_state.db → current_target=TRANSCODE_SSD
transcode_manager.sh reads this on its next cycle
```
### transcode_manager Warning
If `transcode_manager.sh` is running, it may flip the symlink back to the ramdisk
on its next cycle (once the ramdisk is unmounted, that flip will fail). Stop
`transcode_manager.sh` first if you need the SSD redirect to hold before remounting.
### After Stopping
```bash
# Update host*.conf with new size values:
# HOST1_RAMDISK_SIZE="10G"
# HOST1_RAMDISK_WARN_GB=8.5
# HOST1_RAMDISK_LOW_GB=7
# Remount at new size:
bash Transcodes/ramdisk_setup.sh
```
### Usage
```bash
ramdisk_stop.sh --status # show mount state, symlink, active files — always check first
ramdisk_stop.sh --dry-run # show what would happen without making changes
ramdisk_stop.sh # stop the ramdisk
ramdisk_stop.sh --log # verbose — show each step
```
---
## Adding a New Tool ## Adding a New Tool
Write the tool when you solve a problem manually with bash commands. You'll face it again. Write the tool when you solve a problem manually with bash commands. You'll face it again.
+3 -1
View File
@@ -76,7 +76,7 @@ something new, write the tool. Store it here. Find it at 2am next time.
`bulk_permissions_repair.sh`, `zfs_pool_scrub.sh` `bulk_permissions_repair.sh`, `zfs_pool_scrub.sh`
**Lifecycle Tools** — Backup, setup, and migration support **Lifecycle Tools** — Backup, setup, and migration support
`container_data_export.sh`, `recreate_shares.sh`, `claude_startup.sh` `container_data_export.sh`, `recreate_shares.sh`, `claude_startup.sh`, `ramdisk_stop.sh`
**Library Sync Bootstrap** — Close the gap between Emby and arr libraries **Library Sync Bootstrap** — Close the gap between Emby and arr libraries
`emby_to_lidarr_sync.sh`, `emby_to_sonarr_sync.sh`, `emby_to_radarr_sync.sh` `emby_to_lidarr_sync.sh`, `emby_to_sonarr_sync.sh`, `emby_to_radarr_sync.sh`
@@ -125,6 +125,7 @@ The relationship is one-way: Tools act on state that other scripts have written.
| `recreate_shares.sh` | Share directories missing after fresh install or disk rebuild | After fresh unRAID install or disk replacement on HOST2 | | `recreate_shares.sh` | Share directories missing after fresh install or disk rebuild | After fresh unRAID install or disk replacement on HOST2 |
| `continuous_scripts_status.sh` | Need a live view of watchdog and fallback state | Any time — manual dashboard, no schedule | | `continuous_scripts_status.sh` | Need a live view of watchdog and fallback state | Any time — manual dashboard, no schedule |
| `claude_startup.sh` | Claude Code session setup after reboot — symlinks persistent storage | After each unRAID reboot, or called by array_started.sh | | `claude_startup.sh` | Claude Code session setup after reboot — symlinks persistent storage | After each unRAID reboot, or called by array_started.sh |
| `ramdisk_stop.sh` | Safely stop the transcode ramdisk — redirect symlink to SSD, unmount, update state | Before re-running ramdisk_setup.sh with new size or thresholds |
| `emby_to_lidarr_sync.sh` | Add all Emby album artists not yet tracked in Lidarr | After Lidarr setup, database wipe, or when you suspect gaps | | `emby_to_lidarr_sync.sh` | Add all Emby album artists not yet tracked in Lidarr | After Lidarr setup, database wipe, or when you suspect gaps |
| `emby_to_sonarr_sync.sh` | Add all Emby TV series not yet tracked in Sonarr | After Sonarr setup, database wipe, or when you suspect gaps | | `emby_to_sonarr_sync.sh` | Add all Emby TV series not yet tracked in Sonarr | After Sonarr setup, database wipe, or when you suspect gaps |
| `emby_to_radarr_sync.sh` | Add all Emby movies not yet tracked in Radarr | After Radarr setup, database wipe, or when you suspect gaps | | `emby_to_radarr_sync.sh` | Add all Emby movies not yet tracked in Radarr | After Radarr setup, database wipe, or when you suspect gaps |
@@ -151,6 +152,7 @@ Situation arises
│ recreate_shares ◄── fresh HOST2 setup or disk rebuild │ │ recreate_shares ◄── fresh HOST2 setup or disk rebuild │
│ continuous_scripts_status ◄── manual status check at any time │ │ continuous_scripts_status ◄── manual status check at any time │
│ claude_startup ◄── after each unRAID reboot │ │ claude_startup ◄── after each unRAID reboot │
│ ramdisk_stop ◄── before ramdisk resize / remount │
│ │ │ │
│ emby_to_lidarr_sync ◄── Lidarr setup / database wipe / gap │ │ emby_to_lidarr_sync ◄── Lidarr setup / database wipe / gap │
│ emby_to_sonarr_sync ◄── Sonarr setup / database wipe / gap │ │ emby_to_sonarr_sync ◄── Sonarr setup / database wipe / gap │
+339
View File
@@ -0,0 +1,339 @@
#!/bin/bash
# ==============================================================================================
# ================================= Ramdisk Stop ===============================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Safely stops the transcode ramdisk: redirects the transcode symlink to the
# SSD fallback before unmounting so Emby continues writing without interruption,
# then unmounts the tmpfs and updates the state file.
#
# Primary use case: stopping the current ramdisk before re-running
# ramdisk_setup.sh with new size or threshold values (setup is idempotent —
# if the ramdisk is mounted, it skips the mount and reports status, so you
# must stop it first to change the size).
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# Executes in safe order:
# 1. Validate — ramdisk mounted, SSD fallback exists
# 2. Redirect symlink → SSD (Emby immediately writes to SSD instead)
# 3. Warn if active transcode files still on ramdisk (informational — not a blocker)
# 4. Unmount ramdisk tmpfs
# 5. Update /tmp/transcode_state.db → current_target=TRANSCODE_SSD
#
# The symlink redirect happens before unmount so there is no window where Emby
# has nowhere to write. Existing in-progress transcode files on the ramdisk are
# lost on unmount — warn the user but proceed (this is expected for maintenance).
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Root Required
# umount requires root.
#
# Single Instance Lock
# acquire_lock prevents concurrent stop attempts.
#
# Mounted Check
# Exits cleanly if ramdisk is not mounted — nothing to do.
#
# Symlink-First Order
# Symlink is redirected before unmount — Emby never sees a broken path.
#
# transcode_manager Warning
# Warns if transcode_manager.sh is running — it may flip the symlink back
# to ramdisk on its next cycle. Stop transcode_manager before running this
# if you need the SSD redirect to hold.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
# HOST*_TRANSCODE_SSD SSD fallback directory — redirect target during stop.
# Aliased by detect_hosts() → TRANSCODE_SSD.
#
# master.conf
# TRANSCODE_LINK Symlink Emby uses. Must match Emby's transcode path setting.
# RAMDISK_PATH tmpfs mount point.
#
# ==============================================================================================
# STATE FILES
# ==============================================================================================
#
# /tmp/transcode_state.db — updated to current_target=TRANSCODE_SSD after stop.
# transcode_manager.sh reads this on its next cycle.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# ramdisk_stop.sh
# Stop the ramdisk: redirect symlink → SSD, unmount, update state.
#
# ramdisk_stop.sh --dry-run
# Show what would happen without making any changes.
#
# ramdisk_stop.sh --status
# Show current mount state, symlink target, active files on ramdisk. Exit.
#
# ramdisk_stop.sh --log
# Verbose output for each step.
#
# ==============================================================================================
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 — umount 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"
acquire_lock
detect_hosts
STATE_FILE="/tmp/transcode_state.db"
log "Identity: $MY_ID ($LOCAL_SERVER_NAME)"
log "Ramdisk: $RAMDISK_PATH"
log "Fallback: $TRANSCODE_SSD"
log "Symlink: $TRANSCODE_LINK"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_RAM Ramdisk path: $RAMDISK_PATH"
echo "$ICON_DISK SSD fallback: $TRANSCODE_SSD"
echo "$ICON_LINK Symlink: $TRANSCODE_LINK"
echo ""
if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
USAGE=$(df -BG "$RAMDISK_PATH" | awk 'NR==2 {print $3}')
AVAIL=$(df -BG "$RAMDISK_PATH" | awk 'NR==2 {print $4}')
echo " $ICON_RAM Ramdisk: mounted — $USAGE used / $AVAIL available ✅"
FILE_COUNT=$(find "$RAMDISK_PATH" -type f 2>/dev/null | wc -l)
echo " $ICON_RAM Active files on ramdisk: $FILE_COUNT"
else
echo " $ICON_RAM Ramdisk: NOT mounted"
fi
if [[ -L "$TRANSCODE_LINK" ]]; then
TARGET=$(readlink "$TRANSCODE_LINK")
echo " $ICON_LINK Symlink: $TRANSCODE_LINK$TARGET"
else
echo " $ICON_LINK Symlink: not set"
fi
if [[ -f "$STATE_FILE" ]]; then
echo ""
echo " State file ($STATE_FILE):"
while IFS='=' read -r key value; do
[[ -z "$key" ]] && continue
echo " $key = $value"
done < "$STATE_FILE"
else
echo " $ICON_INFO State file: not found (ramdisk never started this boot)"
fi
if pgrep -f "transcode_manager.sh" >/dev/null 2>&1; then
echo ""
warn "transcode_manager.sh is currently RUNNING"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
fi
# ==============================================================================================
# ━━━ Preflight ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR Preflight ━━━"
# Bail if not mounted — nothing to do
if ! mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
warn "Ramdisk is not mounted at $RAMDISK_PATH — nothing to stop"
exit 0
fi
log "Ramdisk is mounted ✅"
# Warn if transcode_manager is running — it may flip symlink back on next cycle
if pgrep -f "transcode_manager.sh" >/dev/null 2>&1; then
warn "transcode_manager.sh is currently RUNNING"
warn "It may flip the symlink back to ramdisk on its next cycle"
warn "Stop transcode_manager.sh first if you need the SSD redirect to hold"
echo ""
fi
# Confirm SSD fallback exists
if [[ ! -d "$TRANSCODE_SSD" ]]; then
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — SSD fallback does not exist: $TRANSCODE_SSD"
warn "DRY RUN — would create it before redirecting symlink"
else
warn "SSD fallback does not exist — creating: $TRANSCODE_SSD"
mkdir -p "$TRANSCODE_SSD" || {
error "Failed to create SSD fallback: $TRANSCODE_SSD"
error "Cannot safely redirect symlink — aborting"
exit 1
}
log "SSD fallback created ✅"
fi
else
log "SSD fallback exists: $TRANSCODE_SSD"
fi
START=$(date +%s)
STOP_SUCCESS=true
# ==============================================================================================
# ━━━ Redirect Symlink → SSD ━━━
# ==============================================================================================
# Redirect BEFORE unmount — Emby continues writing to SSD with no broken path window.
echo ""
echo "━━━ $ICON_LINK Redirect Symlink → SSD ━━━"
if [[ -L "$TRANSCODE_LINK" ]]; then
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK")
if [[ "$CURRENT_TARGET" == "$TRANSCODE_SSD" ]]; then
log "Symlink already points to SSD — no change needed"
else
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would redirect: $TRANSCODE_LINK$TRANSCODE_SSD"
else
ln -sfn "$TRANSCODE_SSD" "$TRANSCODE_LINK" && \
warn "Symlink redirected: $TRANSCODE_LINK$TRANSCODE_SSD" || {
error "Failed to redirect symlink"
STOP_SUCCESS=false
}
fi
fi
elif [[ -e "$TRANSCODE_LINK" ]]; then
warn "$TRANSCODE_LINK exists but is not a symlink — leaving as-is"
else
warn "Symlink $TRANSCODE_LINK does not exist — nothing to redirect"
fi
# ==============================================================================================
# ━━━ Active Files Warning ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_RAM Active Files Check ━━━"
FILE_COUNT=$(find "$RAMDISK_PATH" -type f 2>/dev/null | wc -l)
if [[ "$FILE_COUNT" -gt 0 ]]; then
warn "⚠️ $FILE_COUNT file(s) still on ramdisk — will be lost on unmount"
warn "Active transcode sessions should be stopped before unmounting"
warn "Proceeding regardless (this is expected for maintenance)"
if [[ "$LOG" == true ]]; then
find "$RAMDISK_PATH" -type f 2>/dev/null | while read -r f; do
log " $f"
done
fi
else
log "No active files on ramdisk ✅"
fi
# ==============================================================================================
# ━━━ Unmount ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_RAM Unmount Ramdisk ━━━"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would unmount: $RAMDISK_PATH"
else
if umount "$RAMDISK_PATH" 2>/dev/null; then
warn "Ramdisk unmounted: $RAMDISK_PATH"
else
# Regular unmount failed — check if only directory handles are open (no active writes)
OPEN_FILES=$(lsof +D "$RAMDISK_PATH" 2>/dev/null | awk 'NR>1 && $5 != "DIR"' | wc -l)
if [[ "$OPEN_FILES" -eq 0 ]]; then
warn "Busy — only directory handles open, no active writes — trying lazy unmount"
if umount -l "$RAMDISK_PATH"; then
warn "Ramdisk lazy-unmounted: $RAMDISK_PATH"
warn "Handles will release when owning processes next check the directory"
else
error "Lazy unmount also failed — $RAMDISK_PATH"
STOP_SUCCESS=false
fi
else
error "Failed to unmount $RAMDISK_PATH$OPEN_FILES file(s) still open for writing"
error "Stop active transcode sessions and retry"
STOP_SUCCESS=false
fi
fi
fi
# ==============================================================================================
# ━━━ Update State File ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR State File ━━━"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would update $STATE_FILE: current_target=$TRANSCODE_SSD"
elif [[ "$STOP_SUCCESS" == true ]]; then
NOW=$(date +%s)
cat > "$STATE_FILE" <<EOF
current_target=$TRANSCODE_SSD
last_flip_time=$NOW
flip_count_hour=0
flip_hour_start=$NOW
EOF
log "State file updated: current_target=$TRANSCODE_SSD"
else
warn "Skipping state file update — stop had errors"
fi
END=$(date +%s)
# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY RAMDISK STOP SUMMARY ━━━━━"
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_RAM Ramdisk: $RAMDISK_PATH"
echo "$ICON_DISK Fallback: $TRANSCODE_SSD"
echo "$ICON_LINK Symlink: $TRANSCODE_LINK$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "not set")"
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
echo ""
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — no changes made"
elif [[ "$STOP_SUCCESS" == true ]]; then
echo "$ICON_DONE Status: done ✅"
echo "Run ramdisk_setup.sh to remount with new configuration"
else
echo "$ICON_ERROR Status: STOP HAD ERRORS"
notify "Ramdisk stop errors on $(hostname) ($MY_ID) — check output" \
"Ramdisk Stop" "warning"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+1 -1
View File
@@ -592,7 +592,7 @@ detect_hosts() {
_alias_assoc "WATCHDOG_APPDATA_SIZES" _alias_assoc "WATCHDOG_APPDATA_SIZES"
# ── Output ──────────────────────────────────────────────────────────────── # ── Output ────────────────────────────────────────────────────────────────
info "$ICON_HOST Host: $LOCAL_SERVER_NAME$REMOTE_SERVER_NAME" log "$ICON_HOST Host: $LOCAL_SERVER_NAME$REMOTE_SERVER_NAME"
} }
# ============================================================================================== # ==============================================================================================
+5 -5
View File
@@ -513,13 +513,13 @@
# ============================================================================================== # ==============================================================================================
# Ramdisk size ceiling — tmpfs only uses RAM actually needed, not the full size upfront. # Ramdisk size ceiling — tmpfs only uses RAM actually needed, not the full size upfront.
# Real-world: 9 streams peaked at ~5.5GB — 8G gives comfortable headroom on 128GB RAM. # Real-world: 9 streams peaked at ~5.5GB — 10G gives generous headroom on 128GB RAM.
HOST1_RAMDISK_SIZE="8G" HOST1_RAMDISK_SIZE="10G"
# Usage thresholds — coupled to HOST1_RAMDISK_SIZE, adjust all three together if size changes. # Usage thresholds — coupled to HOST1_RAMDISK_SIZE, adjust all three together if size changes.
# Hysteresis gap (6.8 - 5.5 = 1.3GB) prevents flip-flop between ramdisk and SSD. # Hysteresis gap (8.5 - 7 = 1.5GB) prevents flip-flop between ramdisk and SSD.
HOST1_RAMDISK_WARN_GB=6.8 # flip to SSD when ramdisk usage reaches this HOST1_RAMDISK_WARN_GB=8.5 # flip to SSD when ramdisk usage reaches this
HOST1_RAMDISK_LOW_GB=5.5 # flip back to ramdisk when usage drops to this HOST1_RAMDISK_LOW_GB=7 # flip back to ramdisk when usage drops to this
# SSD fallback path — where transcodes land when ramdisk exceeds HOST1_RAMDISK_WARN_GB. # SSD fallback path — where transcodes land when ramdisk exceeds HOST1_RAMDISK_WARN_GB.
# Must be on cache pool — array disks too slow for active transcode writes. # Must be on cache pool — array disks too slow for active transcode writes.