# 事务机制

OpenViking 的事务机制为 AI Agent 上下文数据库提供可靠的操作保障，解决数据一致性、并发控制和错误恢复等核心问题。

## 概览

```
操作请求 → TransactionManager   →   锁保护   →   执行操作 → 状态更新
          ↓                          ↓              ↓
        事务ID分配和事务状态管理      路径锁校验和加锁


事务生命周期：开始操作 → 创建事务 → 锁保护生效 → 文件系统同步操作 → 摘要和索引异步操作 → 移除锁保护 → 事务结束
```

**设计原则**：
1. **最小化锁粒度**：仅支持路径锁机制，不实现复杂的 MVCC 等
2. **写互斥优先**：暂不实现读锁（共享锁），先承诺写操作的互斥性
3. **渐进式扩展**：避免过度设计，聚焦核心需求，未来需要时再添加更复杂的锁机制
4. **默认生效**：所有数据操作命令均开启事务机制，用户无需额外配置

## 核心需求分析

OpenViking 的数据操作命令（如 `add_resource`、`rm`、`mv` 等）存在以下无保护操作问题：

1. **并发冲突**：多个用户同时操作同一目录可能导致数据不一致
2. **无原子性**：`add_resource` 多阶段操作中，某个阶段失败可能留下中间状态
3. **无可观测性**：操作结果无法预测，用户无法直接观察到正在操作的状态

## 系统一致性要求

从系统分析的角度，OpenViking 要求实现组件间的分布式一致性：

1. **向量索引的最终一致**：所有上下文数据的向量表征依托独立的向量数据库或向量索引实现，要求确保在任何操作序列下，向量表示的更新都能实现最终一致
2. **文件系统的读写一致性**：所有上下文数据的文件系统表示依托 VikingFS 实现，底层为 AGFS 桥接的分布式文件系统，要求确保在任何操作序列下，文件系统的更新都能保证数据不会损坏或丢失
3. **队列和异步数据处理的一致性**：所有上下文数据的异步操作依托队列实现，要求确保在任何操作序列下，队列中的数据都能实现最终一致，即队列中的数据会最终被处理，不会丢失或重复

## TransactionManager（事务管理器）

TransactionManager 是全局单例，负责管理事务生命周期和锁机制实现。

### 核心职责

- 分配事务ID
- 管理事务生命周期（开始、提交、回滚）
- 提供事务的锁机制实现接口，防止死锁

### 关键特性

```
路径锁 + 写互斥 = 并发冲突防护
```

- **路径锁**：锁定目标目录，防止并发的目录级操作如目录删除、目录移动等
- **写互斥**：同一时间只允许一个事务写操作，路径锁机制确保所有写操作的互斥性
- **事务结束状态**：事务有明确的结束状态，包括完成、失败丢弃等

### 事务状态机

```
INIT → AQUIRE → EXEC → COMMIT/FAIL → RELEASING → RELEASED
```

**状态说明**：
- `INIT`：事务初始化完成，等待锁获取
- `AQUIRE`：正在获取锁资源
- `EXEC`：事务操作正在执行
- `COMMIT/FAIL`：事务执行完成，进入最终状态
- `RELEASING`：正在释放锁资源
- `RELEASED`：锁资源已完全释放，事务结束

### 事务记录属性

```python
TransactionRecord(
    id: str,                # 事务ID，采用 uuid 格式，唯一标识一个事务
    locks: List[str],       # 锁列表
    status: str,            # 当前状态
    init_info: Dict,        # 事务初始化信息
    rollback_info: Dict,    # 回滚信息
    created_at: float,      # 创建时间
    updated_at: float,      # 更新时间
)
```

### 设计决策

- 暂不实现共享锁（读锁），简化设计
- 锁粒度仅限目录，不实现范围锁机制
- 不实现复杂的死锁检测，通过超时机制防止死锁，事务超时后自动释放所有锁
- 支持可选的自下而上并行加锁模式，提升大型目录树操作的性能和一致性
- 事务状态机增加AQUIRE+RELEASING状态，明确跟踪锁释放过程，提高系统可观测性

## 锁机制

锁机制是事务管理的核心组件，当前只提供路径锁类型。

### 锁类型

| 锁类型 | 作用范围 | 用例 |
|--------|----------|------|
| 路径锁 | 整个目录 | 用于阻止目录被意外整体移动或删除，确保事务操作过程的路径合法性

### 锁协议

```
viking://resources/github/volcengine/OpenViking/.path.ovlock
```

- 锁文件存在即表示已加锁
- 文件内容为事务ID，用于标识当前事务
- 事务操作完成后，删除锁文件以释放锁

### 加锁流程

#### 普通操作加锁流程

```
1. 检查目标目录是否存在
2. 检查目标目录是否已被其他事务锁定
3. 检查目标目录的父目录是否已被其他事务锁定
4. 创建 .path.ovlock 文件，文件内容为事务ID
5. 再次检查目标目录的父目录是否已被其他事务锁定
6. 读取刚创建的 .path.ovlock 文件内容，确认为当前事务ID
7. 一切正常，则返回加锁成功
```

#### rm 操作加锁流程

```
# 传统串行模式：存在更大的竞态条件窗口
1. 检查目标目录是否存在
2. 检查目标目录是否已被其他事务锁定
3. 检查目标目录的父目录是否已被其他事务锁定
4. 在目标目录下创建 .path.ovlock 文件，文件内容为事务ID
5. 递归地在目标目录的所有子目录下创建 .path.ovlock 文件
6. 如果发生加锁失败，移除所有已经创建的 .path.ovlock 文件
7. 一切正常，则返回加锁成功

# 自下而上并行模式
1. 并行遍历整个目录树，收集所有子目录路径
2. 按照目录层级从深到浅排序，从最深层子目录开始
3. 以有限并行度（默认最大8）批量创建 .path.ovlock 文件
4. 最后锁定目标目录
5. 如果任一位置加锁失败，逆序移除所有已经创建的 .path.ovlock 文件
```

#### mv 操作加锁流程

```
1. 先参照 rm 操作对原目录进行加锁
2. 再参照普通操作过程对新目录进行加锁
```

### 锁机制性能分析

- 并行遍历采用广度优先策略，同时处理同一层级的所有目录
- 并行加锁从最深层开始，逐层向上锁定，确保整个目录树的一致性
- 有限并行度（默认最大8）避免AGFS服务过载
- 加锁失败时采用逆序回滚，确保所有已加锁目录都能正确释放
- 事务状态机明确区分锁管理过程（AQUIRE+RELEASING状态），提高系统可观测性和调试效率

## 相关文档

- [架构概述](./01-architecture.md) - 系统整体架构
- [存储架构](./05-storage.md) - AGFS 和向量库
- [会话管理](./08-session.md) - 会话和记忆管理