存储模块 gRPC 接入设计
本文描述 Storage 模块在 Bob-facing gRPC/proto 边界、TaskChannel 和配置边界上的设计。它不是 Deepsight Server 的 cold storage、MCP Layer、Memory Buffer、index 或 ticket manager 设计文档。 模块总览见存储模块设计,Probe 侧 eBPF、loader 和 transformer 设计见 存储模块 Probe 设计。
一、gRPC 边界职责
Storage 模块对 Deepsight Server 负责的边界是数据面和控制面契约。
数据面:
Probe -> PushTelemetry -> MetricWrapper{storage} / EventWrapper{storage}控制面:
TaskRequest{storage_args} -> Probe executor -> TaskResponse{trace_results / metric_results}Alice 负责:
- Storage proto 字段语义。
- Probe 侧采集、聚合、采样、截断和任务执行。
PushTelemetry中 Storage Metric/Event 的可解释性。TaskRequest.storage_args、TaskResponse.trace_results和TaskResponse.metric_results的失败语义和资源边界。
Bob 负责:
- Server 内部缓存、索引、防抖、查询、ticket 和 MCP 展示策略。
- 将上层诊断意图映射为已接受的 gRPC/TaskChannel 契约。
本文只描述 Bob-facing 契约边界,不规定 Bob-owned Server 内部结构。
二、PushTelemetry 数据面契约
Storage 模块复用现有 TelemetryBatch:
TelemetryBatch
-> MetricWrapper{storage}
-> EventWrapper{storage}
-> gRPC ingester
-> dispatcher2.1 Metric 输入
Storage Metric 应以低基数、可聚合形态进入 gRPC 边界。
Bob-facing 消费侧应能获得:
- metric kind 和 temporality。
- metric value 和 window。
- device identity,例如 major/minor、disk name。
- operation,例如 read、write、flush、discard。
- layer 和 phase,例如 block issue、block complete、block queue。
- latency bucket 和 reason class 等当前 proto 已接受的低基数字段。
当前已接受 Metric kind:
READ_BYTESWRITE_BYTESREAD_OPSWRITE_OPSREQUEST_LATENCY_BUCKETERROR_COUNT
Future/unavailable Metric 候选不属于当前 Bob-facing 契约,Bob 侧不得把它们作为 Memory schema、 Resource schema 或 Tool schema 的必需输入:
queue_depth/in_flight_requestsretry_count/timeout_countflush_latency/fsync_latencywriteback_pressureprocess_io_rate
gRPC 边界不要求 Probe 了解 Bob 如何存储热窗口、计算 Resource 或输出 JSON。
2.2 Event 输入
Storage Event 应携带足够上下文,使 Bob 侧能够做事件防抖、索引、归档和诊断展示。
建议防抖 key:
event_kind + device + operation + reason_class + stack_id + cgroup_idPID 默认不进入主防抖 key。直接按 PID 拆分会在慢盘或队列拥塞时放大事件基数,削弱防抖效果。对于 slow_io、io_error、timeout、retry 或 queue_congestion 等影响多个进程的异常,Event 应保留受害进程样本:
- representative pid/comm/cgroup。
- affected process count。
- first seen / last seen 窗口语义。
- 被截断的受害进程数量。
task scoped 事件可以保留更完整的 pid、comm、target device 或 operation,因为任务已有明确过滤条件和事件上限。当前 StorageEvent 不包含 path 字段;路径归因属于 future/unavailable 扩展。
当前已接受 Event 字段应保留:
SLOW_IOIO_ERRORTIMEOUTRETRYQUEUE_CONGESTION- latency、count 和 reason/error code。
- device、operation、layer、phase、mount namespace/cgroup。
- actor pid/comm,表达触发 I/O 的主体。
- 受影响进程样本,用于表达横向影响面。
- affected process count。
- stack id 或可还原的 stack/string 字典语义。
truncated_count,表达 Probe 侧采样或截断。- task id 或 task scope,区分常驻事件和任务结果。
当前已接受 reason class 对应语义:
NORMALDEVICE_LATENCYQUEUE_WAITIO_ERRORTIMEOUTRETRYWRITEBACK_PRESSUREUNKNOWN_LATENCY
Future/unavailable Event 候选不属于当前 Bob-facing 契约:
flush_latencyfsync_latencywriteback_pressure事件- VFS/fs/writeback 专用事件
- path/inode 归因事件
2.3 S5 归因字段
S5 为 StorageEvent 消息新增以下字段(proto3 向前兼容):
| 字段 | 编号 | 类型 | 来源 | 内核依赖 |
|---|---|---|---|---|
cgroup_id | 22 | uint64 | bpf_get_current_cgroup_id() | cgroupv2, Linux 4.18+ |
mount_ns_id | 23 | uint64 | /proc/<pid>/ns/mnt inode(用户态) | 无(/proc 文件系统) |
affected_process_count | 24 | uint64 | in_flight_count 扫描 | 无 |
StorageVictimSample 新增:
| 字段 | 编号 | 类型 | 说明 |
|---|---|---|---|
cgroup_id | 5 | uint64 | victim 进程的 cgroup identity |
已有字段现在被填充:
| 字段 | 编号 | S5 之前 | S5 之后 |
|---|---|---|---|
stack_id | 17 | 始终 0 | bpf_get_stackid() 填充,用户态栈 |
victims | 20 | 始终空 | QUEUE_CONGESTION 事件携带 representative victims |
detail | 21 | 始终空 | 保留供后续使用 |
消耗侧注意事项:
cgroup_id在 cgroupv1 或内核 < 4.18 的系统上为 0。不将 cgroup_id > 0 作为必需断言。stack_id在内核态 I/O completion 路径(async I/O)中常为 0(当前进程非原始调用者)。 Bob 侧可将 stack_id 作为可选的诊断增强字段,不可作为事件分类键。mount_ns_id在容器或 pid namespace 隔离环境中可能为 0(/proc 不可达), 此时应依赖 cgroup_id 作为主要容器归因维度。victims仅 QUEUE_CONGESTION 事件携带,最多 5 个 representative 样本。 常驻 SLOW_IO 和 IO_ERROR 事件不携带 victims(actor pid 已足够)。affected_process_count反映 in-flight 请求总量,非受害进程精确计数。
三、TaskChannel 控制面契约
TaskChannel 表达运行时诊断任务,不是配置热加载,也不是任意 eBPF 执行入口。
推荐链路:
Bob-side diagnostic request
-> Bob-side validation and scheduling
-> TaskRequest{storage_args}
-> Probe executor
-> collect samples / metrics
-> TaskResponse{trace_results / metric_results}
-> Bob-side consumer当前已接受 task type:
trace_slow_iomonitor_process_iomonitor_device_io
Future/unavailable 候选,不属于当前 Storage baseline:
trace_fsync_latencytrace_vfs_latencytrace_writeback_pressuretrace_io_errors
TaskChannel 只能接受预定义 task type,不能传递任意 hook、函数名、BPF 字节码、文件系统修改动作或 I/O 阻断动作。
四、配置系统关系
配置系统表达 policy、limits 和 defaults。TaskChannel 表达 runtime command。
Config = policy / limits / defaults / whitelist
TaskChannel = runtime command / task lifecycle
Bob-facing consumer = diagnostic surfaceStorage 配置负责定义:
modules.storage:是否启用 Storage 基础能力。probe.modules.storage.mode:采集路线。probe.modules.storage.devices:可选设备白名单;为空时由 Probe 自动选择可观测 block device。probe.modules.storage.event_sample_rate:默认事件采样率。probe.modules.storage.max_events_per_sec:Probe 侧事件上限。probe.modules.storage.enable_stack:是否采集 stack id。probe.modules.storage.enable_attribution:是否启用增强归因。probe.modules.storage.allowed_task_types:Probe 允许执行哪些 Storage 任务。
重要边界:
- TaskChannel 请求不修改 YAML。
- TaskChannel 不改变持久配置。
- endpoint、TLS、日志和公共 transport 配置不属于 Storage 模块。
- Probe 必须在任务完成、失败、取消或断连时清理运行时状态。
五、TaskRequest 参数
TraceStorageArgs 应保持严格受控参数。
当前已接受参数:
task_type:白名单任务类型。duration_sec:任务窗口。pid:进程过滤。device:设备过滤。mount_path:挂载路径过滤,best-effort。cgroup:cgroup 字符串过滤,best-effort。operation:read、write、flush、discard。sample_rate:采样率。max_events:最大返回事件数。
约束:
task_type必须在 Probe 白名单内。duration_sec必须有默认值和硬上限。max_events必须有硬上限,超出后通过truncated_count表达。- 没有过滤条件的高成本任务必须降级为聚合模式或拒绝。
mount_path/cgroup过滤必须声明 best-effort 语义;rename、unlink、bind mount、overlayfs 和 namespace 差异可能导致不命中或命中旧路径。- Probe 侧必须重复校验参数,不能只依赖 Bob 侧校验。
Future/unavailable 参数不属于当前 TraceStorageArgs 契约,Bob 侧不得下发:
tgidmajor/minormount_ns_id/cgroup_idlayer/phasepath/inodelatency_threshold_ns
六、Probe 运行时状态变化
允许 TaskChannel 改变的临时状态:
- 当前 task registry。
- 临时 link handles。
- BPF map 中的过滤条件。
- task scoped sample rate、duration、max events。
- task scoped result buffer。
- task scoped dictionary entries。
- task cancellation 和 cleanup state。
禁止改变:
- YAML 配置文件。
- 全局模块启用状态。
- TLS、endpoint、日志等公共配置。
- 未白名单 BPF 程序或 hook。
- 会修改、阻断、延迟或重定向 I/O 的动作。
Storage Task 默认只读观测,不参与 I/O 调度决策。
七、任务生命周期
Probe executor 的基本状态机:
idle
-> receive TaskRequest
-> validate
-> accepted/running
-> attach or enable filter
-> collect samples
-> transform results
-> completed/failed/cancelled
-> cleanup失败处理:
- 参数不合法:拒绝任务或返回
STATUS_FAILED。 - task type 不支持:Probe 返回
STATUS_FAILED。 - hook attach 失败:只影响该 task,并返回错误消息。
- 任务超时:Probe 清理状态并返回可预测结果。
- Probe 断连:Bob-facing consumer 获得可预测失败语义;重连和 ticket 策略由 Bob 侧决定。
- 结果超出上限:返回截断后的结果,并设置
truncated_count。
八、TaskResponse 与结果边界
Storage Task 结果复用现有 TaskResponse:
message TaskResponse {
string task_id = 1;
deepsight.common.Status status = 2;
string error_msg = 3;
repeated EventWrapper trace_results = 4;
repeated MetricWrapper metric_results = 5;
}结果语义:
trace_results中每条结果使用EventWrapper{storage},表达异常样本、慢 I/O 证据、错误和解释上下文。metric_results中每条结果使用MetricWrapper{storage},表达任务窗口内汇总指标、top device/operation 和 monitoring delta。level表达严重级别。truncated_count表达对应 event wrapper 在任务窗口内的截断数量;metric 汇总应通过 metric payload 字段表达窗口计数。StorageEvent表达 device、operation、layer、phase、latency、actor pid/comm、reason/error、stack id 和 task scope。StorageMetric表达 task scoped 或常驻的低基数聚合状态,例如 read/write bytes、ops、latency bucket、error count 和 window。- 常驻事件用受害进程样本表达影响面,不把 PID 作为默认防抖主键。
- 当前
StorageEvent不包含 path 字段;路径归因属于 future/unavailable 扩展,不得作为当前结果前提。 - Task scoped 结果应包含窗口汇总 metric,表达 task window 内 read/write bytes、read/write ops、top device、top operation、slow sample count、error count、truncated count 和 reason_class;异常样本再放入 event。
- 字典化 stack/string 必须能由 Bob-facing consumer 还原。
本文不规定 Bob 侧 JSON 形态、ticket 存储、MCP Tool 响应或查询索引。
九、安全边界
Storage 控制面的默认安全策略:
- 只允许白名单 task type。
- 不允许任意 hook、函数名或 BPF bytecode。
- 不允许修改、阻断或重定向 I/O。
- 必须有 duration、sample_rate、max_events 和并发上限。
- 必须支持取消、超时和 cleanup。
- 高基数字段只在 Event 或 Task 作用域内使用。
- 文件路径观测默认关闭或 task scoped,避免把 Storage 常驻路径变成文件级审计系统。
gRPC 接入层和 Probe 都要做校验。接入层校验保护用户体验和全局策略,Probe 校验保护宿主机边界。
十、验证要求
后续实现 gRPC/control 能力时,需要验证:
- Storage 参数到
TaskRequest.storage_args的映射。 - 配置白名单、duration、sample_rate、max_events 和并发上限。
- Probe 返回 completed、failed、cancelled、timeout 的行为。
TaskResponse.trace_results能复用EventWrapper{storage}。TaskResponse.metric_results能复用MetricWrapper{storage}。- 常驻 Event 和 task scoped Event 能被区分。
TaskChannel未实现或未启用时明确失败,不让调用方误以为任务已下发。