format

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: BSD-2-Clause Imports: 17 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 / vinyl RUN and INDEX) and the operations of UPDATE and UPSERT. 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
  • vinyl runs: src/box/vy_run.c, src/box/vy_stmt.c
  • vclock: src/lib/vclock/vclock.h:54-80, src/lib/vclock/vclock.c:55-72
  • update ops: src/box/xrow_update_field.c (xrow_update_op_decode)

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

Aliasing

The decoders do not copy. Every []byte a decoder returns, and every []byte field of a struct it fills, is a sub-slice of its input buffer. This is the contract of the whole package, not a detail of one function, and it is what keeps the read path allocation-free: a caller that only needs the SpaceID of a row never pays for its tuple.

The flip side is that a decoded value is only valid as long as its input is. Retaining an aliased slice past the next reuse of the buffer is a use-after-free in all but name: nothing fails, the bytes just change under it, and it reads as data corruption. A file reader hands the decoders a fresh region per block, so the window is wide and the bug hides; a wire consumer that reads every packet into one receive buffer sees the next row overwrite the tuple it kept from the last one.

The slices that alias the input are, per type:

  • XRow: BodyRaw (DecodeXRow, DecodeXRowInto). A body decoded from BodyRaw aliases the same buffer in turn.
  • DMLBody: Tuple, Key, Ops, OldTuple, NewTuple and Arrow.
  • VyStmtBody: Tuple, Key and Ops.
  • VyRunInfo: MinKey, MaxKey and Bloom.
  • VyPageInfo: MinKey.
  • VyLogBody: the values of Keys. The map itself is new on every decode.
  • Operation: FieldName and Value (DecodeOps, DecodeOpsInto).
  • DecodeOptions.OnUnknown: its raw argument, and so the values of the map CollectUnknown fills.
  • DecodeTxBlock: the row slices, which alias the plain payload, and the plain payload itself for a block that is not compressed.

Nothing else aliases: RaftBody and SynchroBody hold numbers and a VClock map built on every decode, DecodeVyRowIndexBody returns new offsets, and a VClockArray and a Fixheader are values.

To keep a value past its buffer, copy it rather than its fields one by one. XRow, Operation and every body struct - RaftBody and SynchroBody too, for their maps - have Clone, which returns a deep copy sharing no memory with the input, and CloneInto, which deep-copies into a caller-owned value and reuses the capacity of its slices, so a loop that retains rows allocates nothing once warm. CloneOps and CloneOpsInto do the same for a slice of operations; a raw value of OnUnknown or a row slice is a plain []byte for bytes.Clone. A nil slice or map stays nil and an empty one stays empty, so a clone is reflect.DeepEqual to its source.

CloneInto writes into the slices dst holds, so they must belong to dst: dst must be the zero value or hold what Clone or CloneInto last put in it. A dst that was decoded into aliases its buffer, and CloneInto would copy into that buffer; zero it first. A slice of dst that shares its array with the same field of the source - dst is the source, or a shallow copy of it - is replaced rather than reused, so CloneInto never leaves dst sharing memory with the source. A map is never reused, as the decoders never reuse one: the vclock of a RaftBody or a SynchroBody and the keys of a VyLogBody are new maps.

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.

View Source
const VClockMax = 32

VClockMax is the number of components of a vclock — Tarantool's VCLOCK_MAX (src/lib/vclock/vclock.h:54). Replica ids of a VClockArray are 0..VClockMax-1.

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 (
	// ErrVyMissingKey is returned for a run info or a page info row without
	// a key Tarantool requires.
	ErrVyMissingKey = errors.New("missing mandatory key")
	// ErrVyRowIndex is returned for a row index whose data is not a whole
	// number of offsets.
	ErrVyRowIndex = errors.New("bad row index")
)

Sentinel errors of the vinyl body decoders.

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 (
	// ErrOpNotArray is returned when the operations, or one of them, are not
	// a msgpack array.
	ErrOpNotArray = errors.New("not an array")
	// ErrOpArity is returned when an operation has the wrong number of
	// elements for its opcode.
	ErrOpArity = errors.New("wrong number of arguments")
	// ErrOpCodeType is returned when the opcode of an operation is not a
	// string.
	ErrOpCodeType = errors.New("opcode is not a string")
	// ErrUnknownOpCode is returned when the opcode of an operation is not
	// one of Tarantool's.
	ErrUnknownOpCode = errors.New("unknown opcode")
	// ErrOpFieldType is returned when the field of an operation is neither
	// an integer nor a string.
	ErrOpFieldType = errors.New("field is neither an integer nor a string")
	// ErrOpArgType is returned when the position or offset of a splice is
	// not an integer.
	ErrOpArgType = errors.New("splice argument is not an integer")
	// ErrOpIntRange is returned when a field number or a splice argument does
	// not fit an int32, as Tarantool requires (mp_read_int32).
	ErrOpIntRange = errors.New("integer out of int32 range")
)

Sentinel errors of DecodeOps and DecodeOpsInto, wrapped with the position of the operation at fault.

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 ErrBadVyLogRecord = errors.New("bad vylog record")

ErrBadVyLogRecord is returned by DecodeVyLogBody for a VYLOG row body that does not hold a vinyl log record.

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.

View Source
var ErrVClockReplicaID = errors.New("vclock: replica id out of range")

ErrVClockReplicaID reports a replica id that does not fit a VClockArray (id >= VClockMax). The VClock map accepts any id; a VClockArray, like Tarantool's struct vclock, does not.

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 DecodeDMLBodyInto added in v0.2.0

func DecodeDMLBodyInto(b []byte, dst *DMLBody, opts *DecodeOptions) error

DecodeDMLBodyInto parses a DML body map at b[0:] into dst. It is the zero-allocation core of DecodeDMLBody: a hot loop can decode every body into one caller-owned DMLBody. It first resets dst to the zero DMLBody, so a reused dst carries no field of a prior row, then sets the fields of the keys b holds. The raw fields of dst (Tuple, Key, Ops, OldTuple, NewTuple, Arrow) alias b; see Aliasing in the package doc. A key with no field goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. On error dst holds what was decoded before it.

Example

ExampleDecodeDMLBodyInto decodes the bodies of a stream of rows into one DMLBody, with no allocation per row. A body key DMLBody has no field for is skipped unless the options ask for it: here CollectUnknown gathers them, as a path that writes the body again must.

package main

import (
	"fmt"

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

func main() {
	bodies := [][]byte{
		// {IPROTO_SPACE_ID: 512, IPROTO_INDEX_BASE: 1, IPROTO_KEY: [1]}
		{0x83, 0x10, 0xcd, 0x02, 0x00, 0x15, 0x01, 0x20, 0x91, 0x01},
		// {IPROTO_SPACE_ID: 513, 0x7e: 5}
		{0x82, 0x10, 0xcd, 0x02, 0x01, 0x7e, 0x05},
	}

	var body format.DMLBody

	for _, raw := range bodies {
		var unknown map[uint64][]byte

		if err := format.DecodeDMLBodyInto(raw, &body, format.CollectUnknown(&unknown)); err != nil {
			panic(err)
		}

		fmt.Printf("space=%d index_base=%d key=%x unknown=%x\n", body.SpaceID, body.IndexBase, body.Key, unknown)
	}

}
Output:
space=512 index_base=1 key=9101 unknown=map[]
space=513 index_base=0 key= unknown=map[7e:05]

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 DecodeRaftBodyInto added in v0.2.0

func DecodeRaftBodyInto(b []byte, dst *RaftBody, opts *DecodeOptions) error

DecodeRaftBodyInto parses a RAFT body map into dst, the core of DecodeRaftBody. It first resets dst to the zero RaftBody, so a dst reused across rows carries no field of a prior row, then sets the fields of the keys b holds. A key with no field goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. The VClock map of a prior row is never reused: a body without a vclock decodes with no allocation, but a vclock allocates a new map. VClockArray is the allocation-free alternative to VClock; the body decoders do not fill one. On error dst holds what was decoded before it.

func DecodeSynchroBodyInto added in v0.2.0

func DecodeSynchroBodyInto(b []byte, dst *SynchroBody, opts *DecodeOptions) error

DecodeSynchroBodyInto parses a synchro body map at b[0:] into dst, the core of DecodeSynchroBody. It first resets dst to the zero SynchroBody, so a dst reused across rows carries no field of a prior row, then sets the fields of the keys b holds. A key with no field goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. The ConfirmedVClock map of a prior row is never reused: a CONFIRM or ROLLBACK decodes with no allocation, but a PROMOTE or DEMOTE with a vclock allocates a new map. VClockArray is the allocation-free alternative to VClock; the body decoders do not fill one. On error dst holds what was decoded before it.

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, and plain is a fresh allocation for a zrow block and aliases b for a row block; see Aliasing in the package doc.

func DecodeVClockArrayInto added in v0.2.0

func DecodeVClockArrayInto(b []byte, dst *VClockArray) (int, error)

DecodeVClockArrayInto decodes a msgpack map of {uint replica_id: uint lsn} — the encoding of IPROTO_RAFT_VCLOCK and IPROTO_VCLOCK — into dst and returns the number of bytes consumed. It resets dst first, so nothing of a previous value survives, and allocates nothing on success.

A replica id >= VClockMax fails with ErrVClockReplicaID. A repeated id keeps its last LSN, as the VClock decoder does. On error dst is left reset.

func DecodeVyLogBodyInto added in v0.2.0

func DecodeVyLogBodyInto(b []byte, dst *VyLogBody, opts *DecodeOptions) error

DecodeVyLogBodyInto parses a VYLOG row body into dst, the core of DecodeVyLogBody. It first resets dst to the zero VyLogBody, so a dst reused across rows carries no field of a prior row. The values of Keys alias b; see Aliasing in the package doc. A body key other than IPROTO_TUPLE goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. The Keys map of a prior row is never reused: every record allocates a new one. On error dst holds what was decoded before it.

func DecodeVyPageInfoBodyInto added in v0.2.0

func DecodeVyPageInfoBodyInto(b []byte, dst *VyPageInfo, opts *DecodeOptions) error

DecodeVyPageInfoBodyInto parses the body of a VY_INDEX_PAGE_INFO row into dst, the zero-allocation core of DecodeVyPageInfoBody. It first resets dst to the zero VyPageInfo, so a reused dst carries no field of a prior row, then sets the fields of the keys b holds. MinKey aliases b; see Aliasing in the package doc. A key with no field goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. On error dst holds what was decoded before it.

func DecodeVyRowIndexBody added in v0.2.0

func DecodeVyRowIndexBody(b []byte) ([]uint32, error)

DecodeVyRowIndexBody parses the body of a VY_RUN_ROW_INDEX row: the offset of each statement of the page in the rows of the page, uncompressed (vy_row_index_encode, src/box/vy_run.c).

func DecodeVyRunInfoBodyInto added in v0.2.0

func DecodeVyRunInfoBodyInto(b []byte, dst *VyRunInfo, opts *DecodeOptions) error

DecodeVyRunInfoBodyInto parses the body of a VY_INDEX_RUN_INFO row into dst, the zero-allocation core of DecodeVyRunInfoBody. It first resets dst to the zero VyRunInfo, so a reused dst carries no field of a prior row, then sets the fields of the keys b holds. The raw fields of dst (MinKey, MaxKey, Bloom) alias b; see Aliasing in the package doc. A key with no field goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. On error dst holds what was decoded before it.

func DecodeVyStmtBodyInto added in v0.2.0

func DecodeVyStmtBodyInto(b []byte, dst *VyStmtBody, opts *DecodeOptions) error

DecodeVyStmtBodyInto parses the body of a statement of a .run file into dst, the zero-allocation core of DecodeVyStmtBody. It first resets dst to the zero VyStmtBody, so a dst reused across statements carries no field of a prior one, then sets the fields of the keys b holds. The raw fields of dst (Tuple, Key, Ops) alias b; see Aliasing in the package doc. A body key with no field goes to opts.OnUnknown, or is skipped when opts or its OnUnknown is nil. On error dst holds what was decoded before it.

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 / Sync / SchemaVersion / Flags / Present 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; see Aliasing in the package doc.

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_SYNC / IPROTO_SCHEMA_VERSION / IPROTO_REPLICA_ID / IPROTO_LSN / IPROTO_GROUP_ID / IPROTO_TIMESTAMP / IPROTO_STREAM_ID 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).

EncodeXRow never reads r.Present: a key is emitted by the rules above, from the value of its field alone. Present is decode-only, and every XRow built by hand has Present == 0; gating a key on it would encode such rows with an empty header (see XRow.Present).

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.

The keys are emitted in the order of xrow_header_encode (src/box/xrow.c:338-452): REQUEST_TYPE, SYNC, REPLICA_ID, GROUP_ID, LSN, TIMESTAMP, TSN, STREAM_ID, FLAGS, so a header Tarantool would write encodes to the same bytes. xrow_header_encode never writes IPROTO_SCHEMA_VERSION; only iproto_header_encode (src/box/xrow.c:497-515) does, in a response header, right after IPROTO_SYNC, so EncodeXRow emits it there too.

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; the vinyl row types are named as Tarantool's iproto_type_name names them ("RUNINFO", "PAGEINFO", "ROWINDEX"); 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 is IPROTO_SPACE_ID, the space the request applies to.
	SpaceID uint32
	// IndexID is IPROTO_INDEX_ID, the index a DELETE or UPDATE looks its key
	// up in; 0 (the primary index) when the body carries none. Tarantool
	// rebinds a request on a secondary index to the primary key before it
	// writes the WAL, so a journal row normally has none.
	IndexID uint32
	// IndexBase is IPROTO_INDEX_BASE of an UPDATE or UPSERT: the number of
	// the first field in the field numbers of Ops (or of Tuple, which holds
	// the operations of an UPDATE). 0 when the body carries none; a request
	// from Lua carries 1.
	IndexBase uint32
	// Tuple is IPROTO_TUPLE, raw msgpack: the tuple of an INSERT, REPLACE or
	// UPSERT, the operations of an UPDATE. It aliases the input.
	Tuple []byte
	// Key is IPROTO_KEY, raw msgpack: the key of an UPDATE or DELETE. It
	// aliases the input.
	Key []byte
	// Ops is IPROTO_OPS, raw msgpack: the operations of an UPSERT. It
	// aliases the input.
	Ops []byte
	// OldTuple is IPROTO_OLD_TUPLE, raw msgpack: the tuple before the change,
	// written for replica-applied and audited rows. It aliases the input.
	OldTuple []byte
	// NewTuple is IPROTO_NEW_TUPLE, raw msgpack: the tuple after the change,
	// written for replica-applied and audited rows. It aliases the input.
	NewTuple []byte
	// Arrow is IPROTO_ARROW of an INSERT_ARROW, raw msgpack: the whole
	// MP_EXT value, ext header included, holding the Arrow IPC stream. It
	// aliases the input.
	Arrow []byte
}

DMLBody is the typed body of an INSERT/REPLACE/UPDATE/DELETE/UPSERT row (and of INSERT_ARROW). Keys come from src/box/iproto_constants.h and are written by Tarantool's xrow_encode_dml (src/box/xrow.c). Each variable- length field (Tuple, Key, Ops, OldTuple, NewTuple, Arrow) is held as raw msgpack that aliases the input, so callers who only need the SpaceID pay no decode cost; see Aliasing in the package doc.

A body key with no field (IPROTO_TUPLE_META, IPROTO_BEGIN_KEY, ...) is skipped, unless DecodeOptions.OnUnknown of DecodeDMLBodyInto receives it.

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. It is DecodeDMLBodyInto into a new DMLBody, with no options: a key with no field is skipped.

func (*DMLBody) Clone added in v0.2.0

func (b *DMLBody) Clone() *DMLBody

Clone returns a deep copy of b: its Tuple, Key, Ops, OldTuple, NewTuple and Arrow are copied into one new allocation. A nil receiver returns nil.

func (*DMLBody) CloneInto added in v0.2.0

func (b *DMLBody) CloneInto(dst *DMLBody)

CloneInto deep-copies b into dst, overwriting every field, and copies each raw field of b into the array of the same field of dst when it has the capacity. dst must own its slices; see Aliasing in the package doc. dst may be b.

type DecodeOptions added in v0.2.0

type DecodeOptions struct {
	// OnUnknown, if non-nil, is called for each body key with no typed
	// field, in the order of the body. raw is the msgpack value of the key
	// and aliases the input; see Aliasing in the package doc. Nil means
	// unknown keys are skipped, and the decode stays allocation-free.
	//
	// A path that decodes a body and encodes it again must set OnUnknown and
	// write the keys it receives back, or the encoded body loses them.
	OnUnknown func(key uint64, raw []byte)
}

DecodeOptions configure the body decoders that fill a caller-owned struct (DecodeDMLBodyInto, DecodeRaftBodyInto, DecodeSynchroBodyInto, DecodeVyLogBodyInto, DecodeVyStmtBodyInto, DecodeVyRunInfoBodyInto and DecodeVyPageInfoBodyInto). A nil *DecodeOptions is the zero value: unknown keys are skipped.

func CollectUnknown added in v0.2.0

func CollectUnknown(dst *map[uint64][]byte) *DecodeOptions

CollectUnknown returns DecodeOptions whose OnUnknown stores every unknown body key and its raw msgpack value into *dst, making the map on the first one: *dst stays nil for a body with no unknown key. The values alias the input; see Aliasing in the package doc. A later key replaces an earlier duplicate. *dst is not cleared between decodes; set it to nil to start over.

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 OpCode added in v0.2.0

type OpCode byte

OpCode is the opcode of an update operation: the one-character string an operation array starts with.

const (
	// OpSet assigns Value to the field: [=, field, value].
	OpSet OpCode = '='
	// OpInsert inserts Value before the field: [!, field, value].
	OpInsert OpCode = '!'
	// OpAdd adds the number Value to the field: [+, field, value].
	OpAdd OpCode = '+'
	// OpSubtract subtracts the number Value from the field: [-, field, value].
	OpSubtract OpCode = '-'
	// OpAnd is the bitwise AND of the field and the unsigned Value:
	// [&, field, value].
	OpAnd OpCode = '&'
	// OpOr is the bitwise OR of the field and the unsigned Value:
	// [|, field, value].
	OpOr OpCode = '|'
	// OpXor is the bitwise XOR of the field and the unsigned Value:
	// [^, field, value].
	OpXor OpCode = '^'
	// OpSplice replaces Offset bytes of the string field at Position with the
	// string Value: [:, field, position, offset, value].
	OpSplice OpCode = ':'
	// OpDelete deletes Value fields starting with the field, Value being a
	// positive integer: [#, field, count].
	OpDelete OpCode = '#'
)

The opcodes Tarantool knows (xrow_update_op_by).

func (OpCode) String added in v0.2.0

func (c OpCode) String() string

String returns the opcode as Tarantool writes it, "=" for OpSet, or OpCode(0x..) for a byte that is no opcode.

type Operation added in v0.2.0

type Operation struct {
	// Op is the opcode.
	Op OpCode
	// Field is the number of the field the operation addresses when
	// FieldName is nil, and 0 otherwise.
	Field int32
	// FieldName is the name or JSON path of the field the operation
	// addresses, nil when it addresses it by number. It aliases the input.
	// Tarantool writes a string; a bin is accepted too.
	FieldName []byte
	// HasSplice reports that Position and Offset are set: the operation is
	// a splice.
	HasSplice bool
	// Position is the position in the string of a splice where Offset bytes
	// are cut and Value pasted (Tarantool's offset); a negative one counts
	// from the end of the string.
	Position int32
	// Offset is the number of bytes a splice cuts (Tarantool's cut_length).
	Offset int32
	// Value is the raw msgpack of the last element of the operation: the
	// value of =, !, +, -, &, |, ^, the number of fields of # and the string
	// : pastes. It aliases the input.
	Value []byte
}

Operation is a decoded update operation. FieldName and Value alias the input; see Aliasing in the package doc.

Field numbers and the splice position are as written in the request. Tarantool subtracts the index base of the request from a non-negative one: IPROTO_INDEX_BASE (DMLBody.IndexBase), which box.space:update and :upsert set to 1 and which is 0 when absent. A negative one counts from the end, -1 being the last field. The operations of a vinyl run (VyStmtBody.Ops) are 0-based.

func CloneOps added in v0.2.0

func CloneOps(ops []Operation) []Operation

CloneOps returns a deep copy of ops: a new slice whose operations share no memory with ops, their FieldName and Value copied into one allocation. nil returns nil, and an empty non-nil ops an empty non-nil slice.

func CloneOpsInto added in v0.2.0

func CloneOpsInto(src, dst []Operation) []Operation

CloneOpsInto is CloneOps into dst, as DecodeOpsInto is DecodeOps: it deep-copies the operations of src into dst[:len(src)], reusing the capacity of dst and, by Operation.CloneInto, that of the operations it holds up to its capacity, and returns the result. It does not allocate when dst holds as many operations as src, each with the capacity for its bytes. The operations of dst must own their slices, as for CloneInto. A nil src returns nil.

func DecodeOps added in v0.2.0

func DecodeOps(b []byte) ([]Operation, error)

DecodeOps decodes the operations of an UPDATE or UPSERT: the IPROTO_TUPLE of an UPDATE (DMLBody.Tuple - Tarantool keeps the UPDATE operations under IPROTO_TUPLE for legacy reasons, src/box/iproto_constants.h) or the IPROTO_OPS of an UPSERT (DMLBody.Ops). The Ops of a vinyl UPSERT (VyStmtBody.Ops) is an array of such operation lists; DecodeOps decodes one list.

It checks the shape of each operation - its number of elements and the types of its opcode, field and splice arguments - not the type of its value, which Tarantool checks when it applies the operation. Bytes after the array are ignored. The FieldName and Value of every operation alias b; see Aliasing in the package doc.

func DecodeOpsInto added in v0.2.0

func DecodeOpsInto(b []byte, dst []Operation) ([]Operation, error)

DecodeOpsInto is DecodeOps decoding into dst[:0], reusing its capacity: it does not allocate when cap(dst) holds every operation. It returns the operations, or dst[:0] and an error. The FieldName and Value of every operation alias b; see Aliasing in the package doc.

func (*Operation) Clone added in v0.2.0

func (op *Operation) Clone() Operation

Clone returns a deep copy of op: its FieldName and Value are copied into one new allocation. It returns a value, as DecodeOps does.

func (*Operation) CloneInto added in v0.2.0

func (op *Operation) CloneInto(dst *Operation)

CloneInto deep-copies op into dst, overwriting every field, and copies FieldName and Value into the arrays of those of dst when they have the capacity. dst must own its slices; see Aliasing in the package doc. dst may be op.

type Present added in v0.2.0

type Present uint32

Present is a set of the header keys of a row, one bit per key the decoder recognises. The bit of a key is 1 << its IPROTO code, so PresentLSN is 1 << IPROTO_LSN.

const (
	PresentType          Present = 1 << iproto.IPROTO_REQUEST_TYPE
	PresentSync          Present = 1 << iproto.IPROTO_SYNC
	PresentReplicaID     Present = 1 << iproto.IPROTO_REPLICA_ID
	PresentLSN           Present = 1 << iproto.IPROTO_LSN
	PresentTimestamp     Present = 1 << iproto.IPROTO_TIMESTAMP
	PresentSchemaVersion Present = 1 << iproto.IPROTO_SCHEMA_VERSION
	PresentGroupID       Present = 1 << iproto.IPROTO_GROUP_ID
	// PresentTSN is set only when IPROTO_TSN is on the wire. The TSN the
	// decoder infers from the LSN of a single-statement row, which carries no
	// IPROTO_TSN, does not set it.
	PresentTSN Present = 1 << iproto.IPROTO_TSN
	// PresentFlags is set only when IPROTO_FLAGS is on the wire. The
	// IPROTO_FLAG_COMMIT the decoder infers for a single-statement row does
	// not set it.
	PresentFlags    Present = 1 << iproto.IPROTO_FLAGS
	PresentStreamID Present = 1 << iproto.IPROTO_STREAM_ID
)

The header keys of a row recorded in XRow.Present.

type RaftBody

type RaftBody struct {
	// Term is IPROTO_RAFT_TERM, the Raft term of the instance.
	Term uint64
	// Vote is IPROTO_RAFT_VOTE, the instance voted for in Term, 0 for none.
	Vote uint64
	// State is IPROTO_RAFT_STATE, the Raft state (follower, candidate,
	// leader).
	State uint32
	// VClock is IPROTO_RAFT_VCLOCK, the vclock of the instance, nil when the
	// body carries none.
	VClock VClock
	// LeaderID is IPROTO_RAFT_LEADER_ID, the leader the instance knows of.
	LeaderID uint32
	// IsLeaderSeen is IPROTO_RAFT_IS_LEADER_SEEN, whether the instance sees
	// the leader directly.
	IsLeaderSeen bool
}

RaftBody is the typed body of a IPROTO_RAFT row. Keys come from src/box/iproto_constants.h:465-471. A body key with no field is skipped, unless DecodeOptions.OnUnknown of DecodeRaftBodyInto receives it.

func DecodeRaftBody

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

DecodeRaftBody parses a RAFT body map. It is DecodeRaftBodyInto into a new RaftBody, with no options: a key with no field is skipped.

func (*RaftBody) Clone added in v0.2.0

func (b *RaftBody) Clone() *RaftBody

Clone returns a deep copy of b. A RaftBody aliases nothing, but its VClock is a map, which the copy gets its own of. A nil receiver returns nil.

func (*RaftBody) CloneInto added in v0.2.0

func (b *RaftBody) CloneInto(dst *RaftBody)

CloneInto deep-copies b into dst, overwriting every field. The VClock of dst is a new map, or nil when that of b is. dst may be b.

type SynchroBody

type SynchroBody struct {
	// ReplicaID is the owner of the synchronous queue (queue_owner_id): the
	// instance whose transactions the row confirms or rolls back, and for a
	// PROMOTE/DEMOTE the previous owner. It is not the issuer of the row,
	// which is the replica id of the row header.
	ReplicaID uint32
	// LSN is the LSN of the owner the row applies to: a CONFIRM confirms the
	// transactions up to it, a ROLLBACK rolls back those from it on, and a
	// PROMOTE/DEMOTE confirms up to it and rolls back the rest.
	LSN int64
	// Term is the Raft term a PROMOTE/DEMOTE is issued in (IPROTO_TERM).
	// CONFIRM and ROLLBACK carry none.
	Term uint64
	// ConfirmedVClock is the confirmed LSN of every previous queue owner
	// (IPROTO_VCLOCK) of a PROMOTE/DEMOTE, nil when the row carries none.
	ConfirmedVClock VClock
}

SynchroBody is the typed body of the synchronous replication rows: IPROTO_RAFT_CONFIRM, IPROTO_RAFT_ROLLBACK, IPROTO_RAFT_PROMOTE and IPROTO_RAFT_DEMOTE. Tarantool's xrow_encode_synchro writes a map of ordinary IPROTO keys (src/box/xrow.c):

  • CONFIRM and ROLLBACK: IPROTO_REPLICA_ID and IPROTO_LSN.
  • PROMOTE and DEMOTE: IPROTO_REPLICA_ID, IPROTO_LSN, IPROTO_TERM and, when it is set, IPROTO_VCLOCK. The PROMOTE that checkpoints the limbo in a snapshot carries the vclock.

Any other key is skipped, unless DecodeOptions.OnUnknown of DecodeSynchroBodyInto receives it.

func DecodeSynchroBody

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

DecodeSynchroBody parses a synchro body map at b[0:]. It is DecodeSynchroBodyInto into a new SynchroBody, with no options: a key with no field is skipped.

func (*SynchroBody) Clone added in v0.2.0

func (b *SynchroBody) Clone() *SynchroBody

Clone returns a deep copy of b. A SynchroBody aliases nothing, but its ConfirmedVClock is a map, which the copy gets its own of. A nil receiver returns nil.

func (*SynchroBody) CloneInto added in v0.2.0

func (b *SynchroBody) CloneInto(dst *SynchroBody)

CloneInto deep-copies b into dst, overwriting every field. The ConfirmedVClock of dst is a new map, or nil when that of b is. dst may be b.

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 VClockArray added in v0.2.0

type VClockArray struct {
	// contains filtered or unexported fields
}

VClockArray is a dense vclock: Tarantool's struct vclock (src/lib/vclock/vclock.h:71-80) without the cached signature. It holds one LSN per replica id 0..VClockMax-1 and a bitmap of the ids that are set, so it copies by value and allocates nothing — the hot-path counterpart of VClock, which is a map. Convert at the boundary with VClock and VClockArrayFrom.

The zero value is an empty vclock. The LSN of an id that is not set is always 0, so two VClockArrays are == exactly when they set the same ids to the same LSNs.

The methods take a pointer receiver so that a call does not copy the 264-byte value; print a VClockArray through a pointer (fmt.Print(&v)) for String to apply.

func VClockArrayFrom added in v0.2.0

func VClockArrayFrom(v VClock) (VClockArray, error)

VClockArrayFrom converts v to a VClockArray. It returns ErrVClockReplicaID if v holds an id >= VClockMax. A nil or empty v gives the zero VClockArray.

func (*VClockArray) Compare added in v0.2.0

func (v *VClockArray) Compare(o *VClockArray) (int, bool)

Compare orders v against o with the semantics of VClock.Compare: (-1, true) if v is before o, (0, true) if they are equal, (1, true) if v is after o, and (0, false) if they are incomparable. An id that is not set counts as LSN 0. This is Tarantool's vclock_compare (src/lib/vclock/vclock.h:310-344).

func (*VClockArray) Get added in v0.2.0

func (v *VClockArray) Get(id uint32) (int64, bool)

Get returns the LSN of replica id and whether id is set. An id >= VClockMax is never set.

func (*VClockArray) Has added in v0.2.0

func (v *VClockArray) Has(id uint32) bool

Has reports whether replica id is set.

func (*VClockArray) IsZero added in v0.2.0

func (v *VClockArray) IsZero() bool

IsZero reports whether no replica id is set — the zero VClockArray.

func (*VClockArray) Len added in v0.2.0

func (v *VClockArray) Len() int

Len returns the number of replica ids that are set.

func (*VClockArray) Reset added in v0.2.0

func (v *VClockArray) Reset()

Reset clears every replica id, making v the zero VClockArray.

func (*VClockArray) Set added in v0.2.0

func (v *VClockArray) Set(id uint32, lsn int64) error

Set sets the LSN of replica id, marking it set even when lsn is 0. It returns ErrVClockReplicaID, leaving v unchanged, if id >= VClockMax.

func (*VClockArray) Signature added in v0.2.0

func (v *VClockArray) Signature() int64

Signature returns the sum of the LSNs — Tarantool's vclock_sum (src/lib/vclock/vclock.h:236), the same value as VClock.Signature.

func (*VClockArray) String added in v0.2.0

func (v *VClockArray) String() string

String formats v as VClock.String does — Tarantool's vclock_to_string form `{id: lsn, id: lsn}` with ids ascending, `{}` when empty (src/lib/vclock/vclock.c:55-72).

func (*VClockArray) VClock added in v0.2.0

func (v *VClockArray) VClock() VClock

VClock converts v to a VClock map. An empty v gives an empty, non-nil VClock, as decoding an empty msgpack vclock map does.

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 is the record type.
	Type VyLogType
	// Keys holds every field of the record as raw msgpack, keyed by
	// vy_log_key. The caller decodes the fields it needs lazily. Its values
	// alias the input.
	Keys map[VyKey][]byte
}

VyLogBody is the typed body of a VYLOG row: one vinyl log record. Tarantool's vy_log_record_encode (src/box/vy_log.c) stores a record as an IPROTO_INSERT of no space whose tuple is a pair of the record type and a map of its fields keyed by vy_log_key:

{IPROTO_TUPLE: [type, {VyKey: value, ...}]}

A field equal to zero is not written: an absent key reads as zero (the first LSM tree, for instance, has id 0 and no VyKeyLSMID).

func DecodeVyLogBody

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

DecodeVyLogBody parses a VYLOG row body. Body keys other than IPROTO_TUPLE are ignored, as Tarantool's vy_log_record_decode does. It is DecodeVyLogBodyInto into a new VyLogBody, with no options.

func (*VyLogBody) Clone added in v0.2.0

func (b *VyLogBody) Clone() *VyLogBody

Clone returns a deep copy of b: a new Keys map whose values are copied into one new allocation. A nil receiver returns nil.

func (*VyLogBody) CloneInto added in v0.2.0

func (b *VyLogBody) CloneInto(dst *VyLogBody)

CloneInto deep-copies b into dst, overwriting every field. Keys is a map, which is never reused: dst gets a new one, as from Clone. dst may be b.

type VyLogType added in v0.2.0

type VyLogType uint32

VyLogType is the type of a vinyl log record (Tarantool's vy_log_record_type enum, src/box/vy_log.h).

const (
	VyLogCreateLSM        VyLogType = 0
	VyLogDropLSM          VyLogType = 1
	VyLogInsertRange      VyLogType = 2
	VyLogDeleteRange      VyLogType = 3
	VyLogPrepareRun       VyLogType = 4
	VyLogCreateRun        VyLogType = 5
	VyLogDropRun          VyLogType = 6
	VyLogForgetRun        VyLogType = 7
	VyLogInsertSlice      VyLogType = 8
	VyLogDeleteSlice      VyLogType = 9
	VyLogDumpLSM          VyLogType = 10
	VyLogSnapshot         VyLogType = 11
	VyLogTruncateLSM      VyLogType = 12
	VyLogModifyLSM        VyLogType = 13
	VyLogForgetLSM        VyLogType = 14
	VyLogPrepareLSM       VyLogType = 15
	VyLogRebootstrap      VyLogType = 16
	VyLogAbortRebootstrap VyLogType = 17
)

Vinyl log record types (src/box/vy_log.h).

func (VyLogType) String added in v0.2.0

func (t VyLogType) String() string

String returns the short record type name (e.g. "CREATE_LSM"), or VyLogType(n) for an unrecognised type.

type VyPageInfo added in v0.2.0

type VyPageInfo struct {
	// Offset is the offset of the page in the .run file: where its tx block
	// starts.
	Offset uint64
	// Size is the size of the tx block of the page in the file.
	Size uint32
	// UnpackedSize is the size of the rows of the page, uncompressed.
	UnpackedSize uint32
	// RowCount is the number of statements of the page.
	RowCount uint32
	// MinKey is the smallest key of the page, a raw msgpack array. It
	// aliases the input.
	MinKey []byte
	// RowIndexOffset is the offset of the VY_RUN_ROW_INDEX row in the rows of
	// the page, uncompressed.
	RowIndexOffset uint32
}

VyPageInfo is the typed body of a VY_INDEX_PAGE_INFO row of a .index file (vy_page_info_encode, src/box/vy_run.c).

func DecodeVyPageInfoBody added in v0.2.0

func DecodeVyPageInfoBody(b []byte) (*VyPageInfo, error)

DecodeVyPageInfoBody parses the body of a VY_INDEX_PAGE_INFO row. Like Tarantool's vy_page_info_decode it requires every key. It is DecodeVyPageInfoBodyInto into a new VyPageInfo, with no options: a key with no field is skipped.

func (*VyPageInfo) Clone added in v0.2.0

func (page *VyPageInfo) Clone() *VyPageInfo

Clone returns a deep copy of page: its MinKey is a new slice. A nil receiver returns nil.

func (*VyPageInfo) CloneInto added in v0.2.0

func (page *VyPageInfo) CloneInto(dst *VyPageInfo)

CloneInto deep-copies page into dst, overwriting every field, and copies page.MinKey into the array of dst.MinKey when it has the capacity. dst must own its slices; see Aliasing in the package doc. dst may be page.

type VyRunInfo added in v0.2.0

type VyRunInfo struct {
	// MinKey and MaxKey are the smallest and the greatest key of the run,
	// raw msgpack arrays. They alias the input.
	MinKey []byte
	MaxKey []byte
	// MinLSN and MaxLSN bound the LSNs of the statements of the run.
	MinLSN int64
	MaxLSN int64
	// PageCount is the number of pages of the run, and of VY_INDEX_PAGE_INFO
	// rows after this one.
	PageCount uint32
	// BloomVersion is the version of the bloom filter: 3 for the current
	// one, 1 or 2 for the legacy ones, 0 when the run has none.
	BloomVersion uint8
	// Bloom is the bloom filter of the keys of the run, raw msgpack. It
	// aliases the input.
	Bloom []byte
	// StmtStat counts the statements of the run by type.
	StmtStat VyStmtStat
}

VyRunInfo is the typed body of a VY_INDEX_RUN_INFO row, the first row of a .index file (vy_run_info_encode, src/box/vy_run.c).

func DecodeVyRunInfoBody added in v0.2.0

func DecodeVyRunInfoBody(b []byte) (*VyRunInfo, error)

DecodeVyRunInfoBody parses the body of a VY_INDEX_RUN_INFO row. Like Tarantool's vy_run_info_decode it requires the keys, the LSNs and the page count. It is DecodeVyRunInfoBodyInto into a new VyRunInfo, with no options: a key with no field is skipped.

func (*VyRunInfo) Clone added in v0.2.0

func (info *VyRunInfo) Clone() *VyRunInfo

Clone returns a deep copy of info: its MinKey, MaxKey and Bloom are copied into one new allocation. A nil receiver returns nil.

func (*VyRunInfo) CloneInto added in v0.2.0

func (info *VyRunInfo) CloneInto(dst *VyRunInfo)

CloneInto deep-copies info into dst, overwriting every field, and copies each raw field of info into the array of the same field of dst when it has the capacity. dst must own its slices; see Aliasing in the package doc. dst may be info.

type VyStmtBody added in v0.2.0

type VyStmtBody struct {
	// SpaceID is the IPROTO_SPACE_ID of the row. Tarantool does not write it
	// to a run, so it is normally 0; the space and index of a run are in its
	// path, <vinyl_dir>/<space_id>/<index_id>/<run_id>.run.
	SpaceID uint32
	// Tuple is the IPROTO_TUPLE of an INSERT, REPLACE or UPSERT, raw msgpack.
	// In a primary index run it is the whole tuple; in a secondary index run
	// it is the key of the tuple: the parts of the secondary index followed
	// by those of the primary one. It aliases the input.
	Tuple []byte
	// Key is the IPROTO_KEY of a DELETE, raw msgpack: the key the DELETE
	// removes, in the parts of the index (as Tuple of a secondary index). It
	// aliases the input.
	Key []byte
	// Ops is the IPROTO_OPS of an UPSERT, raw msgpack. Unlike the operations
	// of an UPSERT request it is an array of operation lists, one per UPSERT
	// squashed into the statement, oldest first: when the key does not exist,
	// Tuple is inserted and the first list is not applied, and each other list
	// is applied in turn. Field numbers are 0-based. It aliases the input.
	Ops []byte
	// Flags are the statement flags of IPROTO_TUPLE_META.
	Flags VyStmtFlags
}

VyStmtBody is the typed body of a statement of a .run file: an INSERT, REPLACE, DELETE or UPSERT row written by Tarantool's vy_stmt_encode_primary or vy_stmt_encode_secondary (src/box/vy_stmt.c). It is a DML body without a space id, plus the statement flags.

func DecodeVyStmtBody added in v0.2.0

func DecodeVyStmtBody(b []byte) (*VyStmtBody, error)

DecodeVyStmtBody parses the body of a statement of a .run file. It is DecodeVyStmtBodyInto into a new VyStmtBody, with no options: a key with no field is skipped.

Example

ExampleDecodeVyStmtBody decodes a DELETE statement of a vinyl run: the key it deletes and the flag that it did not delete the old tuple from the secondary indexes, as Tarantool writes it for a space with defer_deletes.

package main

import (
	"fmt"

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

func main() {
	// {IPROTO_KEY: [2], IPROTO_TUPLE_META: {VY_STMT_FLAGS: 1}}
	raw := []byte{0x82, 0x20, 0x91, 0x02, 0x2a, 0x81, 0x01, 0x01}

	stmt, err := format.DecodeVyStmtBody(raw)
	if err != nil {
		panic(err)
	}

	fmt.Printf("key=%x deferred_delete=%t\n", stmt.Key, stmt.Flags&format.VyStmtDeferredDelete != 0)
}
Output:
key=9102 deferred_delete=true

func (*VyStmtBody) Clone added in v0.2.0

func (b *VyStmtBody) Clone() *VyStmtBody

Clone returns a deep copy of b: its Tuple, Key and Ops are copied into one new allocation. A nil receiver returns nil.

func (*VyStmtBody) CloneInto added in v0.2.0

func (b *VyStmtBody) CloneInto(dst *VyStmtBody)

CloneInto deep-copies b into dst, overwriting every field, and copies each raw field of b into the array of the same field of dst when it has the capacity. dst must own its slices; see Aliasing in the package doc. dst may be b.

type VyStmtFlags added in v0.2.0

type VyStmtFlags uint64

VyStmtFlags are the persistent flags of a vinyl statement (vy_stmt_flags, src/box/vy_stmt.h).

const (
	// VyStmtDeferredDelete marks a REPLACE or DELETE of a primary index that
	// skipped deleting the old tuple from the secondary indexes: compaction
	// of the primary index generates those DELETEs. Only primary index runs
	// carry it.
	VyStmtDeferredDelete VyStmtFlags = 1 << 0
	// VyStmtSkipRead marks a statement reads ignore: the deferred DELETEs
	// compaction of the primary index writes to a secondary index.
	VyStmtSkipRead VyStmtFlags = 1 << 1
)

type VyStmtStat added in v0.2.0

type VyStmtStat struct {
	Inserts  uint64
	Replaces uint64
	Deletes  uint64
	Upserts  uint64
}

VyStmtStat counts the statements of a run by type.

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.
	// Sync is IPROTO_SYNC, the id that correlates an IPROTO request with its
	// response. Wire-only: a row written to an xlog carries none.
	Sync uint64
	// SchemaVersion is IPROTO_SCHEMA_VERSION, the schema version a response
	// (or a request that checks it) carries. Wire-only, like Sync.
	SchemaVersion uint64
	// Present records which header keys the decoded row carried on the wire,
	// so a key that is absent can be told from a key present with the value
	// 0: a replication heartbeat, for one, is a row without IPROTO_LSN
	// (!HasLSN()), not a row whose LSN is 0. Only DecodeXRowInto (and
	// DecodeXRow) sets it, and it describes the row as decoded: changing a
	// field afterwards does not update it.
	//
	// DECODE-ONLY AND ADVISORY: EncodeXRow never consults Present. It infers
	// which keys to emit from the zero-ness of the fields, as it always has,
	// because every XRow built by hand - in a test, a writer, a filter that
	// rewrites rows - has Present == 0, and an encoder that honoured it would
	// emit an empty header for all of them. The asymmetry is deliberate; do
	// not "fix" it. To drop a key from an encoded row, zero its field.
	Present Present
}

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). It aliases the input. 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.

The row's Present records the header keys found on the wire; the TSN and IPROTO_FLAG_COMMIT inferred for a single-statement row set no bit. Its BodyRaw aliases b; see Aliasing in the package doc.

func (*XRow) Clone added in v0.2.0

func (r *XRow) Clone() *XRow

Clone returns a deep copy of r: its BodyRaw is a new slice. A nil receiver returns nil.

Cloning a row does not detach a body decoded from r.BodyRaw before: that body aliases the buffer r.BodyRaw did. Decode the body from the BodyRaw of the clone, or clone the body too.

func (*XRow) CloneInto added in v0.2.0

func (r *XRow) CloneInto(dst *XRow)

CloneInto deep-copies r into dst, overwriting every field, and copies r.BodyRaw into the array of dst.BodyRaw when it has the capacity. dst must own its slices; see Aliasing in the package doc. dst may be r.

func (*XRow) HasFlags added in v0.2.0

func (r *XRow) HasFlags() bool

HasFlags reports whether the decoded row carried IPROTO_FLAGS. It is false for a single-statement row with no other flag, whose IPROTO_FLAG_COMMIT the decoder infers.

func (*XRow) HasGroupID added in v0.2.0

func (r *XRow) HasGroupID() bool

HasGroupID reports whether the decoded row carried IPROTO_GROUP_ID.

func (*XRow) HasLSN added in v0.2.0

func (r *XRow) HasLSN() bool

HasLSN reports whether the decoded row carried IPROTO_LSN. A heartbeat of the replication stream carries none.

func (*XRow) HasReplicaID added in v0.2.0

func (r *XRow) HasReplicaID() bool

HasReplicaID reports whether the decoded row carried IPROTO_REPLICA_ID.

func (*XRow) HasSchemaVersion added in v0.2.0

func (r *XRow) HasSchemaVersion() bool

HasSchemaVersion reports whether the decoded row carried IPROTO_SCHEMA_VERSION.

func (*XRow) HasStreamID added in v0.2.0

func (r *XRow) HasStreamID() bool

HasStreamID reports whether the decoded row carried IPROTO_STREAM_ID.

func (*XRow) HasSync added in v0.2.0

func (r *XRow) HasSync() bool

HasSync reports whether the decoded row carried IPROTO_SYNC.

func (*XRow) HasTSN added in v0.2.0

func (r *XRow) HasTSN() bool

HasTSN reports whether the decoded row carried IPROTO_TSN, that is whether it belongs to a multi-statement transaction. It is false for a single-statement row, whose TSN the decoder infers from its LSN.

func (*XRow) HasTimestamp added in v0.2.0

func (r *XRow) HasTimestamp() bool

HasTimestamp reports whether the decoded row carried IPROTO_TIMESTAMP.

func (*XRow) HasType added in v0.2.0

func (r *XRow) HasType() bool

HasType reports whether the decoded row carried IPROTO_REQUEST_TYPE.

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