format

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: BSD-2-Clause Imports: 15 Imported by: 0

Documentation

Overview

Package format implements pure byte-level encoding and decoding of every on-disk artefact in Tarantool's journal envelope: meta header, fixheader, xrow records, tx blocks, the EOF marker, and per-body schemas (DML / RAFT / SYNCHRO / VYLOG). It is the foundation for the `reader`, `writer`, `dir`, `rotate`, and utility packages.

Design references in the Tarantool 3.x tree:

  • meta: src/box/xlog.c:115-122,155
  • fixheader: src/box/iproto_constants.h:51, src/box/xlog.c:1095-1105
  • magic bytes: src/box/xlog.c:73-75
  • tx encoding: src/box/xlog.c:1080-1120 (uncompressed), :1128-1200 (zstd)
  • xrow header: src/box/xrow.c:331-444
  • tx semantics:src/box/xrow.c:259-263, 402-410
  • CRC32C: src/box/xlog.c:1086, src/lib/digest/crc32_impl.c:196-205
  • vy_log keys: src/box/vy_log.c:71-89

No I/O. No global mutable state (sync.Pool instances are construction caches, not state).

Index

Examples

Constants

View Source
const (
	// MarkerSize is the byte length of every magic marker (RowMarker,
	// ZRowMarker, EOFMarker) — the fixed prefix a reader peeks to classify
	// what comes next.
	MarkerSize = 4

	// FixheaderSize is the exact on-disk size of every tx-block fixheader.
	// XLOG_FIXHEADER_SIZE in Tarantool. The fixheader is zero-padded as an
	// mp_str so the total always reaches this value.
	FixheaderSize = 19

	// CompressThreshold is the minimum payload size (XLOG_TX_COMPRESS_THRESHOLD)
	// above which a tx is zstd-compressed.
	CompressThreshold = 2 * 1024

	// AutocommitThreshold is the in-memory buffer size after which the writer
	// auto-flushes the current tx on the next row boundary
	// (XLOG_TX_AUTOCOMMIT_THRESHOLD, src/box/xlog.c:85).
	AutocommitThreshold = 128 * 1024

	// ZstdLevel is the compression level Tarantool uses
	// (ZSTD_compressBegin(ctx, 3), src/box/xlog.c:1135).
	ZstdLevel = 3

	// FormatVersion is the literal "0.13" string written in the meta header
	// for current files. The writer always emits this.
	FormatVersion = "0.13"

	// LegacyFormatVersion is the older "0.12" string the reader accepts when
	// AcceptV012 is set on MetaOptions.
	LegacyFormatVersion = "0.12"
)

Layout constants. See src/box/iproto_constants.h:51 and src/box/xlog.c:85,93.

View Source
const MaxDecompressedTxSize = 64 << 20 // 64 MiB

MaxDecompressedTxSize caps the number of bytes a single zstd-compressed tx block (ZRowMarker) is allowed to decompress to. It is the decompression-bomb guard: a malicious or corrupt file can carry a tiny ZRowMarker payload whose zstd frame *declares* an enormous content size; klauspost's decoder defaults to a 64 GiB ceiling, so without this cap a few on-disk bytes can drive a multi-gigabyte allocation and OOM the process before a single row is read.

The cap is deliberately generous relative to anything Tarantool writes: a tx block is auto-flushed at AutocommitThreshold (128 KiB), so a legitimate plain payload is at most that plus one trailing row, and a single row is bounded by the tuple-size limit (1 MiB by default, raisable). 64 MiB leaves ~500x headroom over the autocommit threshold and comfortably covers even large-tuple deployments, while bounding a hostile block to an allocation the process can survive. The decoder rejects an over-cap frame (returning zstd.ErrDecoderSizeExceeded) from its *declared* size, before allocating.

Variables

View Source
var (
	ErrIsLeaderSeenLen = errors.New("is_leader_seen bad length")
	ErrIsLeaderSeenTag = errors.New("is_leader_seen unexpected tag")
)

Sentinel errors for malformed RAFT bodies.

View Source
var (
	// RowMarker prefixes an uncompressed tx block.
	RowMarker = [4]byte{0xD5, 0xBA, 0x0B, 0xAB}
	// ZRowMarker prefixes a zstd-compressed tx block.
	ZRowMarker = [4]byte{0xD5, 0xBA, 0x0B, 0xBA}
	// EOFMarker terminates a complete xlog/snap/vylog file.
	EOFMarker = [4]byte{0xD5, 0x10, 0xAD, 0xED}
)

