# 运行配置业务说明

本文档从业务视角说明配置字段含义、对阶段行为的影响，以及推荐的覆盖方式。

## 0. 配置约定

当前运行配置分成两层：

1. 输入配置
   - 你写在 `run_profiles/*.yaml` 或 `run_profiles/preset/*.default.yaml` 里的内容。
   - 只建议写必要覆盖项，不要把默认值全部展开。
2. Resolved Full Config
   - pipeline 启动时打印并写入日志的全量配置。
   - 其中已经补齐 datasource 默认值、策略默认值和日志/持久化默认值。

也就是说：
- 模板应当尽量短。
- 排障时看启动日志里的 **Resolved Full Config**。

### 0.1 一个最小输入示例

```yaml
local_datasource:
  type: jsonl
  jsonl_dir: ${PROJECT_ROOT}/filtered_datasource/datasource/filtered
remote_datasource:
  type: jsonl
  jsonl_dir: ${PROJECT_ROOT}/remote_datasource/datasource
persist:
  db_path: ${PROJECT_ROOT}/test_results/sync_state_machine/demo.db
  backend: sqlite
  backend_options: {}
strategies:
  project:
    config:
      auto_bind: true
      auto_bind_fields:
        - name
```

### 0.2 对应的全量 resolved 结果骨架

```yaml
node_types:
  - company
  - supplier
  - user
  - project
  ...
local_datasource:
  type: jsonl
  jsonl_dir: /abs/path/filtered_datasource/datasource/filtered
  read_only: false
  handler_configs: {}
remote_datasource:
  type: jsonl
  jsonl_dir: /abs/path/remote_datasource/datasource
  read_only: false
  handler_configs: {}
strategies:
  - node_type: project
    skip_sync: false
    config:
      auto_bind: true
      auto_bind_fields:
        - name
      pre_bind_data_id: []
      depend_fields:
        company_id: company
      local_orphan_action: create_remote
      remote_orphan_action: none
      update_direction: push
      field_direction_key: null
      field_direction_overrides: {}
      post_check_ignore_fields: []
      compare_ignore_fields: []
persist:
  backend: sqlite
  db_path: /abs/path/test_results/sync_state_machine/demo.db
  backend_options: {}
  enable: true
  wipe_on_start: false
logging:
  initialize: true
  file_path: null
  file_dir: null
  file_name_pattern: null
  level: 20
  suppress_node_logs: true
```

## 1. 推荐使用方式（先 default，再覆盖）

1. 先使用基线配置运行：
  - `run_profiles/preset/jsonl_to_jsonl.default.yaml`
2. 若需项目定制，再复制为：
   - `run_profiles/jsonl_to_jsonl.local.yaml`
3. 仅改有业务差异的字段，避免一次改太多导致排障困难。

占位符：
- `${PROJECT_ROOT}` 会在运行时替换为项目根目录绝对路径。

## 2. 顶层字段（你在改什么）

- `node_types`
  - 本轮参与同步的业务类型列表。
  - 顺序会影响依赖链执行时机（当前按 node_type 串行执行 bind/create/update）。
- `local_datasource` / `remote_datasource`
  - 分别定义两侧数据来源与执行端。
- `strategies`
  - 每个业务类型的行为策略（依赖、自动绑定、创建方向、更新方向、跳过等）。
- `persist`
  - 持久化开关与数据库位置。
- `persist.backend`
  - 持久化后端注册表里的名称，例如 `sqlite`。
  - backend 实现会自行解释 `persist` 的其余字段。
- `persist.backend_options`
  - 后端私有配置，后端自己决定如何解释。

### 6. 持久化后端注册

如果你希望在 backend 初始化阶段替换持久化后端，可以先在启动代码里按名称注册，再通过配置选择，不需要改调用方。

#### 6.1 内置 SQLite

```yaml
persist:
  backend: sqlite
  db_path: ${PROJECT_ROOT}/test_results/sync_state_machine/demo.db
  backend_options: {}
```

#### 6.2 注册自定义后端

```python
from sync_state_machine.common.persistence import register_persistence_backend

register_persistence_backend("mysql", lambda config: MySQLPersistenceBackend(config))
```

然后在配置里选择这个名字：

```yaml
persist:
  backend: mysql
  db_path: ${PROJECT_ROOT}/test_results/sync_state_machine/demo.db
  backend_options:
    url: mysql+aiomysql://user:pass@127.0.0.1/depm_sync?charset=utf8mb4
    pool_size: 5
```

后端实现会收到整份 `PersistConfig`，自行决定是读取 `db_path`、`backend_options`，还是额外扩展别的字段。
- `logging`
  - 运行日志输出位置与级别。

## 3. 数据源字段（DataSource）

### 3.1 JSONL 数据源

- `type=jsonl`：使用本地 JSONL 文件作为数据源。
- `jsonl_dir`：JSONL 目录路径。
- `read_only=false`：允许该侧执行 create/update 回写；若为 true，通常用于只读加载。
- `handler_configs.project.data_id_filter=[]`：项目白名单；为空时不进行项目筛选。
- `handler_configs`：按节点类型下发给各业务 handler 的扩展参数。

### 3.2 API 数据源

- `type=api`：对接 HTTP API。
- `api_base_url` / `api_uid` / `api_secret`：接口访问参数。
- `api_debug`：开启后输出更多调试信息。
- `poll_max_retries` / `poll_interval`：异步任务轮询次数与间隔。
- `handler_configs.project.data_id_filter`：建议显式配置的项目白名单；至少需要指定一个项目ID，以避免加载过多远程数据。
- `handler_configs`：业务 handler 扩展参数。

## 4. 策略字段（Strategy）

每个 node_type 的 **Resolved Full Config** 都会包含以下字段，但输入配置不需要全部显式写出。

### 4.1 `update_direction`

- `push`：本地变更推送到远端。
- `pull`：远端变更拉取到本地。
- `bidirectional`：双向更新（需要明确冲突策略，谨慎使用）。
- `none`：关闭 update 阶段。

### 4.2 `local_orphan_action` / `remote_orphan_action`

- `create_remote`：本地孤儿创建到远端。
- `create_local`：远端孤儿创建到本地。
- `none`：不自动创建。
- `delete_local` / `delete_remote`：当前主流程未作为默认路径，谨慎使用。

### 4.3 `auto_bind` / `auto_bind_fields` / `pre_bind_data_id`

- `auto_bind=true`：在 `S02(MISSING)` 上尝试自动匹配。
- `auto_bind=false`：不会做自动匹配，也不会走自动创建识别路径；通常会进入阻断态等待人工处理。
- `auto_bind_fields`：自动匹配使用的业务键字段。
- `pre_bind_data_id`：显式 data_id 预绑定清单（通用能力），格式为：
  - `- local_data_id: "..."`
    `  remote_data_id: "..."`
  - 规则：若两侧数据源都存在这对 data_id 对应节点，且当前无绑定记录，则直接绑定。
  - 不依赖 `auto_bind/auto_bind_fields`，即使 `auto_bind=false` 也会执行。
  - 不做业务键一对一匹配检查，按你给定的映射对优先执行。

### 4.4 `depend_fields`

- 定义依赖字段到依赖 node_type 的映射，例如：`company_id -> company`。
- 依赖节点状态非 `NORMAL` 时，会在 bind 的依赖检查阶段进入 `DEPENDENCY_ERROR`。

### 4.5 `skip_sync`

- 运行配置中已不建议使用 `skip_sync`。
- 推荐使用显式组合：
  - `local_orphan_action: none`
  - `remote_orphan_action: none`
  - `update_direction: none`
- `skip_sync=true` 的当前语义是：
  - 仍会执行该 node_type 的 load / bind
  - 只跳过 create / update 写入阶段
- 适合“保留绑定与依赖可见性，但临时禁止写入”的场景。
- `none/none/none` 是更推荐的输入风格，因为它表达的是“该类型不做创建、不做补本地、不做更新”。
- 这组配置不会绕过前置 load / bind / 依赖检查；它只会把该类型变成无写入策略。

### 4.6 Handler 级参数（`handler_configs`）

- 建议将 handler 特有参数放在 datasource 的 `handler_configs.<node_type>` 下。
- 示例：
  - `remote_datasource.handler_configs.supplier.load_mode: persisted_only`
- `persisted_only` 表示只使用已持久化数据，不主动从外部拉新数据（具体以对应 handler 实现为准）。

### 4.7 `field_direction_key` / `field_direction_overrides`

- 这两个字段只在少数 node_type 上需要，典型例子是 `project_detail_data`。
- `field_direction_key`
  - 指定从节点 data 里取哪个字段作为“方向查表键”。
  - 例如 `key`，表示用 `data["key"]` 决定这条记录是 push 还是 pull。
- `field_direction_overrides`
  - 指定“字段值 -> 更新方向”的映射。
  - 没命中的值继续使用默认 `update_direction`。

示例：

```yaml
project_detail_data:
  config:
    field_direction_key: key
    field_direction_overrides:
      ensure_production: pull
      climb_production: pull
      challenge_production: pull
```

含义：
- `key=ensure_production/climb_production/challenge_production` 时，从远端拉回本地。
- 其余 key 继续按默认 `update_direction=push` 处理。

## 5. 阶段行为与配置关系

- bind 阶段
  - `depend_fields` 决定是否做依赖校验。
  - `pre_bind_data_id` 先按显式 local/remote data_id 映射做预绑定。
  - `auto_bind/auto_bind_fields` 决定是否继续进行业务键自动绑定。
- create 阶段
  - `local_orphan_action` / `remote_orphan_action` 决定孤儿是否创建。
- update 阶段
  - `update_direction` 决定是否和向哪侧更新。
- 写入后 reload（Pipeline 内置）
  - 对 API / 需要重新拉取权威状态的数据源，create 或 update 成功后仍会按 node_type reload。
  - 对纯 JSONL -> JSONL pipeline，reload 会跳过，直接复用当前内存状态。
  - 目的：避免对离线 JSONL 数据做无意义重复扫描，同时保留 API 数据源的权威状态刷新。
- 同步后一致性检查（Pipeline 内置）
  - 每个 node_type 在本轮同步后会基于绑定对执行数据一致性校验（比较时忽略 `id/_id` 字段）。
  - 若发现差异，会在日志中打印差异样本，并在汇总表新增 `Consistent` 列展示 `Y` / `N(差异数)`。
- 全局跳过
  - `local_orphan_action=none` + `remote_orphan_action=none` + `update_direction=none`
    可将该类型收敛为“只做 load/bind，不做 create/update 写入”的只读同步策略。

## 6. 覆盖配置建议

- 先在 default 跑通一轮，再做 local 覆盖。
- 每次只改一类问题相关字段（例如只改 `project` 的依赖与自动绑定）。
- 覆盖后优先看 `sync_log` 是否出现预期状态迁移。

## 7. 持久化与日志字段

### `persist`

- `enable`：是否开启持久化。
- `db_path`：SQLite 路径。
- `wipe_on_start`：启动是否清理历史持久化。

### `logging`

- `file_dir`：日志目录。
- `file_name_pattern`：文件命名模板。
- `level`：日志级别。

排障建议：运行失败时优先看日志中的 `状态机流转` 与 `reason=`，再对照 `docs/state_machine.md` 与 `docs/node_state_definition.md` 理解含义。
