wal

package
v0.1.0-alpha Latest Latest
Warning

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

Go to latest
Published: Dec 31, 2024 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package wal implements a Write-Ahead Log (WAL) for durable and atomic record storage.

A Write-Ahead Log is a reliability mechanism that ensures data persistence by writing records sequentially to disk before they are considered committed. The WAL is organized into segments, where each segment contains a sorted collection of records.

The WAL implementation provides the following guarantees:

  • Durability: Records are persisted to disk before operations are considered complete
  • Atomicity: Records within a segment are written atomically
  • Order preservation: Records are maintained in sorted order within segments

Basic usage:

file, err := os.Create("mywal.db")
if err != nil {
    log.Fatal(err)
}

// Create a new WAL writer with segments of up to 1000 records
writer, err := wal.NewWriter(file, 1000)
if err != nil {
    log.Fatal(err)
}

// Write records
err = writer.Write(myRecord)
if err != nil {
    log.Fatal(err)
}

// Close the writer
err = writer.Close()
if err != nil {
    log.Fatal(err)
}

// Read records back
file, err = os.Open("mywal.db")
if err != nil {
    log.Fatal(err)
}

reader := wal.NewReader(file)
for record := range reader.All() {
    // Process record
}

File Format: Each segment in the WAL consists of:

  • Segment header: Total size of the segment (8 bytes)
  • Records: Series of variable-length records

The WAL automatically rotates segments when they reach the configured maximum number of records, ensuring efficient storage and retrieval.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidMaxRecords = errors.New("maxRecords must be greater than 0")
	ErrWALClosed         = errors.New("WAL is closed")
)

Functions

This section is empty.

Types

type Reader

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

func NewReader

func NewReader(r io.ReaderAt) *Reader

func (*Reader) All

func (r *Reader) All() iter.Seq[partition.Record]

type Writer

type Writer struct {
	// contains filtered or unexported fields
}
Example

ExampleWriter demonstrates creating a WAL, writing records, and reading them back.

package main

import (
	"bytes"
	"fmt"
	"time"

	"github.com/davidvella/xp/partition"
	"github.com/davidvella/xp/wal"
)

type writeCloser struct {
	*bytes.Buffer
}

func (w writeCloser) Close() error {
	return nil
}

func main() {
	buffer := bytes.NewBuffer([]byte{})
	buf := writeCloser{buffer}
	// Initialize the WAL writer with segments of up to 1000 records
	writer, err := wal.NewWriter(buf, 1000)
	if err != nil {
		fmt.Printf("Error opening writer: %v\n", err)
		return
	}

	// Create and write some records
	records := []partition.Record{
		&partition.RecordImpl{
			ID:           "key1",
			PartitionKey: "partition1",
			Timestamp:    time.Now(),
			Data:         []byte("value1"),
		},
		&partition.RecordImpl{
			ID:           "key2",
			PartitionKey: "partition1",
			Timestamp:    time.Now(),
			Data:         []byte("value2"),
		},
	}

	for _, record := range records {
		if err := writer.Write(record); err != nil {
			fmt.Printf("Error writing record: %v\n", err)
			return
		}
	}

	// Close the writer to flush any remaining records
	if err := writer.Close(); err != nil {
		fmt.Printf("Error closing writer: %v\n", err)
		return
	}

	// Initialize the WAL reader
	readerAt := bytes.NewReader(buf.Bytes())
	reader := wal.NewReader(readerAt)

	// Read all records back
	for record := range reader.All() {
		fmt.Printf("Read record: %s = %s\n", record.GetID(), string(record.GetData()))
	}

}
Output:
Read record: key1 = value1
Read record: key2 = value2
Example (Segments)

ExampleWriter_segments demonstrates how the WAL handles segment rotation.

package main

import (
	"bytes"
	"fmt"
	"time"

	"github.com/davidvella/xp/partition"
	"github.com/davidvella/xp/wal"
)

type writeCloser struct {
	*bytes.Buffer
}

func (w writeCloser) Close() error {
	return nil
}

func main() {
	buffer := bytes.NewBuffer([]byte{})
	buf := writeCloser{buffer}
	// Initialize the WAL writer with small segments (2 records per segment)
	writer, err := wal.NewWriter(&buf, 2)
	if err != nil {
		fmt.Printf("Error opening writer: %v\n", err)
		return
	}

	// Write three records to trigger segment rotation
	records := []partition.Record{
		&partition.RecordImpl{
			ID:           "1",
			PartitionKey: "partition1",
			Timestamp:    time.Now(),
			Data:         []byte("first"),
		},
		&partition.RecordImpl{
			ID:           "2",
			PartitionKey: "partition1",
			Timestamp:    time.Now(),
			Data:         []byte("second"),
		},
		&partition.RecordImpl{
			ID:           "3",
			PartitionKey: "partition1",
			Timestamp:    time.Now(),
			Data:         []byte("third"),
		},
	}

	for _, record := range records {
		if err := writer.Write(record); err != nil {
			fmt.Printf("Error writing record: %v\n", err)
			return
		}
	}

	if err := writer.Close(); err != nil {
		fmt.Printf("Error closing writer: %v\n", err)
		return
	}

	// Read all records back
	readerAt := bytes.NewReader(buf.Bytes())
	reader := wal.NewReader(readerAt)
	for record := range reader.All() {
		fmt.Printf("Read record: %s = %s\n", record.GetID(), string(record.GetData()))
	}

}
Output:
Read record: 1 = first
Read record: 2 = second
Read record: 3 = third

func NewWriter

func NewWriter(wc io.WriteCloser, maxRecords int) (*Writer, error)

func (*Writer) Close

func (w *Writer) Close() error

func (*Writer) Write

func (w *Writer) Write(record partition.Record) error

Jump to

Keyboard shortcuts

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