2.7 KiB
Kata 04: The Zero-Allocation JSON Parser
Target Idioms: Performance Optimization, json.RawMessage, Streaming Parsers, Buffer Reuse
Difficulty: 🟡 Intermediate
🧠 The "Why**
Developers from dynamic languages often parse JSON by unmarshaling entire documents into map[string]interface{} or generic structs. In high-throughput Go services, this creates:
- Massive memory churn (GC pressure)
- Unnecessary allocations for unused fields
- Lost type safety
The Go way: Parse only what you need, reuse everything. This kata teaches you to treat JSON as a stream, not a document.
🎯 The Scenario
You're processing 10MB/s of IoT sensor data with JSON like:
{"sensor_id": "temp-1", "timestamp": 1234567890, "readings": [22.1, 22.3, 22.0], "metadata": {...}}
You only need sensor_id and the first reading value. Traditional unmarshal would allocate for all fields and the entire readings array.
🛠 The Challenge
Implement SensorParser that extracts specific fields without full unmarshaling.
1. Functional Requirements
- Parse
sensor_id(string) and firstreadingsvalue (float64) from JSON stream - Process
io.Readerinput (could be HTTP body, file, or network stream) - Handle malformed JSON gracefully (skip bad records, continue parsing)
- Benchmark under 100ns per object and 0 allocations per parse
2. The "Idiomatic" Constraints (Pass/Fail Criteria)
- NO
encoding/json.Unmarshal: Usejson.DecoderwithToken()streaming - Reuse Buffers: Use
sync.Poolforbytes.Bufferorjson.Decoder - Early Exit: Stop parsing once required fields are found
- Type Safety: Return concrete struct
SensorData{sensorID string, value float64}, notinterface{} - Memory Limit: Process arbitrarily large streams in constant memory (<1MB heap)
🧪 Self-Correction (Test Yourself)
-
The Allocation Test:
go test -bench=. -benchmem -count=5Pass:
allocs/op= 0 for parsing loop Fail: Any allocations in hot path -
The Stream Test:
- Pipe 1GB of JSON through your parser (mock with repeating data)
- Pass: Memory usage flatlines after warm-up
- Fail: Memory grows linearly with input size
-
The Corruption Test:
- Input:
{"sensor_id": "a"} {"bad json here(malformed second object) - Pass: Returns first object, logs/skips second, doesn't panic
- Fail: Parser crashes or stops processing entirely
- Input: