Skip to content

Commit 90905da

Browse files
committed
low disk space detection and script execution action
1 parent 2bc39ed commit 90905da

11 files changed

Lines changed: 477 additions & 0 deletions

File tree

documentation/OPTIONS.es.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,30 @@ Mapeo de opciones upstream (LayerSync → SimpleTuner):
11801180

11811181
> ℹ️ Los modelos transformer como PixArt, SD3 o Hunyuan usan los nombres de subcarpeta `transformer` y `transformer_ema`.
11821182
1183+
### `--disk_low_threshold`
1184+
1185+
- **Qué**: Espacio mínimo libre en disco requerido antes de guardar checkpoints.
1186+
- **Por qué**: Previene que el entrenamiento falle por errores de disco lleno al detectar espacio bajo tempranamente y tomar una acción configurada.
1187+
- **Formato**: Cadena de tamaño como `100G`, `50M`, `1T`, `500K`, o bytes simples.
1188+
- **Por defecto**: Ninguno (función desactivada)
1189+
1190+
### `--disk_low_action`
1191+
1192+
- **Qué**: Acción a tomar cuando el espacio en disco está por debajo del umbral.
1193+
- **Opciones**: `stop`, `wait`, `script`
1194+
- **Por defecto**: `stop`
1195+
- **Comportamiento**:
1196+
- `stop`: Termina el entrenamiento inmediatamente con un mensaje de error.
1197+
- `wait`: Hace bucle cada 30 segundos hasta que el espacio esté disponible. Puede esperar indefinidamente.
1198+
- `script`: Ejecuta el script especificado por `--disk_low_script` para liberar espacio.
1199+
1200+
### `--disk_low_script`
1201+
1202+
- **Qué**: Ruta a un script de limpieza para ejecutar cuando el espacio en disco es bajo.
1203+
- **Por qué**: Permite limpieza automatizada (ej: eliminar checkpoints antiguos, limpiar caché) cuando el espacio en disco es bajo.
1204+
- **Notas**: Solo se usa cuando `--disk_low_action=script`. El script debe ser ejecutable. Si el script falla o no libera suficiente espacio, el entrenamiento se detendrá con un error.
1205+
- **Por defecto**: Ninguno
1206+
11831207
---
11841208

11851209
## 📊 Registro y monitoreo

documentation/OPTIONS.hi.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,30 @@ Upstream option mapping (LayerSync → SimpleTuner):
11781178

11791179
> ℹ️ PixArt, SD3, या Hunyuan जैसे transformer मॉडल `transformer` और `transformer_ema` subfolder नाम उपयोग करते हैं।
11801180
1181+
### `--disk_low_threshold`
1182+
1183+
- **What**: checkpoint saves से पहले आवश्यक न्यूनतम खाली disk space।
1184+
- **Why**: disk full errors से training crash को रोकता है, कम space का जल्दी पता लगाकर configured action लेता है।
1185+
- **Format**: size string जैसे `100G`, `50M`, `1T`, `500K`, या plain bytes।
1186+
- **Default**: None (feature disabled)
1187+
1188+
### `--disk_low_action`
1189+
1190+
- **What**: disk space threshold से कम होने पर लिया जाने वाला action।
1191+
- **Choices**: `stop`, `wait`, `script`
1192+
- **Default**: `stop`
1193+
- **Behavior**:
1194+
- `stop`: error message के साथ training तुरंत समाप्त करता है।
1195+
- `wait`: space उपलब्ध होने तक हर 30 seconds में loop करता है। अनिश्चित काल तक प्रतीक्षा कर सकता है।
1196+
- `script`: space खाली करने के लिए `--disk_low_script` द्वारा specified script चलाता है।
1197+
1198+
### `--disk_low_script`
1199+
1200+
- **What**: disk space कम होने पर चलाने के लिए cleanup script का path।
1201+
- **Why**: disk space कम होने पर automated cleanup (जैसे पुराने checkpoints हटाना, cache clear करना) की अनुमति देता है।
1202+
- **Notes**: केवल `--disk_low_action=script` होने पर उपयोग होता है। script executable होना चाहिए। यदि script fail होती है या पर्याप्त space खाली नहीं करती, training error के साथ रुक जाएगी।
1203+
- **Default**: None
1204+
11811205
---
11821206

