# 数据源连接池与凭据安全设计 日期:2026-07-18 状态:已批准,进入实施计划 ## 1. 目标 为 DataOps 平台接入的外部业务数据源建立统一、安全、可观测的连接池框架。 第一阶段走通 PostgreSQL 和 MySQL,部署边界为单台服务器、单个后端实例, 并为后续多副本部署保留配置与接口扩展能力。 本设计明确区分两类数据库连接: - 平台控制库:DataOps 自身使用的 PostgreSQL,保存用户、订单、流程版本、 工作台、知识库等平台数据。继续由 Flask-SQLAlchemy 管理,不建设额外的 数据源连接池管理层。 - 业务数据源:通过 `DataSource` 定义接入的外部数据库。元数据采集、数据预览、 质量检查和 DataFlow 执行通过本设计的连接池管理器访问。 ## 2. 范围 ### 2.1 第一阶段范围 - PostgreSQL 数据源,驱动为 `postgresql+psycopg2`。 - MySQL 数据源,驱动为 `mysql+pymysql`。 - 按数据源延迟创建和复用连接池。 - 空闲池回收、配置变更失效、删除时排空。 - 数据源凭据加密保存和历史明文凭据迁移。 - 故障隔离、熔断、指标和管理员诊断。 - 全本地隔离 Docker 验收环境。 ### 2.2 暂不进入第一阶段 - 多后端副本间的全局连接预算和共享状态。 - PgBouncer 或其他外部连接代理。 - Oracle、SQL Server、DB2、Sybase 等驱动的正式验收。 - 跨实例分布式连接池、分布式锁或集中式连接代理。 - 任意 SQLAlchemy driver name 和任意 `connect_args` 透传。 ## 3. 总体架构 ```text 平台控制面 Flask-SQLAlchemy ──> 平台 PostgreSQL 仅使用框架连接管理,不进入 DataSource Pool Registry 业务数据面 DataSource API / 元数据采集 / 数据预览 / DataFlow │ ▼ DataSourceConnectionManager ├── DataSourceDefinitionRepository ├── CredentialProvider ├── PostgreSQLAdapter ├── MySQLAdapter ├── PoolRegistry ├── CircuitBreaker └── PoolMetrics │ ┌─────────┴─────────┐ ▼ ▼ PostgreSQL 数据源池 MySQL 数据源池 ``` 业务模块只能通过 `DataSourceConnectionManager` 获取外部数据库连接,不得直接 读取数据源密码并调用 `create_engine()`。连接测试是唯一允许创建临时 Engine 的 入口,且必须使用 `NullPool` 并在完成后执行 `dispose()`。 ## 4. 数据源身份与连接池注册 每个数据源必须具有稳定 UUID,并以 `data_source_uid` 作为业务身份。池注册键由 以下内容组成: ```text data_source_uid credential_version connection_config_fingerprint ``` 连接配置指纹由数据库类型、主机、端口、数据库名、Schema 和白名单安全选项生成, 不包含用户名、密码、连接 URL 或密文。 连接池按需创建: - 第一次采集、预览或 DataFlow 使用时创建。 - 同一 Gunicorn Worker 内,同一注册键复用一个 SQLAlchemy Engine。 - 数据源定义、凭据版本或安全配置变化后,旧注册键失效。 - 数据源删除后,禁止新连接借出并排空旧池。 - 空闲 15 分钟的池执行回收。 - 每个 Worker 默认最多缓存 20 个空闲池。 - 正在执行任务的池进入 `draining`,连接归还后再释放。 Gunicorn Worker 是独立进程。即使只有一个后端实例,各 Worker 仍拥有独立的 Pool Registry,第一阶段不尝试跨进程共享数据库连接。 ## 5. 默认容量 单个数据源、单个 Worker 的默认参数: ```text pool_size=2 max_overflow=3 pool_timeout=10 pool_recycle=1800 pool_pre_ping=true pool_use_lifo=true ``` 采用 4 个 Worker 时,一个持续繁忙的数据源理论最大连接数为 20,常驻连接数 最大为 8。数据源可以配置受控的池大小覆盖值,但服务端必须限制上下限,不能直接 接受用户提交的任意连接数量。 后续多副本阶段使用以下公式重新计算单数据源连接预算: ```text 副本数 × Worker 数 × (pool_size + max_overflow) ``` ## 6. 驱动适配 Adapter 负责: - 校验数据源类型和连接字段。 - 构建 SQLAlchemy URL。 - 生成驱动专属白名单参数。 - 创建 Engine。 - 执行 `SELECT 1` 探测。 - 将驱动异常转换为稳定的平台错误。 - 提供数据库标识符引用和只读事务能力。 PostgreSQL 默认启用 TCP keepalive: ```text connect_timeout=5 keepalives=1 keepalives_idle=30 keepalives_interval=10 keepalives_count=3 ``` MySQL 默认设置: ```text connect_timeout=5 read_timeout=30 write_timeout=30 charset=utf8mb4 ``` SSL 和证书参数只能从明确白名单读取,不允许任意驱动参数透传。前端传入的 `type` 必须映射到平台内部 Adapter,不能直接成为 SQLAlchemy driver name。 ## 7. 凭据模型 平台 PostgreSQL 新增 `datasource_credentials`: ```text id data_source_uid credential_version encrypted_payload nonce key_version status created_at retired_at ``` `encrypted_payload` 包含用户名、密码和必要的认证选项。Neo4j `DataSource` 节点 只保存: ```text uid type host port database schema credential_ref credential_version 业务描述和治理属性 ``` 凭据使用 AES-256-GCM 加密: - 主密钥由 `DATASOURCE_CREDENTIAL_MASTER_KEY` 环境变量注入。 - 每条凭据使用独立随机 nonce。 - `data_source_uid + credential_version` 作为附加认证数据。 - `key_version` 为后续主密钥轮换保留。 - 主密钥不写入代码、数据库、镜像或日志。 - 缺少主密钥或解密失败时,数据源功能失败关闭,但平台控制面仍可启动。 API、日志、异常、Neo4j 查询结果和诊断接口均不得返回用户名、密码、密文、 带凭据的 URL 或完整连接串。数据源列表只返回 `credential_configured` 等布尔状态。 ## 8. 数据源变更流程 ### 8.1 创建 1. 校验类型,只允许 PostgreSQL 或 MySQL。 2. 校验主机、端口、数据库名和安全选项。 3. 使用 `NullPool` 临时 Engine 测试连接,随后关闭。 4. 将凭据加密写入平台 PostgreSQL。 5. 在 Neo4j 保存不含密码的数据源定义及凭据引用。 6. 成功后允许 Pool Registry 按 UID 延迟创建连接池。 ### 8.2 修改 1. 创建不可变的新凭据版本。 2. 更新 Neo4j 数据源定义和 `credential_version`。 3. 将旧连接池标记为 `draining`。 4. 后续请求使用新注册键创建连接池。 5. 旧凭据标记为 `retired`。 修改失败时,数据源继续引用原有有效版本。PostgreSQL 与 Neo4j 之间使用现有 Outbox 和补偿巡检实现最终一致性。 ### 8.3 删除 1. 禁止新连接借出。 2. 等待已借出连接在限定时间内归还。 3. 到期后强制释放 Engine。 4. 删除或归档 Neo4j 数据源定义。 5. 将凭据标记为 `revoked`。 6. 记录不含敏感数据的审计事件。 ## 9. 连接获取接口 业务模块使用统一的上下文接口: ```python with data_source_manager.connect( data_source_uid, purpose="metadata_preview", ) as connection: ... ``` 管理器依次完成定义读取、凭据获取、Adapter 选择、池注册、连接借出和归还。 业务模块不持有 Engine,不负责连接池配置,也不能访问凭据明文。 `purpose` 使用平台枚举,第一阶段至少包括: - `connection_test` - `metadata_collection` - `metadata_preview` - `quality_check` - `dataflow_read` - `dataflow_write` 除 `dataflow_write` 外默认使用只读事务。写入任务必须显式提交,异常时回滚。 ## 10. 故障隔离与熔断 每个数据源独立维护状态: ```text healthy degraded open draining closed ``` - 单个数据源连接耗尽或宕机,不影响平台控制库和其他数据源。 - 连续 3 次建连失败后熔断 30 秒。 - 熔断结束后只允许一个半开放探测请求。 - 探测成功恢复 `healthy`,失败继续熔断。 - 借出等待超过 10 秒返回 `DATASOURCE_POOL_TIMEOUT`。 - 事务中断必须回滚,不自动重放数据源写操作。 - 失效连接不再返回池中。 主要稳定错误码: - `DATASOURCE_NOT_FOUND` - `DATASOURCE_TYPE_UNSUPPORTED` - `DATASOURCE_CREDENTIAL_UNAVAILABLE` - `DATASOURCE_CONNECTION_FAILED` - `DATASOURCE_POOL_TIMEOUT` - `DATASOURCE_CIRCUIT_OPEN` - `DATASOURCE_QUERY_TIMEOUT` - `DATASOURCE_READ_ONLY_VIOLATION` 错误响应不包含驱动原始连接 URL 或凭据。 ## 11. 监控与管理 管理员可以查看单个数据源的池摘要: ```text data_source_uid database_type pool_state pool_size checked_out checked_in overflow checkout_wait_ms last_used_at consecutive_failures circuit_open_until credential_version ``` 平台健康检查仅返回聚合数据: ```text active_pool_count degraded_pool_count open_circuit_count checked_out_total pool_timeout_total ``` 指标至少覆盖: - 连接创建成功和失败次数。 - Checkout 等待时间。 - 当前占用、空闲和溢出连接数。 - 池超时、连接失效和重建次数。 - 查询耗时。 - 空闲池回收次数。 - 凭据版本导致的池重建次数。 所有日志使用数据源 UID 和错误分类;异常文本在写日志前统一脱敏。 ## 12. 历史凭据迁移 迁移必须分两步: 1. 只读报告:统计含明文凭据、缺少 UID 或配置不完整的数据源,只输出 UID 和问题 分类。 2. 显式迁移:补齐 UID、加密凭据、写入凭据引用、验证可解密后删除 Neo4j 明文字段。 生产执行前必须单独备份 Neo4j 和平台 PostgreSQL。迁移过程不得在日志、报告或 异常中输出明文密码。迁移失败的数据源保持原状态并进入人工核查清单。 现有数据源保存、验证和连接测试接口必须先完成请求日志脱敏。数据源列表接口必须 在凭据迁移前就停止返回密码。 ## 13. 本地隔离验收 Docker 测试栈新增两个业务数据源服务: ```text platform-postgres 平台控制库 source-postgres PostgreSQL 验收数据源 source-mysql MySQL 验收数据源 ``` 验收标准: 1. PostgreSQL、MySQL 均可保存、测试、建池和执行查询。 2. 同一数据源并发请求复用连接且不超过池上限。 3. 50–100 个并发请求下无连接泄漏。 4. 配置或密码修改后旧池失效,新池使用新版本。 5. 空闲池到期释放,再次访问可重建。 6. MySQL 停机不影响 PostgreSQL 数据源和平台功能。 7. 数据源恢复后可通过半开放探测恢复。 8. 删除数据源后连接池和凭据不可继续使用。 9. API、日志、Neo4j 和异常中不存在明文密码。 10. 后端重启后连接池按需重建。 测试包括: - AES-GCM 加密、篡改检测和密钥版本测试。 - PostgreSQL、MySQL Adapter 合约测试。 - Pool Registry 并发、回收和失效测试。 - 熔断状态机测试。 - 历史明文凭据迁移测试。 - Docker 数据源宕机和恢复测试。 - 前后端数据源管理接口验收。 ## 14. 后续扩展 进入多副本阶段后再评估: - 按副本数重新计算连接预算。 - PgBouncer。 - 跨实例池状态聚合。 - 数据源任务按 UID 路由。 - Oracle、SQL Server 等额外 Adapter。 第一阶段的 Adapter、CredentialProvider、Pool Registry 和指标接口保持稳定, 多副本阶段不改变业务模块的连接获取接口。