Skip to content

Distributed consensus using S3 conditional operations

License

Notifications You must be signed in to change notification settings

thevilledev/gonsensus

Repository files navigation

gonsensus

Go Reference test Go Report Card

Gonsensus is a distributed consensus implementation, written in Go, using S3 conditional operations for leader election. It provides a simple, reliable way to coordinate distributed systems using any S3 compatible object storage as the backing store. Conditional writes are required. See RFC 7232 for more details about conditional requests in general.

See example_test.go for a fully fledged example use case for simple leader election.

See integration_test.go for an example use case for leader election with quorum.

Features

  • S3-based leader election with atomic operations
  • Automatic failover and clean shutdown
  • Configurable TTL, polling, and quorum settings
  • Leader election and demotion callbacks
  • Observer pattern with heartbeat monitoring
  • Thread-safe operations with mutex protection
  • Version control for consistency
  • Minimal dependencies (AWS SDK only)

How it works

Gonsensus implements distributed consensus through S3:

Leader Election

  • Nodes compete to create a lock file in S3 using atomic operations
  • Winner becomes leader and maintains lock through periodic renewals
  • Failed renewals trigger automatic failover

Optional Quorum Management

  • When enabled, leader tracks observer nodes through heartbeats
  • Configurable quorum size ensures consensus
  • Can operate in simple leader-election mode without quorum

Fault Tolerance

  • Automatic failover on leader failure
  • Version control prevents split-brain scenarios
  • Grace periods handle clean transitions

Installation

go get github.com/thevilledev/gonsensus

Getting started

package main

import (
    "context"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/s3"
    "github.com/thevilledev/gonsensus"
)

func main() {
    // Initialize AWS S3 client
    cfg, _ := config.LoadDefaultConfig(context.Background())
    s3Client := s3.NewFromConfig(cfg)

    // Create consensus manager
    manager, _ := gonsensus.NewManager(s3Client, "my-bucket", gonsensus.Config{
        NodeID:     "node-1",
        LockPrefix: "consensus/",
    })

    // Set leader callbacks
    manager.SetCallbacks(
        func(ctx context.Context) error { /* on elected */ return nil },
        func(ctx context.Context) { /* on demoted */ },
    )

    // Run manager
    manager.Run(context.Background())
}

Authentication

This project relies on the AWS S3 client default authentication mechanism. See AWS documentation for full reference on configuration and credentials presedence.

In short, if you want to use environment variables you may do it like this:

export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export AWS_ENDPOINT_URL_S3=
export AWS_ENDPOINT_URL_IAM=
export AWS_ENDPOINT_URL_STS=
export AWS_REGION=

# run acceptance tests
make test-acc

If your configuration is in a local AWS profile in ~/.aws/config you may do it like this instead:

export AWS_PROFILE=xyz

# run acceptance tests
make test-acc

Note that acceptance tests require a bucket with the name of "my-bucket-1".

License

This project is licensed under the MIT License - see the LICENSE file for details.