"""
Ch6 配套代码 1 / 3 —— RDB 触发与观察

演示：
  1. 写入一批数据后用 BGSAVE 异步触发 RDB 快照
  2. 通过 LASTSAVE 时间戳前后对比，确认快照已落盘
  3. 用 INFO persistence 解读 rdb_changes_since_last_save / latest_fork_usec
     等关键字段，体会 fork + COW 在生产中的可观测性
"""

import time
import redis

r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)


def section(title: str) -> None:
    print("\n" + "=" * 60)
    print(title)
    print("=" * 60)


def fmt_ts(ts) -> str:
    if hasattr(ts, "timestamp"):
        ts = int(ts.timestamp())
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(ts)))


def demo_prepare_data(n: int = 2000) -> None:
    section(f"Demo 1: 写入 {n} 个 key 作为 RDB 的『原料』")
    r.flushdb()
    pipe = r.pipeline()
    for i in range(n):
        pipe.set(f"rdb:key:{i}", f"value-{i}-{'x' * 20}")
    pipe.execute()
    print(f"  已写入 {r.dbsize()} 个 key")


def demo_lastsave_around_bgsave() -> None:
    section("Demo 2: BGSAVE 前后观察 LASTSAVE")

    before = r.lastsave()
    print(f"  BGSAVE 之前  LASTSAVE = {fmt_ts(before)}")

    print("  → 触发 BGSAVE ...")
    r.bgsave()

    for _ in range(20):
        time.sleep(0.5)
        now = r.lastsave()
        if now != before:
            print(f"  BGSAVE 完成  LASTSAVE = {fmt_ts(now)}")
            return
    print("  ⚠️ 等待 10s 仍未观察到 LASTSAVE 变化（数据集太大或被 throttle）")


def demo_info_persistence() -> None:
    section("Demo 3: INFO persistence 关键字段")
    info = r.info("persistence")

    fields = [
        ("rdb_changes_since_last_save",  "上次 save 后变更的 key 数（决定自动触发）"),
        ("rdb_bgsave_in_progress",       "当前是否正在 BGSAVE（1 = 是）"),
        ("rdb_last_save_time",           "上次成功 RDB 的 unix 时间戳"),
        ("rdb_last_bgsave_status",       "上次 BGSAVE 状态：ok / err"),
        ("rdb_last_bgsave_time_sec",     "上次 BGSAVE 耗时（秒）"),
        ("rdb_current_bgsave_time_sec",  "当前 BGSAVE 已运行秒数（-1 表示无）"),
        ("rdb_last_cow_size",            "上次 BGSAVE 期间 COW 复制的字节数"),
        ("latest_fork_usec",             "最近一次 fork 耗时（微秒，越大越卡）"),
    ]

    print(f"  {'字段':<35} {'值':<18} 含义")
    print(f"  {'-'*35} {'-'*18} {'-'*40}")
    for k, desc in fields:
        v = info.get(k, "N/A")
        if k == "rdb_last_save_time" and isinstance(v, int):
            v = f"{v} ({fmt_ts(v)})"
        print(f"  {k:<35} {str(v):<18} {desc}")


def demo_save_vs_bgsave_note() -> None:
    section("Demo 4: SAVE / BGSAVE / 自动触发 三档对比")
    print("""
    SAVE   ➜ 主线程同步执行，期间所有客户端命令阻塞
            ➜ 数据量大时阻塞数秒，⚠️ 生产严禁使用

    BGSAVE ➜ fork 子进程，主线程继续服务
            ➜ fork 本身仍有短暂阻塞（页表复制），可观察 latest_fork_usec

    自动   ➜ save m n 配置；任意一条满足即触发 BGSAVE
            ➜ 默认：3600s/1次, 300s/100次, 60s/10000次
            ➜ 禁用：CONFIG SET save ""
    """)


if __name__ == "__main__":
    try:
        demo_prepare_data()
        demo_lastsave_around_bgsave()
        demo_info_persistence()
        demo_save_vs_bgsave_note()
    except redis.ConnectionError as e:
        print(f"❌ Redis 连接失败: {e}")