11831207
## 📊 Logging and Monitoring

documentation/OPTIONS.ja.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,30 @@ LayerSync は同一 Transformer 内の「学生」レイヤーを、より強い
11821182

11831183
> ℹ️ PixArt、SD3、Hunyuan などの Transformer モデルは `transformer``transformer_ema` のサブフォルダ名を使用します。
11841184
1185+
### `--disk_low_threshold`
1186+
1187+
- **内容**: チェックポイント保存前に必要な最小空きディスク容量。
1188+
- **理由**: ディスク容量不足を早期に検知して設定されたアクションを実行することで、ディスク満杯エラーによる学習クラッシュを防止します。
1189+
- **形式**: `100G``50M``1T``500K` のようなサイズ文字列、またはバイト数。
1190+
- **デフォルト**: なし(機能無効)
1191+
1192+
### `--disk_low_action`
1193+
1194+
- **内容**: ディスク容量がしきい値を下回った場合のアクション。
1195+
- **選択肢**: `stop``wait``script`
1196+
- **デフォルト**: `stop`
1197+
- **動作**:
1198+
- `stop`: エラーメッセージを表示して学習を即座に終了します。
1199+
- `wait`: 容量が回復するまで 30 秒ごとにループします。無限に待機する可能性があります。
1200+
- `script`: `--disk_low_script` で指定されたスクリプトを実行して空き容量を確保します。
1201+
1202+
### `--disk_low_script`
1203+
1204+
- **内容**: ディスク容量不足時に実行するクリーンアップスクリプトのパス。
1205+
- **理由**: ディスク容量不足時に自動クリーンアップ(古いチェックポイントの削除、キャッシュのクリアなど)を実行できます。
1206+
- **注意**: `--disk_low_action=script` の場合のみ使用されます。スクリプトは実行可能である必要があります。スクリプトが失敗したり、十分な容量を確保できなかった場合、学習はエラーで停止します。
1207+
- **デフォルト**: なし
1208+
11851209
---
11861210

11871211
## 📊 ログとモニタリング

documentation/OPTIONS.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,30 @@ Upstream option mapping (LayerSync → SimpleTuner):
11781178

11791179
> ℹ️ Transformer models such as PixArt, SD3, or Hunyuan, use the `transformer` and `transformer_ema` subfolder names.
11801180
1181+
### `--disk_low_threshold`
1182+
1183+
- **What**: Minimum free disk space required before checkpoint saves.
1184+
- **Why**: Prevents training from crashing due to disk full errors during checkpoint saves by detecting low space early and taking a configured action.
1185+
- **Format**: Size string like `100G`, `50M`, `1T`, `500K`, or plain bytes.
1186+
- **Default**: None (feature disabled)
1187+
1188+
### `--disk_low_action`
1189+
1190+
- **What**: Action to take when disk space is below threshold.
1191+
- **Choices**: `stop`, `wait`, `script`
1192+
- **Default**: `stop`
1193+
- **Behavior**:
1194+
- `stop`: Ends training immediately with an error message.
1195+
- `wait`: Loops every 30 seconds until space becomes available. May wait indefinitely.
1196+
- `script`: Runs the script specified by `--disk_low_script` to free up space.
1197+
1198+
### `--disk_low_script`
1199+
1200+
- **What**: Path to a cleanup script to run when disk space is low.
1201+
- **Why**: Allows automated cleanup (e.g., removing old checkpoints, clearing cache) when disk space runs low.
1202+
- **Notes**: Only used when `--disk_low_action=script`. The script must be executable. If the script fails or doesn't free enough space, training will stop with an error.
1203+
- **Default**: None
1204+
11811205
---
11821206