Magic byte sequences — the first 4 bytes of every tx block and the EOF marker. Stored big-endian on disk; we treat them as opaque [4]byte to avoid host-endianness ambiguity. See src/box/xlog.c:73-75.

View Source
var (
	ErrShortFixheader  = errors.New("format: fixheader: truncated")
	ErrUnknownMagic    = errors.New("format: fixheader: unknown magic")
	ErrFixheaderShape  = errors.New("format: fixheader: malformed shape")
	ErrPaddingTooLarge = errors.New("format: fixheader: padding exceeds fixheader size")
)

ErrShortFixheader / ErrUnknownMagic / ErrFixheaderShape — surfaced when DecodeFixheader rejects a malformed block.

View Source
var (
	ErrMetaTruncated  = errors.New("format: meta header truncated before blank-line terminator")
	ErrMetaBadFormat  = errors.New("format: malformed meta header")
	ErrMetaBadVersion = errors.New("format: unsupported format version")

	ErrNilMeta       = errors.New("nil meta")
	ErrEmptyFiletype = errors.New("empty Filetype")
	ErrNilMetaReader = errors.New("nil reader")
)

Sentinel errors returned by DecodeMeta. The reader package wraps them with file context.

View Source
var (
	ErrEmptyInput      = errors.New("empty input")
	ErrShortInput      = errors.New("short input")
	ErrTruncatedInput  = errors.New("truncated input")
	ErrUnexpectedTag   = errors.New("unexpected type tag")
	ErrUnsupportedTag  = errors.New("unsupported type tag")
	ErrShortHeader     = errors.New("header")
	ErrShortPayload    = errors.New("payload")
	ErrShortFixedWidth = errors.New("short fixed-width value")
	ErrShortFixstr     = errors.New("short fixstr")
)

Sentinel errors for the msgpack peek/skip helpers. Each call site wraps one of these with its specific prefix so the rendered message stays byte-for-byte identical to the historical hand-written strings while satisfying err113 (static, comparable error values).

View Source
var (
	ErrEOFMarkerBlock = errors.New("EOF marker, not a tx block")
	ErrMissingType    = errors.New("header missing IPROTO_REQUEST_TYPE")
)

Sentinel errors for tx-block decoding.

View Source
var (
	ErrVClockBraces = errors.New("vclock: missing surrounding braces")
	ErrVClockColon  = errors.New("vclock: missing ':' in entry")
	ErrVClockDupID  = errors.New("vclock: duplicate replica id")
)

Sentinel errors for ParseVClock.

View Source
var (
	ErrNilRow         = errors.New("nil row")
	ErrEmptyXRowInput = errors.New("empty input")
)

Sentinel errors for xrow encode/decode.

View Source
var ErrCorruptCRC = errors.New("format: tx block: crc32c mismatch")

ErrCorruptCRC is returned by DecodeTxBlock when the computed CRC32C does not match the value in the fixheader.

View Source
var ErrEmptyBody = errors.New("empty body")

ErrEmptyBody is returned by the body decoders when handed a zero-length body slice.

View Source
var ErrUnknownFiletype = errors.New("format: unknown filetype")

ErrUnknownFiletype is returned by Filetype.Ext for a Filetype that does not correspond to a Tarantool on-disk artefact.

Functions

func AppendFramedBlock

func AppendFramedBlock(dst, plain []byte, c Compression) ([]byte, error)

AppendFramedBlock frames an already-encoded plain payload into a complete on-disk tx block (fixheader + payload) appended to dst, returning the extended buffer. It compresses the payload (into a pooled scratch) when the Compression policy says to, computes the CRC32C over the on-disk bytes, and writes the fixheader. Callers holding their own reusable scratch should prefer CompressTxInto + EncodeFixheader directly.

func AppendTxBlock

func AppendTxBlock(dst []byte, rows []XRow, opts TxOptions) ([]byte, error)

AppendTxBlock appends a complete on-disk tx block (fixheader + payload) for rows to dst and returns the extended buffer. Compression and CRC follow the opts.Compression policy (zstd at its level iff the payload meets the policy's threshold and it is not Disabled). EncodeTxBlock is AppendTxBlock(nil, …); writers reuse a dst across txs to avoid the per-block allocation.

Note: the payload is encoded into a transient buffer internally, so this path still allocates that scratch. The fully reusable hot path lives in the writer package (AppendTxBlockPayload into owned scratch + framing), which is what high-throughput writers should use.

func AppendTxBlockPayload

func AppendTxBlockPayload(dst []byte, rows []XRow) ([]byte, error)

AppendTxBlockPayload appends the concatenated EncodeXRow output of rows — the *uncompressed* tx payload, with no fixheader — to dst and returns the extended buffer. Passing a reused dst[:0] makes the per-tx payload encode allocation-free in steady state (the buffer grows to the largest tx, then reuses its capacity). It is the building block BatchWriter accumulates into, and the first half of AppendTxBlock.

func CRC32C

func CRC32C(data []byte) uint32

CRC32C computes the Tarantool variant CRC-32C over data:

  • polynomial: Castagnoli (0x82F63B78 reflected)
  • initial value: 0
  • final XOR: none

Used for the `crc32c` field of every tx-block fixheader. The CRC is computed over the on-disk payload bytes — post-compression for ZRowMarker tx blocks, plain bytes for RowMarker tx blocks.

Tarantool's variant runs init=0 with NO final XOR, whereas the stdlib's crc32.Checksum / crc32.New apply the standard CRC frame (init=^0, final ^0), incompatible with `crc32_calc` (src/lib/digest/crc32_impl.c:196-205). We recover the unframed variant from Go's *hardware-accelerated* Castagnoli path with a length-independent identity: seeding crc32.Update with ^0 cancels its entry inversion, and the trailing ^0 cancels its exit inversion. This is ~20x faster than a byte-at-a-time loop on block-sized payloads (≈0.5 GB/s → ≈11 GB/s on arm64); crc32c_test.go pins it against a software reference over fixed and random inputs.

Example

ExampleCRC32C shows the Castagnoli CRC used to protect every tx block: it is a pure function of the input, so identical bytes hash equally and any change changes the checksum.

package main

import (
	"fmt"

	"github.com/tarantool/go-xlog/format"
)

func main() {
	a := format.CRC32C([]byte("tarantool"))
	b := format.CRC32C([]byte("tarantool"))
	c := format.CRC32C([]byte("Tarantool"))

	fmt.Println(a == b)
	fmt.Println(a == c)

}
Output:
true
false

func CompressTx

func CompressTx(payload []byte) ([]byte, error)

CompressTx zstd-encodes payload at ZstdLevel (3). The output is a self-contained zstd frame; Tarantool's libzstd decoder accepts it. Note that the output bytes are NOT byte-identical to libzstd's output — only the decompressed content is guaranteed to round-trip.

func CompressTxInto

func CompressTxInto(dst, payload []byte, level int) ([]byte, error)

CompressTxInto zstd-encodes payload at the given level, appending the frame to dst[:0] and returning the result (which may alias dst if it had enough capacity, or a fresh allocation otherwise). Pass a reused dst to amortise the output allocation across calls — the writer's hot path does exactly this.

func DecodeFixheaderInto

func DecodeFixheaderInto(b [FixheaderSize]byte, h *Fixheader) error

DecodeFixheaderInto parses the 19 bytes into h, fully overwriting it. It is the zero-allocation core of DecodeFixheader: a reader driving a tight block loop reuses a single Fixheader instead of allocating one per tx block.

func DecodeTxBlock

func DecodeTxBlock(b []byte) ([][]byte, []byte, int, error)

DecodeTxBlock parses a complete tx block at b[0:] and returns:

  • rowSlices: byte slices, one per xrow record, into the *plain* payload
  • plain: the decompressed (or as-is) payload bytes
  • n: total bytes consumed from b (fixheader + on-disk payload)
  • err: on CRC mismatch, malformed header, or truncated input

rowSlices aliases plain — callers that retain row slices past plain's lifetime must clone them. The plain slice may be a fresh allocation (for zrow blocks) or alias the input (for row blocks).

func DecodeXRowInto

func DecodeXRowInto(b []byte, dst *XRow) (int, error)

DecodeXRowInto parses one xrow record at b[0:] into dst, fully overwriting every field (including zeroing BodyRaw / StreamID / Flags so a reused dst carries no state from a prior row), and returns the number of bytes consumed. It is the zero-allocation core of DecodeXRow: a hot read loop can reuse a single *XRow — or an arena slot — across rows with no per-row heap allocation. As with DecodeXRow, dst.BodyRaw aliases b; clone it before retaining past b's lifetime.

func DecompressTx

func DecompressTx(payload, scratch []byte) ([]byte, error)

DecompressTx zstd-decodes payload. The caller may pass a non-nil scratch to amortise allocations across calls; the returned slice is the decompressed bytes (may alias scratch if it was large enough).

func EncodeFixheader

func EncodeFixheader(buf *[FixheaderSize]byte, h *Fixheader)

EncodeFixheader writes a complete 19-byte fixheader into buf.

The three uints are msgpack-encoded with the smallest width that fits (matching `mp_encode_uint` in mp.h). The remaining tail bytes are filled as an mp_str header with N-1 zero bytes of content, where N is the remaining byte count — exactly the recipe in src/box/xlog.c:1100-1105.

EncodeFixheader panics if the encoded uints overflow 15 bytes, which cannot happen for uint32 fields (max 5+5+5 = 15 bytes after the 4-byte magic, leaving exactly 0 padding bytes — and `mp_encode_strl(0)` for a zero-length fixstr is one byte 0xa0, but since `padding == 0` the writer skips the str entirely, matching Tarantool's `if (padding > 0)`).

func EncodeMeta

func EncodeMeta(w io.Writer, m *Meta) error

EncodeMeta writes the meta header. The format-version line is m.FormatVer when set (it must be "0.13" or the legacy "0.12", else ErrMetaBadVersion), and defaults to FormatVersion ("0.13") when m.FormatVer is empty — so the writer's freshly-built metas still emit 0.13, while a rewrite can preserve or retarget the source's version. The VClock line is always emitted (even "VClock: {}", required by Tarantool's signature check); an empty PrevVClock line is omitted. The header ends with a blank line.

Example

ExampleEncodeMeta writes a journal meta header to a byte buffer. The header is plain text terminated by a blank line; the format version defaults to "0.13" and an empty PrevVClock is omitted.

package main

import (
	"bytes"
	"os"

	"github.com/google/uuid"

	"github.com/tarantool/go-xlog/format"
)

func main() {
	meta := &format.Meta{
		Filetype:     format.FiletypeXLOG,
		Version:      "example",
		InstanceUUID: uuid.MustParse("11111111-2222-3333-4444-555555555555"),
		VClock:       format.VClock{1: 2},
	}

	var buf bytes.Buffer
	if err := format.EncodeMeta(&buf, meta); err != nil {
		panic(err)
	}

	_, _ = os.Stdout.Write(buf.Bytes())

}
Output:
XLOG
0.13
Version: example
Instance: 11111111-2222-3333-4444-555555555555
VClock: {1: 2}

func EncodeTxBlock

func EncodeTxBlock(rows []XRow, opts TxOptions) ([]byte, error)

EncodeTxBlock encodes rows into a complete on-disk tx block: fixheader + payload. The payload is each row's EncodeXRow output concatenated in order. If opts.Compression says to compress (the payload meets its threshold and it is not Disabled), the payload is zstd-compressed and the fixheader magic is ZRowMarker; otherwise RowMarker is used. The CRC32C in the fixheader is computed over the on-disk payload bytes (post-compression for zrow, plain for row) per src/box/xlog.c:1086,1165.

func EncodeXRow

func EncodeXRow(buf []byte, r *XRow) ([]byte, error)

EncodeXRow appends a complete xrow record to buf and returns the new buf. The record consists of:

  • a header map (per src/box/xrow.c:331-444)
  • the BodyRaw bytes appended verbatim

Encoding rules (src/box/xrow.c:402-410):

  • IPROTO_REQUEST_TYPE is always present.
  • IPROTO_REPLICA_ID / IPROTO_LSN / IPROTO_GROUP_ID / IPROTO_TIMESTAMP present iff nonzero.
  • Single-statement tx (TSN == LSN && IsCommit && TSN != 0): omit IPROTO_TSN AND clear IPROTO_FLAG_COMMIT in the encoded flags.
  • Multi-statement tx (TSN != LSN || !IsCommit): emit IPROTO_TSN as the diff (LSN - TSN). If IsCommit && TSN != LSN (last row of multi-stmt), OR IPROTO_FLAG_COMMIT back into the encoded flags.
  • IPROTO_FLAGS is emitted iff the resulting flags value is non-zero.

The caller is responsible for BodyRaw being a valid msgpack map (or nil for NOP rows).

Caveat: this function mirrors Tarantool's `if (header->tsn != 0)` guard (src/box/xrow.c:402). A row with LSN == TSN == 0 will skip the TSN/COMMIT emission block entirely. For a single-statement tx that is harmless — the decoder also infers `is_commit = true` when IPROTO_TSN is absent. For a multi-row tx where some rows legitimately have LSN == 0, the encoded output would be ambiguous (Tarantool would treat each as an independent single-stmt tx). The writer package's assignTxIDs avoids this by setting TSN = rows[0].LSN before encoding; callers using EncodeXRow directly must ensure rows in a multi-row tx have non-zero LSN.

Example

ExampleEncodeXRow round-trips a single-statement row through the byte codec. EncodeXRow appends the header map plus the verbatim body bytes; DecodeXRow parses them back and, because no IPROTO_TSN was emitted, infers the row is a committed single-statement transaction.

package main

import (
	"fmt"

	"github.com/tarantool/go-iproto"

	"github.com/tarantool/go-xlog/format"
)

func main() {
	// A minimal valid msgpack body: the one-element array [1].
	body := []byte{0x91, 0x01}

	row := &format.XRow{Type: iproto.IPROTO_INSERT, LSN: 1, BodyRaw: body}

	encoded, err := format.EncodeXRow(nil, row)
	if err != nil {
		panic(err)
	}

	decoded, n, err := format.DecodeXRow(encoded)
	if err != nil {
		panic(err)
	}

	fmt.Printf("type=%s lsn=%d commit=%t body=%d bytes (consumed %d of %d)\n",
		format.TypeName(decoded.Type), decoded.LSN, decoded.IsCommit(),
		len(decoded.BodyRaw), n, len(encoded))

}
Output:
type=INSERT lsn=1 commit=true body=2 bytes (consumed 7 of 7)

func TypeName

func TypeName(t iproto.Type) string

TypeName returns the short Tarantool name of a durable request type ("INSERT", "REPLACE", "RAFT", …). It is iproto.Type.String() with the "IPROTO_" prefix trimmed; unknown types render as iproto's Type(n) form.

Types

type Compression

type Compression struct {
	// Disabled turns compression off entirely: every block is written plain
	// (RowMarker) regardless of size.
	Disabled bool

	// Level is the zstd compression level. Zero means ZstdLevel (Tarantool's
	// 3). Passed to klauspost's EncoderLevelFromZstd.
	Level int

	// Threshold is the minimum plain-payload size in bytes at which a block is
	// compressed; smaller blocks are written plain. Zero means
	// CompressThreshold (2 KiB). Ignored when Disabled.
	Threshold int
}

Compression is a tx-block compression policy. Compression is applied at block granularity (a whole tx block is zstd-compressed or not — there is no per-row compression). The zero value means "default": zstd at ZstdLevel over any payload of at least CompressThreshold bytes — i.e. exactly Tarantool's behaviour. A writer holds one policy; EncodeTxBlock / AppendTxBlock take one.

func (Compression) Compresses

func (c Compression) Compresses(plainLen int) bool

Compresses reports whether a plain payload of plainLen bytes is compressed under this policy (not Disabled and at or above the effective threshold).

func (Compression) ResolvedLevel

func (c Compression) ResolvedLevel() int

ResolvedLevel returns the effective zstd level, substituting ZstdLevel for a zero Level.

type DMLBody

type DMLBody struct {
	SpaceID  uint32
	Tuple    []byte // IPROTO_TUPLE — INSERT/REPLACE/UPSERT.
	Key      []byte // IPROTO_KEY — UPDATE/DELETE.
	Ops      []byte // IPROTO_OPS — UPDATE/UPSERT.
	OldTuple []byte // IPROTO_OLD_TUPLE — replica-applied, audit.
	NewTuple []byte // IPROTO_NEW_TUPLE — replica-applied, audit.
	Extras   map[uint64][]byte
}

DMLBody is the typed body of an INSERT/REPLACE/UPDATE/DELETE/UPSERT row. Keys come from src/box/iproto_constants.h:98-138. We hold each variable- length field (Tuple/Key/Ops/Old/New) as raw msgpack bytes so callers who only need the SpaceID pay no decode cost.

Extras captures any unknown body key, preserving it across rewrite paths.

func DecodeDMLBody

func DecodeDMLBody(b []byte) (*DMLBody, error)

DecodeDMLBody parses a DML body map at b[0:]. It returns an error if b is not a msgpack map header.

type Filetype

type Filetype string

Filetype is the leading text token in the meta header (`XLOG`, `SNAP`, `VYLOG`, `RUN`, `INDEX`). It is the discriminator the reader uses to pick which body decoders are valid for the rows inside.

const (
	FiletypeXLOG  Filetype = "XLOG"
	FiletypeSNAP  Filetype = "SNAP"
	FiletypeVYLOG Filetype = "VYLOG"
	FiletypeRUN   Filetype = "RUN"
	FiletypeINDEX Filetype = "INDEX"
)

Filetype constants — match the literal strings Tarantool writes in src/box/xlog.c:155.

func (Filetype) Ext

func (ft Filetype) Ext() (string, error)

Ext returns the on-disk extension Tarantool uses for this Filetype. Mirrors xdir_open (src/box/xlog.c:267-274).

type Fixheader

type Fixheader struct {
	Magic  [4]byte
	Len    uint32
	CRC32P uint32
	CRC32C uint32
}

Fixheader is the 19-byte preface to every tx block. The wire shape (src/box/xlog.c:1095-1105) is:

bytes 0-3    : Magic (4-byte raw)
mp_uint      : Len    — payload byte count after the fixheader
mp_uint      : CRC32P — always 0 (legacy, not validated on read)
mp_uint      : CRC32C — checksum over the on-disk payload bytes
mp_str(N)    : padding — N=(remaining-bytes-1) zero bytes, sized so the
               whole fixheader is exactly FixheaderSize

func DecodeFixheader

func DecodeFixheader(b [FixheaderSize]byte) (*Fixheader, error)

DecodeFixheader parses the 19 bytes and returns the Fixheader. It validates that the magic is one of {RowMarker, ZRowMarker, EOFMarker} and that the three uints and the trailing padding consume exactly FixheaderSize bytes. The padding bytes themselves are not inspected — Tarantool's writer always zeroes them but readers tolerate any content since it is mp_str-typed data (strict on shape, but the content is not inspected).

type Meta

type Meta struct {
	Filetype     Filetype
	FormatVer    string
	Version      string
	InstanceUUID uuid.UUID
	VClock       VClock
	PrevVClock   VClock
	// Extras captures any unknown `Key: value` line we did not recognise,
	// preserving it across rewrite round-trips (forward-compat catch-all).
	Extras map[string]string
}

Meta is the parsed text header that every xlog/snap/vylog/run/index file begins with. The on-disk layout is:

<Filetype>\n               # XLOG | SNAP | VYLOG | RUN | INDEX
<FormatVer>\n              # "0.13" (or "0.12" with AcceptV012)
Version: <ver>\n           # Tarantool's PACKAGE_VERSION string
Instance: <uuid>\n         # 8-4-4-4-12 hex UUID (alias: "Server: ...")
VClock: {id: lsn, ...}\n   # ALWAYS present; empty renders "VClock: {}"
PrevVClock: {id: lsn, ...}\n# OMITTED when empty / for SNAP files
\n                         # blank line terminates the header

Source: src/box/xlog.c:115-122, 155 (writer) and the meta_parse path.

func DecodeMeta

func DecodeMeta(r *bufio.Reader, opts MetaOptions) (*Meta, error)

DecodeMeta reads a meta header from r up to and including the blank-line terminator. The reader is left positioned at the first byte after the terminator (i.e. the magic byte of tx block 1 or the EOF marker).

Strictness defaults: unrecognised filetypes / format versions return ErrMetaBadFormat / ErrMetaBadVersion.

func (*Meta) Clone

func (m *Meta) Clone() *Meta

Clone returns a deep copy of m. The returned *Meta shares no maps with the receiver: VClock, PrevVClock, and Extras are independently allocated. A nil receiver returns nil.

Used by tools.RewriteMeta to hand a mutation-safe copy to user transform functions without aliasing the parsed source meta.

type MetaOptions

type MetaOptions struct {
	// AcceptV012 accepts the legacy "0.12" format version in addition to
	// the current "0.13". Default false: strict on FormatVersion.
	AcceptV012 bool
}

MetaOptions controls strictness of DecodeMeta.

type RaftBody

type RaftBody struct {
	Term         uint64
	Vote         uint64
	State        uint32
	VClock       VClock
	LeaderID     uint32
	IsLeaderSeen bool
	Extras       map[uint64][]byte
}

RaftBody is the typed body of a IPROTO_RAFT row. Keys come from src/box/iproto_constants.h:465-471.

func DecodeRaftBody

func DecodeRaftBody(b []byte) (*RaftBody, error)

DecodeRaftBody parses a RAFT body map.

type SynchroBody

type SynchroBody struct {
	ReplicaID uint32
	LSN       int64
	Term      uint64
	Extras    map[uint64][]byte
}

SynchroBody is the typed body of IPROTO_RAFT_CONFIRM / IPROTO_RAFT_ROLLBACK rows. Tarantool's xrow_encode_synchro / xrow_decode_synchro encodes a small fixmap using the standard IPROTO keys (REPLICA_ID, LSN, plus Term — the last is encoded under a Raft-namespace key in some builds; we surface it as Extras when we can't recognise it).

We keep this conservative: required fields ReplicaID and LSN; the rest goes into Extras so downstream code can probe specific keys without blocking on future additions.

func DecodeSynchroBody

func DecodeSynchroBody(b []byte) (*SynchroBody, error)

DecodeSynchroBody parses a synchro body map at b[0:].

type TxOptions

type TxOptions struct {
	// Compression is the block compression policy (zstd level + threshold, or
	// disabled). The zero value compresses at ZstdLevel over payloads of at
	// least CompressThreshold bytes (Tarantool's behaviour); set Disabled for
	// byte-inspectable plain output, or Level/Threshold to tune.
	Compression Compression
}

TxOptions controls EncodeTxBlock / AppendTxBlock behaviour.

type VClock

type VClock map[uint32]int64

VClock is a Lamport vector keyed by replica id. Empty map means an "empty" vclock — Tarantool's `vclock_is_set` returns false in that case. The meta header omits the `PrevVClock:` line when empty, but always emits the `VClock:` line (as `VClock: {}` when empty), because Tarantool's recovery validates the filename signature against it.

Tarantool stores vclocks as a fixed-size array internally but the textual ("{id: lsn, id: lsn}") and msgpack-map encodings are sparse, so a Go map is the natural representation here.

Example

ExampleVClock shows the two everyday VClock operations: Signature (the arithmetic sum of per-replica LSNs, which names the file on disk) and the Tarantool textual form via String.

package main

import (
	"fmt"

	"github.com/tarantool/go-xlog/format"
)

func main() {
	v := format.VClock{1: 10, 2: 5}

	fmt.Println(v)             // Tarantool "{id: lsn, ...}" form, ids ascending.
	fmt.Println(v.Signature()) // 10 + 5 — the filename signature.

}
Output:
{1: 10, 2: 5}
15

func ParseVClock

func ParseVClock(s string) (VClock, error)

ParseVClock decodes a Tarantool vclock literal (`{}`, `{1: 42}`, or `{0: 0, 1: 42, 2: 7}`). Whitespace around braces, commas, and colons is tolerated. Returns an empty (non-nil) VClock for `{}`.

Example

ExampleParseVClock parses a Tarantool vclock literal back into a VClock and recomputes its signature — the inverse of VClock.String.

package main

import (
	"fmt"

	"github.com/tarantool/go-xlog/format"
)

func main() {
	v, err := format.ParseVClock("{0: 0, 1: 42}")
	if err != nil {
		panic(err)
	}

	fmt.Println(v)
	fmt.Println(v.Signature())

}
Output:
{0: 0, 1: 42}
42

func (VClock) Clone

func (v VClock) Clone() VClock

Clone returns a deep copy. The receiver and the result share no map.

func (VClock) Compare

func (v VClock) Compare(o VClock) (int, bool)

Compare implements the standard vector-clock partial order.

Returns:

(-1, true) — v < o on every replica (and strictly less on at least one)
( 0, true) — v == o on every replica
( 1, true) — v > o on every replica (and strictly greater on at least one)
( 0, false) — incomparable (e.g. {1:5,2:3} vs {1:3,2:5})

Missing replicas in either side are treated as LSN 0.

Example

ExampleVClock_Compare demonstrates the vector-clock partial order. Two vclocks are comparable only when one dominates the other on every replica; mixed advances (one ahead on replica 1, the other on replica 2) are incomparable.

package main

import (
	"fmt"

	"github.com/tarantool/go-xlog/format"
)

func main() {
	a := format.VClock{1: 5, 2: 3}
	b := format.VClock{1: 6, 2: 3}
	c := format.VClock{1: 3, 2: 5}

	ord, ok := a.Compare(b)
	fmt.Printf("a vs b: ord=%d ok=%t\n", ord, ok) // a < b

	ord, ok = a.Compare(c)
	fmt.Printf("a vs c: ord=%d ok=%t\n", ord, ok) // incomparable

}
Output:
a vs b: ord=-1 ok=true
a vs c: ord=0 ok=false

func (VClock) Signature

func (v VClock) Signature() int64

Signature returns the arithmetic sum of all per-replica LSNs — Tarantool's `vclock_sum` (src/lib/vclock/vclock.h:230). Used for the `<signature>.<ext>` filename naming.

func (VClock) String

func (v VClock) String() string

String formats the vclock in Tarantool's `vclock_to_string` form: `{id: lsn, id: lsn}` with ids sorted ascending. An empty vclock renders as `{}` — which matches Tarantool's output for the empty case (e.g. `simple.xlog`'s meta has `VClock: {}` only because the file was pre-bootstrap; in normal operation `vclock_is_set` would gate the line out entirely, see meta.go).

Src/lib/vclock/vclock.c:55-72.

type VyKey

type VyKey uint64

VyKey is a vy_log body-map key. The VYLOG body is a msgpack map keyed by these integers (Tarantool's vy_log_key enum). Go-iproto does not cover these, so they are defined here. See src/box/vy_log.c:71-89.

const (
	VyKeyLSMID         VyKey = 0
	VyKeyRangeID       VyKey = 1
	VyKeyRunID         VyKey = 2
	VyKeyBegin         VyKey = 3
	VyKeyEnd           VyKey = 4
	VyKeyIndexID       VyKey = 5
	VyKeySpaceID       VyKey = 6
	VyKeyDef           VyKey = 7
	VyKeySliceID       VyKey = 8
	VyKeyDumpLSN       VyKey = 9
	VyKeyGCLSN         VyKey = 10
	VyKeyTruncateCount VyKey = 11
	VyKeyCreateLSN     VyKey = 12
	VyKeyModifyLSN     VyKey = 13
	VyKeyDropLSN       VyKey = 14
	VyKeyGroupID       VyKey = 15
	VyKeyDumpCount     VyKey = 16
)

vy_log body keys (Tarantool's vy_log_key enum, src/box/vy_log.c:71-89).

func (VyKey) String

func (k VyKey) String() string

String returns the short vy_log key name (e.g. "LSM_ID"), or VyKey(n) for an unrecognised key.

type VyLogBody

type VyLogBody struct {
	Type uint32 // vy_log record type.
	Keys map[VyKey][]byte
}

VyLogBody is the typed body of a VYLOG row. The body is structured as a `{IPROTO_REQUEST_TYPE: vy_log_record_type, vy_log_key_i: ..., ...}` map — vy_log piggybacks on the same IPROTO request-type slot to identify which of its 17 record types this row is, then the rest of the map uses keys from `vy_log_key` (src/box/vy_log.c:71-89).

Keys holds every recognised vy_log_key field as raw msgpack bytes. The caller can decode specific fields lazily.

func DecodeVyLogBody

func DecodeVyLogBody(b []byte) (*VyLogBody, error)

DecodeVyLogBody parses a VYLOG row body. It treats the body as a generic uint-keyed msgpack map — Type comes from the IPROTO_REQUEST_TYPE (0x00) slot and every other entry goes into Keys verbatim.

type XRow

type XRow struct {
	Type      iproto.Type
	ReplicaID uint32
	GroupID   uint32
	LSN       int64
	TSN       int64
	Timestamp float64
	// Flags carries every flag bit *except* IPROTO_FLAG_COMMIT, plus
	// IPROTO_FLAG_COMMIT when the row is a transaction-terminating row. The
	// encoder is responsible for the single-stmt vs multi-stmt logic of
	// whether to actually emit IPROTO_FLAGS / IPROTO_FLAG_COMMIT on the wire.
	Flags    iproto.Flag
	BodyRaw  []byte
	StreamID uint64 // Optional, wire-only; usually zero in xlog.
}

XRow is one row in a tx-block payload.

On disk an xrow is a *header* msgpack map followed by an optional *body* msgpack map. NOP rows (IPROTO_NOP) have no body — the row ends after the header map (src/box/xrow.c:272-282). All other types carry exactly one body map immediately after the header.

BodyRaw holds the body map bytes verbatim (decoded on demand). For NOP rows it is nil.

func DecodeXRow

func DecodeXRow(b []byte) (*XRow, int, error)

DecodeXRow parses one xrow record at b[0:] and returns the row plus the number of bytes consumed (header + body). Unknown header keys are skipped via skipMP, preserving forward compatibility with future IPROTO keys (strict on shape, forgiving on unknown keys).

The "is there a body?" question is answered as: NOP rows (IPROTO_NOP) have no body; everything else has a single trailing msgpack value (the body map). This matches src/box/xrow.c:272-282.

Single-statement tx detection: if IPROTO_TSN was absent, set TSN = LSN and Flags |= IPROTO_FLAG_COMMIT.

func (*XRow) IsCommit

func (r *XRow) IsCommit() bool

IsCommit reports whether IPROTO_FLAG_COMMIT is set on r.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL