Sbe Serialization
SBE es el formato binario ultra-low-latency usado por Camunda para records en el log y comunicación entre brokers. Da performance extremo (10-50 ns por encode/decode) gracias a 6 design principles: copy-free, native types, allocation-free, streaming access, word-aligned, backward compat. Para el MVP: NO usar — JSON/Protobuf son suficientes. Las principles son aplicables a cualquier sistema high-performance.
Qué es SBE¶
SBE = Simple Binary Encoding. Especificación FIX (Financial Information eXchange) para encoding de mensajes en binario. Implementación reference open-source por Real Logic Limited.
- Layer: OSI layer 6 (presentation)
- Performance: encode/decode en decenas de nanosegundos
- Use case original: financial trading (latency-sensitive)
- Adoptado por: Camunda (Zeebe), Aeron, sistemas de trading
Por qué Camunda lo usa¶
Camunda lo usa para: - Records persistidos en el log: cada event/command serializado vía SBE - Inter-partition communication: brokers se hablan vía SBE - NO se usa para: API cliente (Protobuf/gRPC) o variables (MessagePack)
Razones: - Throughput máximo en hot paths del engine - Latencia predecible (sin GC variance) - Versioning del log via SBE schema versioning
Las 6 Design Principles¶
1. Copy-Free¶
"Not employ any intermediate buffers for the encoding or decoding of messages."
Encode/decode directamente al buffer subyacente. No staging. No copies.
// SBE direct write:
buffer.putLong(offset, value); // single CPU instruction
// vs JSON (multiple copies):
String json = mapper.writeValueAsString(obj); // creates String
byte[] bytes = json.getBytes(); // copies to byte[]
output.write(bytes); // copies to output buffer
2. Native Type Mapping¶
Tipos del schema mapean directamente a tipos CPU-native:
| SBE type | CPU type | Instructions |
|---|---|---|
| int64 | 64-bit integer | 1 MOV |
| float64 | 64-bit double | 1 MOV |
| char | 8-bit byte | 1 MOV |
| string | byte slice | 1 MOV (zero-copy) |
Field access cost ≈ struct field access en C.
3. Allocation-Free (Flyweight)¶
Decoder es un window sobre el buffer, no una copy:
ProcessInstanceDecoder decoder = new ProcessInstanceDecoder();
decoder.wrap(buffer, offset); // NO allocation
long key = decoder.processInstanceKey();
String bpmnProcessId = decoder.bpmnProcessId(); // points into buffer
Beneficios: - Zero GC pressure: no objetos creados - No CPU cache churn: misma memoria reusada - Latencia consistente: sin stop-the-world pauses
4. Streaming Access¶
Forward-only addressing — secuencial, no random:
// Bueno: sequential access
for (int i = 0; i < count; i++) {
long value = buffer.getLong(offset + i * 8);
}
// Malo: random access
long a = buffer.getLong(1000);
long b = buffer.getLong(50);
long c = buffer.getLong(10000);
Exploits: - CPU prefetching automático - Cache lines completos - Branch prediction efectivo
5. Word Aligned Access¶
Fields alineados a word boundaries del CPU:
Offset 0: int64 (8 bytes aligned to 8) ✓
Offset 8: int32 (4 bytes aligned to 4) ✓
Offset 12: padding (4 bytes)
Offset 16: int64 (next 8-byte boundary) ✓
Misaligned access penaliza: - May require multiple reads - Cache line crossing - 2-10x slower que aligned
6. Backward Compatibility¶
Mechanism para evolution: - Nuevos sistemas agregan campos opcionales al final - Sistemas viejos ignoran campos unknown - Mixed-version deployments funcionan
Schema en XML¶
<sbe:messageSchema id="1" version="0" byteOrder="littleEndian">
<types>
<composite name="messageHeader">
<type name="blockLength" primitiveType="uint16"/>
<type name="templateId" primitiveType="uint16"/>
<type name="schemaId" primitiveType="uint16"/>
<type name="version" primitiveType="uint16"/>
</composite>
</types>
<sbe:message name="ProcessInstanceRecord" id="1">
<field name="processInstanceKey" id="1" type="int64"/>
<field name="bpmnProcessId" id="2" type="utf8String"/>
<field name="version" id="3" type="int32"/>
</sbe:message>
</sbe:messageSchema>
Compiler (sbe-tool) genera Java/C++/C# classes con encoder/decoder.
Performance comparativa¶
| Formato | Encode | Decode | GC pressure | Self-describing |
|---|---|---|---|---|
| SBE | 10-50 ns | 10-50 ns | Zero | NO |
| Protobuf | 100-500 ns | 100-500 ns | Moderate | NO |
| MessagePack | 50-200 ns | 50-200 ns | Moderate | YES |
| JSON | 1-10 μs | 1-10 μs | High | YES |
| Avro | 100-300 ns | 100-300 ns | Moderate | YES |
SBE es 10-100x más rápido que Protobuf, 100-1000x más rápido que JSON.
Trade-offs: - SBE: máxima performance, schema obligatorio, no human-readable - Protobuf: muy rápido, gRPC integration, tooling maduro - JSON: human-readable, ergonómico, lento - MessagePack: balance entre binary y readable
Para el MVP: NO usar SBE¶
Razones para skip¶
- MVP no necesita nanosegundos: single-digit ms es target aceptable (ver analysis/sizing-benchmarks)
- Postgres como backend: serialization no es el bottleneck (SQL roundtrip domina)
- Debuggability matters: tools standard funcionan con JSON
- Team productivity: JSON/Protobuf más productivo que SBE
Stack recomendado para el MVP¶
| Use case | Formato | Razón |
|---|---|---|
| Log internal | JSON en JSONB de Postgres | Queryable, debuggable |
| Cliente API | JSON sobre REST | Standard, ergonómico |
| gRPC opcional | Protobuf | Si streaming jobs |
| Variables del proceso | JSON | Compatible con FEEL |
Lecciones aplicables sin usar SBE¶
Las 6 design principles aplican a cualquier sistema high-performance, no solo SBE:
- Copy-free: minimizar buffers intermedios en hot paths
- Native types: usar primitivos del lenguaje (no Integer, Long boxing en Java)
- Allocation-free / pooling: lección de concepts/microbenchmark-methodology — reuso reduce allocations 25x
- Streaming access: iterar secuencialmente
- Word aligned: relevante para sistemas con perf goals
- Backward compat: schema versioning desde día 1 (incluso en JSON)
Cuándo reconsider SBE para el MVP¶
- Si el MVP necesita > 100K TPS
- Si latencia P99 < 1ms es requirement
- Si Postgres se reemplaza con storage custom embebido
- Si serialization aparece en profiler como top hot spot
Hasta entonces, JSON/Protobuf es la elección correcta.
Recursos¶
- SBE GitHub: https://github.com/real-logic/simple-binary-encoding
- Mechanical Sympathy blog (Martin Thompson, creador de SBE): https://mechanical-sympathy.blogspot.com
- Aeron messaging system: relacionado, mismo equipo