11831207
## 📊 Logging and Monitoring

documentation/OPTIONS.pt-BR.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,30 @@ Mapeamento de opcoes upstream (LayerSync → SimpleTuner):
11761176

11771177
> ℹ️ Modelos transformer como PixArt, SD3 ou Hunyuan usam os subdiretorios `transformer` e `transformer_ema`.
11781178
1179+
### `--disk_low_threshold`
1180+
1181+
- **O que**: Espaco minimo livre em disco necessario antes de salvar checkpoints.
1182+
- **Por que**: Previne falhas no treinamento por erros de disco cheio detectando espaco baixo antecipadamente e tomando uma acao configurada.
1183+
- **Formato**: String de tamanho como `100G`, `50M`, `1T`, `500K`, ou bytes simples.
1184+
- **Padrao**: Nenhum (funcionalidade desativada)
1185+
1186+
### `--disk_low_action`
1187+
1188+
- **O que**: Acao a tomar quando o espaco em disco esta abaixo do limite.
1189+
- **Opcoes**: `stop`, `wait`, `script`
1190+
- **Padrao**: `stop`
1191+
- **Comportamento**:
1192+
- `stop`: Encerra o treinamento imediatamente com uma mensagem de erro.
1193+
- `wait`: Faz loop a cada 30 segundos ate o espaco ficar disponivel. Pode esperar indefinidamente.
1194+
- `script`: Executa o script especificado por `--disk_low_script` para liberar espaco.
1195+
1196+
### `--disk_low_script`
1197+
1198+
- **O que**: Caminho para um script de limpeza a executar quando o espaco em disco esta baixo.
1199+
- **Por que**: Permite limpeza automatizada (ex: remover checkpoints antigos, limpar cache) quando o espaco em disco esta baixo.
1200+
- **Notas**: Usado apenas quando `--disk_low_action=script`. O script deve ser executavel. Se o script falhar ou nao liberar espaco suficiente, o treinamento parara com um erro.
1201+
- **Padrao**: Nenhum
1202+
11791203
---
11801204

11811205
## 📊 Logging e monitoramento

documentation/OPTIONS.zh.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,30 @@ LayerSync 通过在同一 Transformer 内让“学生”层对齐更强的“教
11841184

11851185
> ℹ️ PixArt、SD3、Hunyuan 等 Transformer 模型使用 `transformer``transformer_ema` 子目录名称。
11861186
1187+
### `--disk_low_threshold`
1188+
1189+
- **内容**:检查点保存前所需的最小可用磁盘空间。
1190+
- **原因**:通过提前检测磁盘空间不足并采取配置的操作,防止训练因磁盘已满错误而崩溃。
1191+
- **格式**:大小字符串,如 `100G``50M``1T``500K`,或纯字节数。
1192+
- **默认**:无(功能禁用)
1193+
1194+
### `--disk_low_action`
1195+
1196+
- **内容**:磁盘空间低于阈值时采取的操作。
1197+
- **选项**`stop``wait``script`
1198+
- **默认**`stop`
1199+
- **行为**
1200+
- `stop`:立即停止训练并显示错误消息。
1201+
- `wait`:每 30 秒循环检查直到空间可用。可能无限等待。
1202+
- `script`:运行 `--disk_low_script` 指定的脚本以释放空间。
1203+
1204+
### `--disk_low_script`
1205+
1206+
- **内容**:磁盘空间不足时运行的清理脚本路径。
1207+
- **原因**:允许在磁盘空间不足时自动清理(如删除旧检查点、清除缓存)。
1208+
- **注意**:仅在 `--disk_low_action=script` 时使用。脚本必须可执行。如果脚本失败或未能释放足够空间,训练将停止并报错。
1209+
- **默认**:无
1210+
11871211
---
11881212

11891213
## 📊 日志与监控

simpletuner/helpers/configuration/cmd_args.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,31 @@ def _normalize_input_args(raw_args):
12981298
"Install it with: pip install sageattention"
12991299
)
13001300

1301+
# Disk low space detection validation
1302+
disk_threshold = getattr(args, "disk_low_threshold", None)
1303+
if disk_threshold not in (None, "", "None"):
1304+
from simpletuner.helpers.training.disk_space import DiskLowAction, parse_size_threshold
1305+
1306+
try:
1307+
parse_size_threshold(disk_threshold)
1308+
except ValueError as exc:
1309+
raise ValueError(str(exc)) from exc
1310+
1311+
disk_action = getattr(args, "disk_low_action", None)
1312+
if disk_action not in (None, "", "None"):
1313+
args.disk_low_action = DiskLowAction.from_raw(disk_action)
1314+
else:
1315+
args.disk_low_action = DiskLowAction.STOP
1316+
1317+
if args.disk_low_action == DiskLowAction.SCRIPT:
1318+
script_value = getattr(args, "disk_low_script", None)
1319+
if script_value in (None, "", "None"):
1320+
raise ValueError("--disk_low_script is required when --disk_low_action=script.")
1321+
script_path = os.path.expanduser(str(script_value).strip())
1322+
if not os.path.isfile(script_path):
1323+
raise ValueError(f"Disk cleanup script not found: {script_path}")
1324+
args.disk_low_script = script_path
1325+
13011326
deprecated_options = {
13021327
# how to deprecate options:
13031328
# "flux_beta_schedule_alpha": "flow_beta_schedule_alpha",
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
"""Disk space monitoring utilities for checkpoint saves."""
2+
3+
import logging
4+
import re
5+
import shutil
6+
import subprocess
7+
import time
8+
from enum import Enum
9+
from pathlib import Path
10+
from typing import Any, Optional
11+
12+
logger = logging.getLogger("DiskSpaceMonitor")
13+
14+
15+
class DiskLowAction(str, Enum):
16+
"""Action to take when disk space is below threshold."""
17+
18+
STOP = "stop"
19+
WAIT = "wait"
20+
SCRIPT = "script"
21+
22+
@classmethod
23+
def from_raw(cls, raw_value: Any) -> "DiskLowAction":
24+
"""Convert a raw config/CLI value to DiskLowAction enum."""
25+
if isinstance(raw_value, cls):
26+
return raw_value
27+
if raw_value in (None, "", "None"):
28+
return cls.STOP
29+
normalized = str(raw_value).strip().lower()
30+
try:
31+
return cls(normalized)
32+
except ValueError as exc:
33+
valid_values = ", ".join(member.value for member in cls)
34+
raise ValueError(f"Unsupported disk_low_action '{raw_value}'. Expected one of: {valid_values}") from exc
35+
36+
37+
def parse_size_threshold(threshold_str: Optional[str]) -> Optional[int]:
38+
"""
39+
Parse a human-readable size string into bytes.
40+
41+
Args:
42+
threshold_str: Size string like "100G", "50M", "1T", "500K", or plain bytes.
43+
Returns None if threshold_str is None/empty (feature disabled).
44+
45+
Returns:
46+
Size in bytes, or None if feature is disabled.
47+
48+
Raises:
49+
ValueError: If the format is invalid.
50+
"""
51+
if threshold_str in (None, "", "None"):
52+
return None
53+
54+
threshold_str = str(threshold_str).strip().upper()
55+
56+
match = re.match(r"^(\d+(?:\.\d+)?)\s*([KMGT]?)B?$", threshold_str)
57+
if not match:
58+
raise ValueError(
59+
f"Invalid disk_low_threshold format: '{threshold_str}'. "
60+
"Expected format like '100G', '50M', '1T', '500K', or plain bytes."
61+
)
62+
63+
value = float(match.group(1))
64+
unit = match.group(2)
65+
66+
multipliers = {
67+
"": 1,
68+
"K": 1024,
69+
"M": 1024**2,
70+
"G": 1024**3,
71+
"T": 1024**4,
72+
}
73+
74+
return int(value * multipliers[unit])
75+
76+
77+
def get_available_disk_space(path: str) -> int:
78+
"""
79+
Return available disk space in bytes for the filesystem containing path.
80+
81+
If the path doesn't exist, traverses parent directories to find an existing one.
82+
"""
83+
resolved_path = Path(path).resolve()
84+
while not resolved_path.exists() and resolved_path.parent != resolved_path:
85+
resolved_path = resolved_path.parent
86+
87+
usage = shutil.disk_usage(str(resolved_path))
88+
return usage.free
89+
90+
91+
def _format_bytes(num_bytes: int) -> str:
92+
"""Format bytes as human-readable string."""
93+
value = float(num_bytes)
94+
for unit in ["B", "KB", "MB", "GB", "TB"]:
95+
if abs(value) < 1024.0:
96+
return f"{value:.1f}{unit}"
97+
value /= 1024.0
98+
return f"{value:.1f}PB"
99+
100+
101+
def check_disk_space(
102+
output_dir: str,
103+
threshold_bytes: int,
104+
action: DiskLowAction,
105+
script_path: Optional[str] = None,
106+
check_interval: int = 30,
107+
) -> None:
108+
"""
109+
Check if available disk space is below threshold and take configured action.
110+
111+
Args:
112+
output_dir: Directory to check disk space for.
113+
threshold_bytes: Minimum required free space in bytes.
114+
action: Action to take when space is low.
115+
script_path: Path to cleanup script (required when action is SCRIPT).
116+
check_interval: Seconds between checks in WAIT mode.
117+
118+
Raises:
119+
RuntimeError: When action is STOP, or when SCRIPT fails, or when
120+
space remains low after SCRIPT execution.
121+
"""
122+
available = get_available_disk_space(output_dir)
123+
124+
if available >= threshold_bytes:
125+
return
126+
127+
available_human = _format_bytes(available)
128+
threshold_human = _format_bytes(threshold_bytes)
129+
130+
if action == DiskLowAction.STOP:
131+
raise RuntimeError(
132+
f"Disk space critically low: {available_human} available, " f"threshold is {threshold_human}. Training stopped."
133+
)
134+
135+
elif action == DiskLowAction.WAIT:
136+
logger.warning(
137+
"Disk space low: %s available (threshold: %s). " "Waiting for space to become available...",
138+
available_human,
139+
threshold_human,
140+
)
141+
while available < threshold_bytes:
142+
time.sleep(check_interval)
143+
available = get_available_disk_space(output_dir)
144+
logger.info(
145+
"Disk space recovered: %s available. Resuming training.",
146+
_format_bytes(available),
147+
)
148+
149+
elif action == DiskLowAction.SCRIPT:
150+
if not script_path:
151+
raise RuntimeError("disk_low_action is 'script' but no disk_low_script configured.")
152+
logger.warning(
153+
"Disk space low: %s available (threshold: %s). Running cleanup script: %s",
154+
available_human,
155+
threshold_human,
156+
script_path,
157+
)
158+
try:
159+
subprocess.run([script_path], check=True)
160+
except subprocess.CalledProcessError as exc:
161+
raise RuntimeError(f"Disk cleanup script failed with exit code {exc.returncode}") from exc
162+
except FileNotFoundError as exc:
163+
raise RuntimeError(f"Disk cleanup script not found: {script_path}") from exc
164+
165+
available = get_available_disk_space(output_dir)
166+
if available < threshold_bytes:
167+
raise RuntimeError(
168+
f"Disk space still low after cleanup script: "
169+
f"{_format_bytes(available)} available, threshold is {threshold_human}."
170+
)
171+
logger.info("Disk cleanup script completed. %s now available.", _format_bytes(available))

0 commit comments

Comments
 (0)