Unified Cursor Format
Problem
Two separate cursor implementations with duplicated base62 encoding:
- LogStore:
v1:{sortBy}:{sortOrder}:{position} (not yet on main)
- Tenants:
tntv01:{position}
Design Decision
Cursor format: {resource}v{version}:{data} → base62 encoded
| Resource |
Format |
Current Data |
| Events |
evtv01:{data} |
Position string: 1737123456789_evt_abc |
| Deliveries |
dlvv01:{data} |
Position string: 1737123456789_dlv_xyz |
| Tenants |
tntv01:{data} |
Timestamp: 1737123456789 |
Key insight: Cursors only need opaque data. Sort direction comes from the request. The v1:{sortBy}:{sortOrder}:{position} format in the logstore branch is unnecessary.
Evolution
Currently, data is just a position string. If a driver needs richer data, bump the version:
// v01: data = position string
"evtv01:1737123456789_evt_abc"
// v02: data = structured (hypothetical)
"evtv02:1737123456789_evt_abc|extra"
For structured data, avoid JSON (parsing overhead). Use a custom struct with MarshalBinary/UnmarshalBinary for efficient encoding.
Driver handling multiple versions:
data, err := cursor.Decode(req.Next, "evt", 2)
if errors.Is(err, cursor.ErrVersionMismatch) {
// Fallback to v1
data, err = cursor.Decode(req.Next, "evt", 1)
if err != nil {
return cursor.ErrInvalidCursor
}
position = data // v1: data is just position
} else {
// v2: custom struct with additional fields
var c CursorDataV2
if err := c.UnmarshalBinary([]byte(data)); err != nil {
return cursor.ErrInvalidCursor
}
position = c.Position
extra = c.Extra
}
Each driver owns its data format. The cursor package is format-agnostic - just handles {resource}v{version}:{data} encoding.
Shared Package
New: internal/cursor/cursor.go
package cursor
var (
ErrInvalidCursor = errors.New("invalid cursor")
ErrVersionMismatch = errors.New("cursor version mismatch")
)
func Encode(resource string, version int, data string) string
func Decode(encoded, resource string, version int) (data string, err error)
func Base62Encode(s string) string
func Base62Decode(s string) (string, error)
Backward Compatibility
Not strictly necessary (beta, few users, cursors are ephemeral), but cheap to include.
Legacy logstore cursors (raw base62'd data, no prefix):
data, err := cursor.Decode(req.Next, "evt", 1)
if errors.Is(err, cursor.ErrVersionMismatch) {
data, err = cursor.Base62Decode(req.Next) // legacy: no prefix
}
Unified Cursor Format
Problem
Two separate cursor implementations with duplicated base62 encoding:
v1:{sortBy}:{sortOrder}:{position}(not yet on main)tntv01:{position}Design Decision
Cursor format:
{resource}v{version}:{data}→ base62 encodedevtv01:{data}1737123456789_evt_abcdlvv01:{data}1737123456789_dlv_xyztntv01:{data}1737123456789Key insight: Cursors only need opaque data. Sort direction comes from the request. The
v1:{sortBy}:{sortOrder}:{position}format in the logstore branch is unnecessary.Evolution
Currently, data is just a position string. If a driver needs richer data, bump the version:
For structured data, avoid JSON (parsing overhead). Use a custom struct with
MarshalBinary/UnmarshalBinaryfor efficient encoding.Driver handling multiple versions:
Each driver owns its data format. The cursor package is format-agnostic - just handles
{resource}v{version}:{data}encoding.Shared Package
New:
internal/cursor/cursor.goBackward Compatibility
Not strictly necessary (beta, few users, cursors are ephemeral), but cheap to include.
Legacy logstore cursors (raw base62'd data, no prefix):