Postgresql17增量备份demo

#include <iostream>
#include <string>
#include <vector>
#include <filesystem>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cstdlib>
#include <io.h>
#include <direct.h>


namespace fs = std::filesystem;
using namespace std::chrono;

// 执行外部命令(Windows)
int exec_cmd(const std::string& cmd) {
    std::cout << "[CMD] " << cmd << std::endl;
    return std::system(cmd.c_str());
}

// 获取当前本地时间字符串
std::string current_local_time_str(const char* fmt = "%Y-%m-%d %H:%M:%S") {
    auto now = system_clock::now();
    std::time_t tt = system_clock::to_time_t(now);
    std::tm tm{};
    localtime_s(&tm, &tt);
    std::ostringstream oss;
    oss << std::put_time(&tm, fmt);
    return oss.str();
}

// 获取当前日期(%Y-%m-%d)
std::string get_date() {
    return current_local_time_str("%Y-%m-%d");
}

// 获取星期几(0=Sunday, 6=Saturday)
int get_weekday() {
    auto now = system_clock::now();
    std::time_t tt = system_clock::to_time_t(now);
    std::tm tm{};
    localtime_s(&tm, &tt);
    return tm.tm_wday;
}

// 获取 N 天前的日期(字符串)
std::string get_date_offset(int days) {
    auto now = system_clock::now();
    auto past = now - hours(24 * days);
    std::time_t tt = system_clock::to_time_t(past);
    std::tm tm{};
    localtime_s(&tm, &tt);
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%d");
    return oss.str();
}

// 递归计算目录大小(字节)
uint64_t get_directory_size(const fs::path& dir) {
    uint64_t size = 0;
        for (const auto& entry : fs::recursive_directory_iterator(dir)) {
            if (entry.is_regular_file()) {
                size += entry.file_size();
            }
        }

    return size;
}

// 将字节数转为人类可读格式(KB, MB, GB)
std::string human_readable_size(uint64_t bytes) {
    const char* units[] = {"B", "KB", "MB", "GB", "TB"};
    int unit = 0;
    double size = static_cast<double>(bytes);
    while (size >= 1024.0 && unit < 4) {
        size /= 1024.0;
        ++unit;
    }
    std::ostringstream oss;
    if (unit == 0) {
        oss << static_cast<int>(size) << " " << units[unit];
    } else {
        oss << std::fixed << std::setprecision(1) << size << " " << units[unit];
    }
    return oss.str();
}

// ========== 备份逻辑 ==========
void do_backup() {
    set_pg_env();

    std::string BAK_BASE = "C:\\backup"; // Windows 路径
    std::string DATE = get_date();
    std::string YESTERDAY = get_date_offset(1);
    int WEEK_DAY = get_weekday();
    std::string BAK_DIR = BAK_BASE + "\\" + DATE + "-" + std::to_string(WEEK_DAY);

    std::cout << std::endl;
    std::string START_TIME = current_local_time_str();
    std::cout << "############## Physical backup start at " << START_TIME << " ##############" << std::endl;
    std::cout << std::endl;

    // 确保备份根目录存在
    fs::create_directories(BAK_BASE);

    std::string TYPE;
    int ret = -1;

    // 构建 pg_basebackup 路径(可配置)
    std::string PG_BASEBACKUP = "\"C:\\Program Files\\PostgreSQL\\15\\bin\\pg_basebackup.exe\"";

    if (WEEK_DAY == 6) {
        // Full backup on Saturday
        std::string cmd = PG_BASEBACKUP + " -Fp -D \"" + BAK_DIR + "\" -v";
        ret = exec_cmd(cmd);
        TYPE = "FULL";
    } else {
        std::string INCRE_BASE;
        if (WEEK_DAY == 0) {
            INCRE_BASE = BAK_BASE + "\\" + YESTERDAY + "-6";
        } else {
            INCRE_BASE = BAK_BASE + "\\" + YESTERDAY + "-" + std::to_string(WEEK_DAY - 1);
        }
        std::string manifest = INCRE_BASE + "\\backup_manifest";
        if (!fs::exists(manifest)) {
            std::cerr << "ERROR: Incremental base manifest not found: " << manifest << std::endl;
            return;
        }
        std::string cmd = PG_BASEBACKUP + " -Fp -D \"" + BAK_DIR + "\" -v -i \"" + manifest + "\"";
        ret = exec_cmd(cmd);
        TYPE = "INCR";
    }

    std::cout << std::endl;
    std::string END_TIME = current_local_time_str();
    std::cout << "############## Physical backup end at " << END_TIME << " ##############" << std::endl;
    std::cout << std::endl;

    if (ret == 0 && fs::exists(BAK_DIR)) {
        uint64_t size_bytes = get_directory_size(BAK_DIR);
        std::string SIZE = human_readable_size(size_bytes);

        std::cout << "Backup completed successfully." << std::endl;
        std::cout << "Backup is available at: " << BAK_DIR << "\\backup_label" << std::endl;
    } else {
        std::cerr << "Backup failed." << std::endl;
    }
}

// ========== 清理逻辑 ==========
void do_cleanup() {
    std::string BAK_BASE = "C:\\backup";
    std::cout << std::endl;
    std::string START_TIME = current_local_time_str();
    std::cout << "############## clean up start at " << START_TIME << " ##############" << std::endl;
    std::cout << std::endl;

    auto now = file_time_type::clock::now();
    auto cutoff = now - days(30); // 30天前

    std::vector<fs::path> to_delete;
        for (const auto& entry : fs::directory_iterator(BAK_BASE)) {
            if (entry.is_directory()) {
                auto last_write = entry.last_write_time();
                if (last_write < cutoff) {
                    std::cout << "Will delete: " << entry.path().string() << std::endl;
                    to_delete.push_back(entry.path());
                }
            }
        }

    // 执行删除
    for (const auto& dir : to_delete) {
        try {
            fs::remove_all(dir);
            std::cout << "Deleted: " << dir.string() << std::endl;
        } catch (const fs::filesystem_error& e) {
            std::cerr << "Failed to delete " << dir << ": " << e.what() << std::endl;
        }
    }

    std::cout << std::endl;
    std::string END_TIME = current_local_time_str();
    std::cout << "############## clean up end at " << END_TIME << " ##############" << std::endl;
    std::cout << std::endl;
}

// ========== 主函数 ==========
int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " [backup|cleanup]" << std::endl;
        return 1;
    }

    std::string mode = argv[1];
    if (mode == "backup") {
        do_backup();
    } else if (mode == "cleanup") {
        do_cleanup();
    } else {
        std::cerr << "Unknown mode: " << mode << std::endl;
        return 1;
    }

    return 0;
}

 

posted @ 2025-09-26 01:38  SandaiYoung  阅读(6)  评论(0)    收藏  举报