284 lines
9.2 KiB
C++
284 lines
9.2 KiB
C++
|
|
#pragma once
|
|||
|
|
/**
|
|||
|
|
* @file transaction.h
|
|||
|
|
* @brief 极致性能 — 事务系统 + Command模式 + 无限撤销/重做 + 崩溃恢复
|
|||
|
|
*
|
|||
|
|
* 核心组件:
|
|||
|
|
* - Transaction : begin/commit/rollback 事务
|
|||
|
|
* - Command : 可撤销操作的抽象基类
|
|||
|
|
* - UndoManager : 无限撤销/重做栈 + 事务日志
|
|||
|
|
* - BrepCommand : BrepModel 操作的具体 Command 实现
|
|||
|
|
*
|
|||
|
|
* @ingroup core
|
|||
|
|
*/
|
|||
|
|
#include <vector>
|
|||
|
|
#include <string>
|
|||
|
|
#include <memory>
|
|||
|
|
#include <functional>
|
|||
|
|
#include <deque>
|
|||
|
|
#include <fstream>
|
|||
|
|
#include <sstream>
|
|||
|
|
#include <ctime>
|
|||
|
|
#include <chrono>
|
|||
|
|
#include <cstdint>
|
|||
|
|
#include <atomic>
|
|||
|
|
|
|||
|
|
namespace vde::core {
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
// Command 模式
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/// 可撤销操作的抽象基类
|
|||
|
|
///
|
|||
|
|
/// 每个 Command 封装一个原子操作,支持执行和撤销。
|
|||
|
|
/// 子类需实现 execute() 和 undo()。
|
|||
|
|
class Command {
|
|||
|
|
public:
|
|||
|
|
virtual ~Command() = default;
|
|||
|
|
|
|||
|
|
/// 执行操作
|
|||
|
|
virtual void execute() = 0;
|
|||
|
|
|
|||
|
|
/// 撤销操作(将状态恢复到执行前)
|
|||
|
|
virtual void undo() = 0;
|
|||
|
|
|
|||
|
|
/// 获取操作描述
|
|||
|
|
[[nodiscard]] virtual std::string description() const { return "Command"; }
|
|||
|
|
|
|||
|
|
/// 合并两个命令(可选优化)
|
|||
|
|
/// @return true 若合并成功(this 吸收了 other)
|
|||
|
|
virtual bool merge(Command* /*other*/) { return false; }
|
|||
|
|
|
|||
|
|
/// 命令 ID(用于日志)
|
|||
|
|
[[nodiscard]] int64_t id() const { return id_; }
|
|||
|
|
void set_id(int64_t id) { id_ = id; }
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
int64_t id_ = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 可调用对象包装的 Command
|
|||
|
|
///
|
|||
|
|
/// @code
|
|||
|
|
/// auto cmd = make_command(
|
|||
|
|
/// []{ do_something(); },
|
|||
|
|
/// []{ undo_it(); },
|
|||
|
|
/// "My operation");
|
|||
|
|
/// @endcode
|
|||
|
|
class LambdaCommand : public Command {
|
|||
|
|
public:
|
|||
|
|
using Func = std::function<void()>;
|
|||
|
|
|
|||
|
|
LambdaCommand(Func exec, Func undo_fn, std::string desc = "")
|
|||
|
|
: exec_(std::move(exec))
|
|||
|
|
, undo_(std::move(undo_fn))
|
|||
|
|
, desc_(std::move(desc)) {}
|
|||
|
|
|
|||
|
|
void execute() override { if (exec_) exec_(); }
|
|||
|
|
void undo() override { if (undo_) undo_(); }
|
|||
|
|
[[nodiscard]] std::string description() const override { return desc_; }
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
Func exec_;
|
|||
|
|
Func undo_;
|
|||
|
|
std::string desc_;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 便捷工厂函数
|
|||
|
|
inline std::unique_ptr<Command> make_command(
|
|||
|
|
std::function<void()> exec,
|
|||
|
|
std::function<void()> undo_fn,
|
|||
|
|
std::string desc = "")
|
|||
|
|
{
|
|||
|
|
return std::make_unique<LambdaCommand>(
|
|||
|
|
std::move(exec), std::move(undo_fn), std::move(desc));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
// 事务日志条目
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/// 事务日志条目
|
|||
|
|
///
|
|||
|
|
/// 持久化到事务日志文件,用于崩溃恢复。
|
|||
|
|
struct TransactionLogEntry {
|
|||
|
|
enum class Type : uint8_t {
|
|||
|
|
BEGIN = 0, ///< 事务开始
|
|||
|
|
COMMAND = 1, ///< 命令执行
|
|||
|
|
COMMIT = 2, ///< 事务提交
|
|||
|
|
ROLLBACK = 3, ///< 事务回滚
|
|||
|
|
CHECKPOINT = 4, ///< 检查点
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
Type type;
|
|||
|
|
int64_t transaction_id;
|
|||
|
|
int64_t command_id;
|
|||
|
|
int64_t timestamp; ///< unix 微秒时间戳
|
|||
|
|
std::string description; ///< 命令描述(COMMAND 类型时有效)
|
|||
|
|
|
|||
|
|
/// 序列化为一行
|
|||
|
|
[[nodiscard]] std::string serialize() const;
|
|||
|
|
/// 从一行反序列化
|
|||
|
|
static TransactionLogEntry deserialize(const std::string& line);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
// Transaction
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/// 事务
|
|||
|
|
///
|
|||
|
|
/// 封装一组 Command 的原子执行。支持 begin/commit/rollback。
|
|||
|
|
/// 事务在 commit 时将命令推送到 UndoManager。
|
|||
|
|
///
|
|||
|
|
/// @code
|
|||
|
|
/// Transaction txn;
|
|||
|
|
/// txn.begin();
|
|||
|
|
/// txn.execute(make_command(...));
|
|||
|
|
/// txn.execute(make_command(...));
|
|||
|
|
/// txn.commit(); // 或 txn.rollback()
|
|||
|
|
/// @endcode
|
|||
|
|
class Transaction {
|
|||
|
|
public:
|
|||
|
|
Transaction();
|
|||
|
|
~Transaction();
|
|||
|
|
|
|||
|
|
Transaction(const Transaction&) = delete;
|
|||
|
|
Transaction& operator=(const Transaction&) = delete;
|
|||
|
|
|
|||
|
|
/// 开始事务
|
|||
|
|
void begin();
|
|||
|
|
|
|||
|
|
/// 执行一条命令(加入事物缓冲区)
|
|||
|
|
/// @param cmd 要执行的命令
|
|||
|
|
void execute(std::unique_ptr<Command> cmd);
|
|||
|
|
|
|||
|
|
/// 提交事务(执行所有命令 → 加入 UndoManager)
|
|||
|
|
void commit();
|
|||
|
|
|
|||
|
|
/// 回滚事务(撤销已执行命令 → 丢弃)
|
|||
|
|
void rollback();
|
|||
|
|
|
|||
|
|
/// 是否处于活跃事务中
|
|||
|
|
[[nodiscard]] bool is_active() const { return active_; }
|
|||
|
|
|
|||
|
|
/// 当前缓冲命令数
|
|||
|
|
[[nodiscard]] size_t command_count() const { return commands_.size(); }
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
bool active_ = false;
|
|||
|
|
std::vector<std::unique_ptr<Command>> commands_;
|
|||
|
|
int64_t txn_id_ = 0;
|
|||
|
|
|
|||
|
|
static std::atomic<int64_t> next_txn_id_;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
// UndoManager — 无限撤销/重做
|
|||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/// 撤销管理器
|
|||
|
|
///
|
|||
|
|
/// 维护无限撤销栈和重做栈。
|
|||
|
|
/// 支持命令合并(连续同类型操作合并为一个)、事务日志与崩溃恢复。
|
|||
|
|
///
|
|||
|
|
/// @code
|
|||
|
|
/// UndoManager um;
|
|||
|
|
/// um.execute(make_command(...));
|
|||
|
|
/// um.undo(); // 撤销最后一步
|
|||
|
|
/// um.redo(); // 重做
|
|||
|
|
/// um.save_checkpoint("save/checkpoint.dat"); // 持久化
|
|||
|
|
/// @endcode
|
|||
|
|
class UndoManager {
|
|||
|
|
public:
|
|||
|
|
UndoManager();
|
|||
|
|
~UndoManager();
|
|||
|
|
|
|||
|
|
UndoManager(const UndoManager&) = delete;
|
|||
|
|
UndoManager& operator=(const UndoManager&) = delete;
|
|||
|
|
|
|||
|
|
/// 执行命令并推入撤销栈
|
|||
|
|
/// @param cmd 要执行的命令
|
|||
|
|
/// @param merge 是否尝试与栈顶命令合并
|
|||
|
|
void execute(std::unique_ptr<Command> cmd, bool merge = false);
|
|||
|
|
|
|||
|
|
/// 撤销最近一步操作
|
|||
|
|
/// @return true 若撤销成功
|
|||
|
|
bool undo();
|
|||
|
|
|
|||
|
|
/// 重做最近撤销的操作
|
|||
|
|
/// @return true 若重做成功
|
|||
|
|
bool redo();
|
|||
|
|
|
|||
|
|
/// 是否可撤销
|
|||
|
|
[[nodiscard]] bool can_undo() const { return !undo_stack_.empty(); }
|
|||
|
|
|
|||
|
|
/// 是否可重做
|
|||
|
|
[[nodiscard]] bool can_redo() const { return !redo_stack_.empty(); }
|
|||
|
|
|
|||
|
|
/// 撤销栈深度
|
|||
|
|
[[nodiscard]] size_t undo_depth() const { return undo_stack_.size(); }
|
|||
|
|
|
|||
|
|
/// 重做栈深度
|
|||
|
|
[[nodiscard]] size_t redo_depth() const { return redo_stack_.size(); }
|
|||
|
|
|
|||
|
|
/// 清空所有历史
|
|||
|
|
void clear();
|
|||
|
|
|
|||
|
|
/// 获取撤销栈顶命令描述
|
|||
|
|
[[nodiscard]] std::string undo_description() const;
|
|||
|
|
|
|||
|
|
/// 获取重做栈顶命令描述
|
|||
|
|
[[nodiscard]] std::string redo_description() const;
|
|||
|
|
|
|||
|
|
// ── 事务日志 ──
|
|||
|
|
|
|||
|
|
/// 开启事务日志(追加模式)
|
|||
|
|
/// @param filepath 日志文件路径
|
|||
|
|
/// @return true 若成功
|
|||
|
|
bool enable_log(const std::string& filepath);
|
|||
|
|
|
|||
|
|
/// 关闭事务日志
|
|||
|
|
void disable_log();
|
|||
|
|
|
|||
|
|
/// 是否开启了事务日志
|
|||
|
|
[[nodiscard]] bool log_enabled() const { return log_enabled_; }
|
|||
|
|
|
|||
|
|
/// 写入检查点
|
|||
|
|
void write_checkpoint();
|
|||
|
|
|
|||
|
|
/// 从日志文件恢复(崩溃恢复)
|
|||
|
|
/// @param filepath 日志文件路径
|
|||
|
|
/// @return 恢复的命令数(0 表示无日志或恢复失败)
|
|||
|
|
size_t recover_from_log(const std::string& filepath);
|
|||
|
|
|
|||
|
|
/// 设置最大撤销深度(0 = 无限)
|
|||
|
|
void set_max_undo_depth(size_t max_depth) { max_undo_depth_ = max_depth; }
|
|||
|
|
|
|||
|
|
/// 获取已执行命令总数
|
|||
|
|
[[nodiscard]] int64_t total_commands() const { return cmd_counter_.load(); }
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
std::deque<std::unique_ptr<Command>> undo_stack_;
|
|||
|
|
std::deque<std::unique_ptr<Command>> redo_stack_;
|
|||
|
|
size_t max_undo_depth_ = 0; // 0 = unlimited
|
|||
|
|
|
|||
|
|
// 事务日志
|
|||
|
|
bool log_enabled_ = false;
|
|||
|
|
std::ofstream log_file_;
|
|||
|
|
std::string log_path_;
|
|||
|
|
|
|||
|
|
std::atomic<int64_t> cmd_counter_{0};
|
|||
|
|
|
|||
|
|
/// 将命令序列化到日志文件
|
|||
|
|
void log_command(const Command& cmd, TransactionLogEntry::Type type);
|
|||
|
|
/// 从日志中重放一条命令
|
|||
|
|
std::unique_ptr<Command> replay_from_log(const TransactionLogEntry& entry);
|
|||
|
|
|
|||
|
|
/// 清理多余撤销历史
|
|||
|
|
void trim_undo_stack();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} // namespace vde::core
|