Record structure
Every record has three main components:/home/daytona/workspace/source/common/src/record/mod.rs:261-265:
Position
The position identifies where the record is in the stream:- seq_num: Unique sequence number (0-based, incrementing)
- timestamp: Unix timestamp in milliseconds
Headers
A list of name-value pairs (both are binary data):- Names must not be empty (except for command records)
- Both names and values are arbitrary bytes
- Headers contribute to metered size and storage
Body
Arbitrary binary data representing the record’s payload:Record types
S2 supports two types of records: From/home/daytona/workspace/source/common/src/record/mod.rs:79-84:
Envelope records
Envelope records are the standard record type for application data:- Contain zero or more headers with non-empty names
- Contain a body with arbitrary data
- Used for storing events, messages, logs, etc.
Command records
Command records are special control records that affect stream behavior:- Have exactly one header with an empty name
- The header value identifies the command type
- The body contains the command payload
/home/daytona/workspace/source/common/src/record/mod.rs:166-180:
Fence command
Sets a fencing token for conditional writes:- Must be ≤ 48 bytes in length
- Start as an empty string by default
- Can be any arbitrary bytes
- Enable exactly-once semantics
/home/daytona/workspace/source/common/src/record/fencing.rs:
Trim command
Marks a trim point in the stream:- Body must be exactly 8 bytes (u64 sequence number)
- Records before the trim point become unreadable
- The trim point itself can never be trimmed
- Used for implementing retention policies
/home/daytona/workspace/source/lite/src/backend/streamer.rs:210-232:
Data encoding formats
Records can be represented in two formats when transmitted over the API: From/home/daytona/workspace/source/api/src/data.rs:42-48:
Raw format (default)
- Header names, values, and body are represented as UTF-8 strings
- Efficient for text data
- Storage is in UTF-8 encoding
Base64 format
- Header names, values, and body are Base64-encoded
- Safe for binary data transmission
- Efficient storage of binary data (stored as original bytes)
S2-Format header:
Metered size
Each record has a metered size that determines:- Billing (for s2.dev)
- Batch limits (max 1 MiB per batch)
- Storage tracking
/home/daytona/workspace/source/common/src/record/mod.rs:114-125:
- 8 bytes overhead per record
- 2 bytes per header (delimiter overhead)
- Header deep size: Sum of all header name and value lengths
- Body length: Size of the body in bytes
Metered size is calculated on the raw bytes, not the Base64-encoded representation.
Record encoding
Records are stored in a compact binary format:Magic byte
The first byte encodes the record type and metered size length: From/home/daytona/workspace/source/common/src/record/mod.rs:86-90:
- Bits 0-2: Record type (1=Command, 2=Envelope)
- Bits 3-4: Metered size variable length (0=1 byte, 1=2 bytes, 2=3 bytes)
Wire format
Appending records
Records are appended in batches:Batch constraints
From/home/daytona/workspace/source/api/src/v1/stream/mod.rs:363-367:
- Minimum: 1 record per batch
- Maximum: 1000 records per batch
- Size limit: Total metered size ≤ 1 MiB per batch
Append options
timestamp (optional):- Client-provided timestamp in milliseconds
- Behavior depends on stream timestamping configuration
- Server enforces monotonicity
- Enforce that first record gets this sequence number
- Fails with
seq-num-mismatchif tail has moved - Enables optimistic concurrency control
- Enforce the current fencing token matches
- Fails with
fencing-token-mismatchif token differs - Enables exactly-once semantics
/home/daytona/workspace/source/api/src/v1/stream/mod.rs:360-372:
Reading records
Record validation
Records are validated on append: From/home/daytona/workspace/source/common/src/record/mod.rs:167-180:
- Headers with empty names are only allowed for command records
- Command records must have exactly one header
- Fencing tokens must be ≤ 48 bytes
- Trim points must be 8 bytes (u64)
- Total batch size must be ≤ 1 MiB metered bytes
Best practices
- Use headers for metadata: Store searchable metadata in headers
- Choose appropriate format: Use raw for text, base64 for binary
- Batch appends: Send multiple records per request for better throughput
- Include timestamps: Provide client timestamps when event time matters
- Leverage fencing: Use fencing tokens for exactly-once critical operations
- Monitor metered size: Large records impact performance and costs
Next steps
Durability
Learn about durability guarantees
Reading records
Explore record read API