Spaces:
Running
Running
File size: 8,124 Bytes
5be0a06 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | #!/usr/bin/env python3
"""
智能备份控制器 - 方案B
- 纯变化检测:检测到变化后5分钟内备份
- 2小时最大间隔保险:超过2小时强制备份
- 180天备份保留
- 避免重复备份(5分钟内不重复触发)
"""
import os
import sys
import time
from datetime import datetime, timedelta
from pathlib import Path
import subprocess
import threading
BASE_DIR = "/home/node/.openclaw"
BACKUP_LOCK_FILE = "/tmp/.backup_in_progress"
LAST_BACKUP_TIME_FILE = "/tmp/.last_backup_timestamp"
MIN_BACKUP_INTERVAL = 300 # 5分钟内不重复备份(秒)
MAX_BACKUP_INTERVAL = 7200 # 2小时最大间隔(秒)
def get_last_backup_time():
"""获取上次备份时间"""
try:
if os.path.exists(LAST_BACKUP_TIME_FILE):
with open(LAST_BACKUP_TIME_FILE, 'r') as f:
return float(f.read().strip())
except:
pass
return 0
def set_last_backup_time():
"""设置上次备份时间"""
with open(LAST_BACKUP_TIME_FILE, 'w') as f:
f.write(str(time.time()))
def get_time_since_last_backup():
"""获取距离上次备份的时间(秒)"""
last_backup = get_last_backup_time()
if last_backup == 0:
return float('inf') # 从未备份过
return time.time() - last_backup
def should_backup():
"""检查是否应该备份(避免过于频繁)"""
return get_time_since_last_backup() >= MIN_BACKUP_INTERVAL
def need_forced_backup():
"""检查是否需要强制备份(超过2小时)"""
return get_time_since_last_backup() >= MAX_BACKUP_INTERVAL
def has_changes():
"""检查是否有重要变化(5分钟内)"""
try:
cutoff = time.time() - 300 # 5分钟
# 检查sessions目录
sessions_dir = Path(BASE_DIR) / "sessions"
if sessions_dir.exists():
for root, dirs, files in os.walk(sessions_dir):
for file in files:
try:
if os.path.getmtime(os.path.join(root, file)) > cutoff:
return True
except:
continue
# 检查agents目录(skill安装)
agents_dir = Path(BASE_DIR) / "agents"
if agents_dir.exists():
for agent_dir in agents_dir.iterdir():
if agent_dir.is_dir():
skills_dir = agent_dir / "skills"
if skills_dir.exists():
for root, dirs, files in os.walk(skills_dir):
for file in files:
try:
if os.path.getmtime(os.path.join(root, file)) > cutoff:
return True
except:
continue
# 检查workspace目录
workspace_dir = Path(BASE_DIR) / "workspace"
if workspace_dir.exists():
for root, dirs, files in os.walk(workspace_dir):
for file in files:
try:
if os.path.getmtime(os.path.join(root, file)) > cutoff:
return True
except:
continue
return False
except Exception as e:
print(f"[BACKUP-CONTROL] Error checking changes: {e}")
return False
def do_backup(reason="change-detected"):
"""执行备份"""
if not should_backup():
print(f"[BACKUP-CONTROL] Skipping backup, too soon (reason: {reason})")
return False
# 创建锁文件防止重复执行
if os.path.exists(BACKUP_LOCK_FILE):
print(f"[BACKUP-CONTROL] Backup already in progress")
return False
try:
with open(BACKUP_LOCK_FILE, 'w') as f:
f.write(str(time.time()))
print(f"[BACKUP-CONTROL] Starting backup (reason: {reason})...")
result = subprocess.run(
["python3", "/home/node/app/sync.py", "backup"],
capture_output=True,
text=True,
timeout=120
)
if result.returncode == 0:
set_last_backup_time()
print(f"[BACKUP-CONTROL] Backup completed successfully")
return True
else:
print(f"[BACKUP-CONTROL] Backup failed: {result.stderr}")
return False
except Exception as e:
print(f"[BACKUP-CONTROL] Backup error: {e}")
return False
finally:
try:
if os.path.exists(BACKUP_LOCK_FILE):
os.remove(BACKUP_LOCK_FILE)
except:
pass
def backup_control_loop():
"""
主控制循环
- 每5分钟检查一次
- 有变化则备份(5分钟内不重复)
- 超过2小时无备份则强制备份
"""
print("[BACKUP-CONTROL] Starting backup control loop...")
print("[BACKUP-CONTROL] Strategy: Change detection + 2-hour max interval")
print("[BACKUP-CONTROL] Retention: 180 days")
time_since_last = get_time_since_last_backup()
if time_since_last == float('inf'):
print("[BACKUP-CONTROL] No previous backup found, will backup on first change or 2-hour mark")
else:
print(f"[BACKUP-CONTROL] Time since last backup: {time_since_last/60:.1f} minutes")
# 启动后立即检查一次(如果是新启动)
if time_since_last == float('inf'):
print("[BACKUP-CONTROL] First run, checking for immediate backup need...")
if has_changes():
do_backup(reason="startup-changes")
while True:
time.sleep(300) # 每5分钟检查一次
time_since_last = get_time_since_last_backup()
# 策略1: 检查是否需要强制备份(超过2小时)
if need_forced_backup():
print(f"[BACKUP-CONTROL] No backup for {time_since_last/3600:.1f} hours, forcing backup...")
do_backup(reason="forced-2hour")
continue
# 策略2: 检查是否有变化
if has_changes():
if should_backup():
print("[BACKUP-CONTROL] Changes detected, triggering backup...")
do_backup(reason="change-detected")
else:
print(f"[BACKUP-CONTROL] Changes detected, but backup too soon ({time_since_last/60:.1f} min ago)")
else:
# 只有在快接近2小时时才打印日志
time_remaining = MAX_BACKUP_INTERVAL - time_since_last
if time_remaining < 600: # 如果剩余不到10分钟
print(f"[BACKUP-CONTROL] No changes. Forced backup in {time_remaining/60:.1f} minutes")
else:
print(f"[BACKUP-CONTROL] No changes in last 5 minutes")
def manual_backup():
"""手动触发备份"""
return do_backup(reason="manual")
def main():
"""主函数"""
if len(sys.argv) > 1:
if sys.argv[1] == "manual":
if manual_backup():
print("Manual backup completed")
sys.exit(0)
else:
print("Manual backup failed or skipped")
sys.exit(1)
elif sys.argv[1] == "check":
if has_changes():
print("Changes detected")
sys.exit(0)
else:
print("No changes")
sys.exit(1)
elif sys.argv[1] == "status":
time_since_last = get_time_since_last_backup()
if time_since_last == float('inf'):
print("No previous backup")
else:
print(f"Last backup: {time_since_last/60:.1f} minutes ago")
print(f"Next forced backup: in {(MAX_BACKUP_INTERVAL - time_since_last)/60:.1f} minutes")
if has_changes():
print("Changes detected: Yes")
else:
print("Changes detected: No")
sys.exit(0)
else:
# 启动主控制循环
backup_control_loop()
if __name__ == "__main__":
main() |