类型兼容性矩阵测试模式(Schema Evolution)

是什么

类型兼容性矩阵测试是一种数据驱动测试模式,通过穷举类型系统(Type System)中所有可能的新旧类型组合,验证 Schema 演进过程中的向前兼容向后兼容规则。

核心概念

向前兼容(Forward Compatibility) vs 向后兼容(Backward Compatibility)

旧 Schema  →  新 Schema
     ↓            ↓
  写入数据     读取数据
概念定义示例
向前兼容旧 Schema 写的数据,新 Schema 能读BigInt→Int: true(旧 → 新读)
向后兼容新 Schema 写的数据,旧 Schema 能读Int→BigInt: false(新 → 旧读)

记忆技巧

  • 向前 = 旧数据被新代码读(旧→新,向前演进能读)
  • 向后 = 新数据被旧代码读(新→旧,向后退能读)

类型兼容性规则矩阵

@ParameterizedTest(name = "#14/15 类型 {0}→{1} 兼容={2}")
@CsvSource({
    "BigInt, Int, true",     // 向前兼容:宽度扩大(Int→BigInt)
    "Int, BigInt, false",    // 不兼容:宽度缩小(BigInt→Int)
    "Str, Int, false",       // 跨类型变更不兼容
    "Str, Text, false",      // 跨类型变更不兼容
    "Int, Decimal, false",   // 跨类型变更不兼容
    "Str, Str, true",        // 同类型自然兼容
})
void testTypeCompatibility(String from, String to, boolean expected) { ... }

测试用例解读

测试用例方向兼容性原因
BigInt → Int = true旧→新读Int 存入,BigInt 读取(宽类型能容纳窄类型 → 向上转型
Int → BigInt = false新→旧读BigInt 存入,Int 读取(窄类型无法容纳宽类型 → 精度丢失风险
Str → Int = false任何方向类型语义完全不同,无法转换
Str → Text = false任何方向同字符串但不同语义类型,不兼容
Int → Decimal = false任何方向整数→小数,类型系统视为不同
Str → Str = true同类型类型不变,天然兼容

典型应用场景

1. Apache Avro Schema Evolution

Avro 是 Hadoop 生态最常用的序列化框架,其 Schema 演进规则就是典型的兼容性矩阵:

  • INT → LONG:兼容(宽度扩大)
  • LONG → INT:不兼容(宽度缩小)
  • STRING → BYTES:兼容
  • 添加/删除字段带默认值:兼容
// Avro Schema 兼容性测试
@ParameterizedTest
@CsvSource({
    "INT, LONG, true",     // 向前兼容
    "LONG, INT, false",    // 不兼容
    "STRING, BYTES, true",
})

2. Protocol Buffers(Protobuf)

Protobuf 的字段类型演进规则:

  • int32 → int64:兼容(数值范围扩大)
  • int64 → int32:兼容但可能截断(运行时无错,数据可能损坏)
  • string → bytes:兼容(Wire 格式相同)

3. SQL 数据库 Schema 迁移

-- ❌ 危险迁移:BIGINT → INT(可能导致数据溢出)
ALTER TABLE users ALTER COLUMN id TYPE INT;  -- 若存在 > 2^31-1 的值则失败
 
-- ✅ 安全迁移:INT → BIGINT
ALTER TABLE users ALTER COLUMN id TYPE BIGINT;

4. 数据湖格式(Iceberg / Delta Lake / Hive)

列类型变更规则:

  • INT → BIGINT:✅ 安全
  • FLOAT → DOUBLE:✅ 安全
  • DECIMAL(10,2) → DECIMAL(12,2):✅ 精度扩大
  • STRING → INT:❌ 不允许

5. API 版本兼容性测试

@ParameterizedTest
@CsvSource({
    "2023-01, 2024-01, true",   // 旧版本请求 → 新版本 API 处理
    "2024-01, 2023-01, false",  // 新版本请求 → 旧版本 API 处理
})
void testApiVersionCompatibility(String reqVersion, String apiVersion, boolean compatible) { ... }

常见设计原则

类型扩展原则(Type Widening Rule)

byte → short → int → long → float → double

数值从窄到宽始终兼容,反方向不兼容。

兼容性决策树

类型是否相同?
  ├── 是 → ✅ 兼容
  └── 否 → 是否属于同一类型族(数值族/字符串族/时间族)?
       ├── 是 → 检查宽度:
       │    ├── 从窄到宽 → ✅ 向前兼容
       │    └── 从宽到窄 → ❌ 不兼容
       └── 否 → ❌ 跨族不兼容

相关笔记