Skip to content

A deterministic event recording and replay framework for Go. This library provides a powerful way to record ordered events (inputs, state transitions) and replay them deterministically. It supports branching timelines and checksums for validation, making it particularly useful for debugging distributed systems and simulations.

License

Notifications You must be signed in to change notification settings

BaseMax/go-event-replay

Repository files navigation

go-event-replay

A deterministic event recording and replay framework for Go. This library provides a powerful way to record ordered events (inputs, state transitions) and replay them deterministically. It supports branching timelines and checksums for validation, making it particularly useful for debugging distributed systems and simulations.

Features

  • Event Recording: Capture events with type, payload, timestamp, and automatic checksum generation
  • Deterministic Replay: Replay recorded events in exact order with validation
  • Branching Timelines: Create alternative timelines from any point in the event history
  • Checksum Validation: Automatic integrity checking for events and timelines
  • File-based Storage: Simple JSON-based persistence for easy inspection and portability
  • CLI Tool: Command-line interface for recording and replaying events
  • Thread-safe: Concurrent access support with proper synchronization

Installation

go get github.com/BaseMax/go-event-replay

To install the CLI tool:

go install github.com/BaseMax/go-event-replay/cmd/eventreplay@latest

Or build from source:

git clone https://github.com/BaseMax/go-event-replay
cd go-event-replay
go build -o bin/eventreplay ./cmd/eventreplay

Library Usage

Recording Events

package main

import (
    "fmt"
    "github.com/BaseMax/go-event-replay"
)

func main() {
    // Create a new recorder
    recorder := eventreplay.NewRecorder("my-simulation")
    
    // Record events
    recorder.Record("user.login", map[string]interface{}{
        "username": "alice",
        "ip": "192.168.1.1",
    })
    
    recorder.Record("order.created", map[string]interface{}{
        "order_id": 123,
        "total": 99.99,
    })
    
    // Save timeline to disk
    storage, _ := eventreplay.NewStorage(".eventreplay")
    storage.SaveTimeline(recorder.GetTimeline())
    
    fmt.Printf("Recorded %d events\n", recorder.EventCount())
}

Replaying Events

package main

import (
    "fmt"
    "github.com/BaseMax/go-event-replay"
)

func main() {
    storage, _ := eventreplay.NewStorage(".eventreplay")
    timeline, _ := storage.LoadTimeline("timeline-id")
    
    replayer := eventreplay.NewReplayer(timeline)
    
    // Register handlers for specific event types
    replayer.RegisterHandler("user.login", func(event *eventreplay.Event) error {
        fmt.Printf("User logged in: %v\n", event.Payload["username"])
        return nil
    })
    
    // Replay all events
    count, err := replayer.ReplayAll()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Replayed %d events\n", count)
    }
}

Creating Branches

package main

import (
    "github.com/BaseMax/go-event-replay"
)

func main() {
    storage, _ := eventreplay.NewStorage(".eventreplay")
    timeline, _ := storage.LoadTimeline("timeline-id")
    
    // Create a branch from event index 5
    branch, _ := timeline.Branch("alternate-scenario", 5)
    
    // Add different events to the branch
    branch.AddEvent(eventreplay.NewEvent("order.cancelled", map[string]interface{}{
        "order_id": 123,
        "reason": "customer_request",
    }))
    
    // Save the branch
    storage.SaveTimeline(branch)
}

Validation

package main

import (
    "fmt"
    "github.com/BaseMax/go-event-replay"
)

func main() {
    storage, _ := eventreplay.NewStorage(".eventreplay")
    timeline, _ := storage.LoadTimeline("timeline-id")
    
    valid, errors := timeline.Validate()
    if valid {
        fmt.Println("Timeline is valid")
    } else {
        fmt.Println("Validation errors:")
        for _, err := range errors {
            fmt.Printf("  - %v\n", err)
        }
    }
}

CLI Usage

Recording Events

# Record a new event (creates timeline if it doesn't exist)
eventreplay record my-timeline user.login '{"username":"alice"}'

# Record additional events to the same timeline
eventreplay record my-timeline order.created '{"order_id":123,"total":99.99}'
eventreplay record my-timeline payment.processed '{"order_id":123,"amount":99.99}'

Listing Timelines

eventreplay list

Output:

Available Timelines
===================

Name           ID                   Events  Created
----           --                   ------  -------
my-timeline    1766270220370310...  3       2025-12-20 22:37

Viewing Timeline Info

eventreplay info <timeline-id>

Output:

Timeline Information
====================

ID: 1766270220370310633-fq8wayn0
Name: my-timeline
Created: 2025-12-20T22:37:00Z
Total Events: 3
Checksum: bd0279991db21cb7ad629ffb6645e5d3580046a758909bd4ba4dab16f03f2ace

Events:
  Index  Type               Timestamp  ID
  -----  ----               ---------  --
  0      user.login         22:37:00   1766270220370381...
  1      order.created      22:37:07   1766270227845326...
  2      payment.processed  22:37:07   1766270227847210...

Replaying Events

eventreplay replay <timeline-id>

Output:

Replaying timeline: my-timeline
Total events: 3
---

[Event 0]
ID: 1766270220370381385-4caymk8w
Type: user.login
Timestamp: 2025-12-20T22:37:00Z
Payload: {
  "username": "alice"
}

[Event 1]
ID: 1766270227845326752-0o2q4shv
Type: order.created
Timestamp: 2025-12-20T22:37:07Z
Payload: {
  "order_id": 123,
  "total": 99.99
}
...

Creating Branches

# Create a branch from event index 2
eventreplay branch <timeline-id> 2 alternate-flow

# Add events to the branch
eventreplay record alternate-flow order.cancelled '{"order_id":123,"reason":"test"}'

Validating Timelines

eventreplay validate <timeline-id>

Output:

Timeline: my-timeline
Timeline ID: 1766270220370310633-fq8wayn0
Total Events: 3

✓ Timeline is valid
✓ All events have valid checksums
✓ Timeline checksum is valid

Use Cases

Debugging Distributed Systems

Record all events (messages, state changes) in a distributed system and replay them to reproduce bugs:

// Record every message
recorder.Record("message.sent", map[string]interface{}{
    "from": "service-a",
    "to": "service-b",
    "payload": messageData,
})

// Later, replay to debug
replayer.RegisterHandler("message.sent", func(event *Event) error {
    // Re-execute the message handling logic
    return processMessage(event.Payload)
})

Simulation Testing

Create branches to test different scenarios without re-running the entire simulation:

// Run simulation to checkpoint
// Branch from checkpoint
branch, _ := timeline.Branch("scenario-2", checkpointIndex)

// Test alternative decisions
branch.AddEvent(NewEvent("decision.alternate", decisionData))

Audit Trails

Maintain tamper-evident audit logs with checksum validation:

// Record actions
recorder.Record("user.action", actionData)

// Validate integrity
valid, errors := timeline.Validate()
if !valid {
    // Handle tampering
}

Architecture

The library consists of several key components:

  • Event: Individual events with ID, timestamp, type, payload, and checksum
  • Timeline: Ordered sequence of events with branching support
  • Recorder: Captures and stores events
  • Replayer: Plays back events with handlers
  • Storage: Persists timelines to disk in JSON format

Testing

Run the test suite:

go test -v ./...

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Author

BaseMax

About

A deterministic event recording and replay framework for Go. This library provides a powerful way to record ordered events (inputs, state transitions) and replay them deterministically. It supports branching timelines and checksums for validation, making it particularly useful for debugging distributed systems and simulations.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages