rotate

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: 8 Imported by: 0

Documentation

Overview

Package rotate implements a directory-aware writer that produces a chain of journal files inside a single directory. Rotation is size-based: when the current file's running byte estimate crosses MaxFileSize, the writer closes the current file (Writer.Close atomically renames .inprogress → final name) and opens a new one whose Meta.PrevVClock equals the just-closed file's Meta.VClock.

Rotation happens *between* logical transactions, never mid-tx. All rows of a logical tx live in one file — Tarantool itself flushes the journal queue at wal_opt_rotate (src/box/wal.c) and we mirror that here by only checking the size threshold on entry to WriteTx.

The writer holds all state on its receiver; there is no package-level mutable state. Filesystem concerns (os.MkdirAll, file naming) live in this layer, not in `format`.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrEmptyRows is returned by WriteTx when given a zero-length rows slice.
	ErrEmptyRows = errors.New("rotate: WriteTx: empty rows")
	// ErrNoActiveWriter is returned when a rotation is attempted with no
	// active inner writer.
	ErrNoActiveWriter = errors.New("rotate: rotate without an active writer")
)

Sentinel errors returned by RotatingWriter methods.

View Source
var ErrClosed = errors.New("rotate: closed")

ErrClosed is returned by any RotatingWriter method called after Close.

Functions

This section is empty.

Types

type Option

type Option func(*rotateCfg)

Option configures a RotatingWriter at construction time. Options are applied in order; the RotatingWriter is otherwise immutable.

func MaxFileSize

func MaxFileSize(n int64) Option

MaxFileSize sets the per-file rotation threshold in bytes. Default is 64 MiB. The threshold is checked between transactions only — a single large transaction may exceed it.

Values ≤ 0 are accepted (no validation at option time) — they will effectively rotate on every WriteTx since the running estimate is non-negative.

func WriterOptions

func WriterOptions(opts ...writer.Option) Option

WriterOptions appends WriterOptions to propagate to every inner Writer constructed by the rotating writer. Useful for forwarding NoCompression, Sync, Version, etc. Composable across multiple calls.

type RotatingWriter

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

RotatingWriter wraps writer.Writer with directory awareness: it produces a chain of files in one directory, threading PrevVClock/VClock between them so the chain stays consistent. Rotation is size-based and happens between logical transactions only.

The writer is safe for concurrent WriteTx callers — mu serialises every operation that touches the in-flight state.

Example

ExampleRotatingWriter writes a chain of xlog files into one directory, rotating between transactions. With a tiny MaxFileSize every transaction after the first lands in a fresh file, so the three writes produce three files. Each file's Meta.PrevVClock links back to the previous file's Meta.VClock, keeping the chain consistent — which OpenDir then verifies.

package main

import (
	"fmt"
	"os"

	"github.com/google/uuid"
	"github.com/tarantool/go-iproto"
	"github.com/vmihailenco/msgpack/v5"

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

var exampleInstance = uuid.MustParse("11111111-2222-3333-4444-555555555555")

// exampleBody msgpack-encodes a one-element tuple so each row carries a valid
// body.
func exampleBody(v int) []byte {
	b, err := msgpack.Marshal([]int{v})
	if err != nil {
		panic(err)
	}

	return b
}

// ExampleRotatingWriter writes a chain of xlog files into one directory,
// rotating between transactions. With a tiny MaxFileSize every transaction
// after the first lands in a fresh file, so the three writes produce three
// files. Each file's Meta.PrevVClock links back to the previous file's
// Meta.VClock, keeping the chain consistent — which OpenDir then verifies.
func main() {
	baseDir, err := os.MkdirTemp("", "go-xlog-rotate-example")
	if err != nil {
		panic(err)
	}

	defer func() { _ = os.RemoveAll(baseDir) }()

	// MaxFileSize(1) forces a rotation before every transaction but the first.
	rw, err := rotate.New(baseDir, format.FiletypeXLOG, exampleInstance,
		format.VClock{}, rotate.MaxFileSize(1))
	if err != nil {
		panic(err)
	}

	for lsn := int64(1); lsn <= 3; lsn++ {
		if err := rw.WriteTx([]format.XRow{
			{Type: iproto.IPROTO_INSERT, ReplicaID: 1, LSN: lsn, BodyRaw: exampleBody(int(lsn))},
		}); err != nil {
			panic(err)
		}
	}

	if err := rw.Close(); err != nil {
		panic(err)
	}

	// Index the resulting directory to inspect the chain.
	d, err := dir.OpenDir(baseDir, format.FiletypeXLOG)
	if err != nil {
		panic(err)
	}

	for _, f := range d.Files() {
		fmt.Printf("signature=%d vclock=%s prev=%s\n", f.Signature, f.VClock, f.PrevVClock)
	}

}
Output:
signature=0 vclock={} prev={}
signature=1 vclock={1: 1} prev={}
signature=2 vclock={1: 2} prev={1: 1}

func New

func New(
	dirPath string,
	filetype format.Filetype,
	instance uuid.UUID,
	startVClock format.VClock,
	opts ...Option,
) (*RotatingWriter, error)

New creates a new RotatingWriter rooted at dirPath. The directory is created (with MkdirAll, mode 0750) if it does not already exist.

StartVClock seeds both the running vclock and the first file's Meta.VClock. The first file's Meta.PrevVClock is empty (this is the head of the chain).

Instance is the InstanceUUID stamped into every file's meta.

func (*RotatingWriter) Close

func (rw *RotatingWriter) Close() error

Close flushes and finalises the current writer (atomic-renames its .inprogress to its final name via writer.Writer.Close) and marks the RotatingWriter closed. Subsequent calls return ErrClosed.

func (*RotatingWriter) WriteTx

func (rw *RotatingWriter) WriteTx(rows []format.XRow) error

WriteTx writes rows as a single logical transaction. If the running byte estimate is at or above MaxFileSize on entry, the writer rotates first (so the new tx lands in a fresh file). The tx is never split: once we begin writing rows, we finish them in the current file.

Jump to

Keyboard shortcuts

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