gowal

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2025 License: Apache-2.0 Imports: 17 Imported by: 3

README

tests Go Reference Go Report Card

GoWAL - Write-Ahead Logging in Go

GoWAL is a simple, efficient Write-Ahead Log (WAL) library written in Go. It allows you to store data in an append-only log structure, which is useful for applications that require crash recovery, transaction logging, or high-availability systems. GoWAL is optimized for performance with configurable segment rotation and in-memory indexing.

Installation

go get github.com/vadiminshakov/gowal

Usage

Initialization

To create a new WAL instance, specify the directory to store logs and a prefix for the log files:

import "github.com/vadiminshakov/gowal"

cfg := gowal.Config{
    Dir:    "./log",
    Prefix: "segment_",
    SegmentThreshold: 1000,
    MaxSegments:      100,
    IsInSyncDiskMode: false,
}

wal, err := gowal.NewWAL(cfg)
if err != nil {
    log.Fatal(err)
}
defer wal.Close()
Adding a log entry

You can append a new log entry by providing an index, a key, and a value:

err := wal.Write(1, "myKey", []byte("myValue"))
if err != nil {
    log.Fatal(err)
}

If the entry with the same index already exists, the function will return an error.

Retrieving a log entry

You can retrieve a log entry by its index:

key, value, err := wal.Get(1)
if err != nil {
    log.Println("Entry not found or error:", err)
} else {
    log.Printf("Key: %s, Value: %s", key, string(value))
}
Iterating over log entries

You can iterate over all log entries using the Iterator function:

for msg := range wal.Iterator() {
    log.Printf("Key: %s, Value: %s\n", msg.Key, string(msg.Value))
}
Closing the WAL

Always ensure that you close the WAL instance to properly flush and close the log files:

err := wal.Close()
if err != nil {
    log.Fatal(err)
}
Recover corrupted WAL

If the WAL is corrupted, you can recover it by calling the UnsafeRecover function:

removedFiles, err := gowal.UnsafeRecover("./log", "segment_")
if err != nil {
    log.Fatal(err)
}
log.Printf("Removed corrupted files: %v", removedFiles)
Configuration

The behavior of the WAL can be configured using several configuration options (Config parameter in the NewWAL function):

  • SegmentThreshold: Maximum number of log entries per segment before rotation occurs. Default is 1000.
  • MaxSegments: Maximum number of segments to keep before the oldest segments are deleted. Default is 5.
  • IsInSyncDiskMode: When set to true, every write is synced to disk, ensuring durability at the cost of performance. Default is false.

Architecture

GoWAL uses a segmented architecture with two-level indexing for efficient write and read operations:

Segments

Data is split into numbered files (segment_0, segment_1, etc.). Each record contains:

  • Index, Key, Value
  • CRC32 checksum for integrity verification
Two-Level Indexing
  • tmpIndex: In-memory index for the current active segment. Maps record index to its position in the segment.
  • index: Main in-memory index for all persisted (closed) segments. Provides fast lookups across historical data.
Write Flow
  1. Check if index already exists (prevents duplicates)
  2. Check if rotation is needed based on SegmentThreshold
  3. Calculate CRC32 checksum for the record
  4. Serialize record (with checksum) using MessagePack
  5. Write to current segment file
  6. Add entry to tmpIndex
Rotation & Segment Management

When tmpIndex size exceeds SegmentThreshold:

  1. Current segment is closed
  2. tmpIndex is merged into main index
  3. tmpIndex is cleared
  4. New segment is created

When MaxSegments limit is reached, the oldest segment is automatically deleted along with its index entries to manage disk space.

Read Operations

Lookups check both indexes:

  1. Check tmpIndex first (current segment, smaller and more likely to contain recent data)
  2. If not found, check main index (historical segments)
  3. Verify checksum before returning data
Contributing

Feel free to open issues or submit pull requests for improvements and bug fixes. We welcome contributions!

License

This project is licensed under the Apache License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrExists = errors.New("msg with such index already exists")

Functions

func UnsafeRecover added in v0.0.2

func UnsafeRecover(dir, segmentPrefix string) ([]string, error)

UnsafeRecover recovers the WAL from the given directory. It is unsafe because it removes all the segment and checksum files that are corrupted (checksums do not match). It returns the list of segment and checksum files that were removed.

Types

type Config

type Config struct {
	// Dir is the directory where the log files will be stored.
	Dir string

	// Prefix is the prefix for the segment files.
	Prefix string

	// SegmentThreshold is the number of records after which a new segment is created.
	SegmentThreshold int

	// MaxSegments is the maximum number of segments allowed before the oldest segment is deleted.
	MaxSegments int

	// IsInSyncDiskMode indicates whether the log should be synced to disk after each write.
	IsInSyncDiskMode bool
}

Config represents the configuration for the WAL (Write-Ahead Log).

type Wal

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

Wal is a write-ahead log that stores key-value pairs.

Wal is append-only log, so we can't delete records from it, but log is divided into segments, which are rotated (oldest deleted) when segments number threshold is reached.

Index stored in memory and loaded from disk on Wal init.

func NewWAL

func NewWAL(config Config) (*Wal, error)

NewWAL creates a new WAL with the given configuration.

func (*Wal) Close

func (c *Wal) Close() error

Close closes log file.

func (*Wal) CurrentIndex

func (c *Wal) CurrentIndex() uint64

CurrentIndex returns current index of the log.

func (*Wal) Get

func (c *Wal) Get(index uint64) (string, []byte, error)

Get queries value at specific index in the log.

func (*Wal) Iterator

func (c *Wal) Iterator() iter.Seq[msg]

Iterator returns push-based iterator for the WAL messages. Messages are returned from the oldest to the newest.

Should be used like this:

for msg := range wal.Iterator() {
	...

func (*Wal) PullIterator added in v0.0.2

func (c *Wal) PullIterator() (next func() (msg, bool), stop func())

PullIterator returns pull-based iterator for the WAL messages. Messages are returned from the oldest to the newest.

Should be used like this:

next, stop := wal.PullIterator()
defer stop()
...

func (*Wal) Write

func (c *Wal) Write(index uint64, key string, value []byte) error

Write writes key-value pair to the log.

func (*Wal) WriteTombstone added in v0.0.4

func (c *Wal) WriteTombstone(index uint64) error

WriteTombstone writes a tombstone record for the given index. If no record exists for the index, returns nil (no-op). If a record exists, overwrites it with a tombstone.

Jump to

Keyboard shortcuts

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