Skip to content

Migrate from openark/raft fork to upstream hashicorp/raft v1.7#62

Merged
renecannao merged 3 commits intomasterfrom
issue61-raft-migration
Mar 24, 2026
Merged

Migrate from openark/raft fork to upstream hashicorp/raft v1.7#62
renecannao merged 3 commits intomasterfrom
issue61-raft-migration

Conversation

@renecannao
Copy link
Copy Markdown

@renecannao renecannao commented Mar 24, 2026

Summary

  • Removes the replace directive that redirected github.com/hashicorp/raft to the openark/raft fork (pinned at a September 2017 commit), switching to upstream hashicorp/raft v1.7.3
  • Migrates all raft API calls to their upstream equivalents: PeerStore to BootstrapCluster/GetConfiguration, AddPeer/RemovePeer to AddVoter/RemoveServer, Yield/StepDown to LeadershipTransfer, and updates FileSnapshotStore.Create() to the new signature
  • Eliminates nine years of accumulated security and maintenance risk from running unmaintained consensus code

Changes by file

go.mod / go.sum / vendor/

  • Remove openark/raft replace directive
  • Pull in hashicorp/raft v1.7.3 and its new dependency hashicorp/go-msgpack/v2

go/raft/store.go

  • Remove peerStore field from Store struct
  • Replace raft.StaticPeers/SetPeers with raft.BootstrapCluster() using raft.Configuration
  • Replace raft.AddUniquePeer() with local helper
  • Remove EnableSingleNode/DisableBootstrapAfterElect config; single-node handled via bootstrap
  • Set config.LocalID (required by upstream)
  • Update NewRaft() from 7-arg to 6-arg constructor
  • Replace AddPeer(addr) with AddVoter(ServerID, ServerAddress, 0, 0)
  • Replace RemovePeer(addr) with RemoveServer(ServerID, 0, 0)

go/raft/raft.go

  • Cast raft.Leader() return from ServerAddress to string
  • Replace GetPeers() via PeerStore.Peers() with GetConfiguration() extracting server addresses
  • Replace StepDown() and Yield() with LeadershipTransfer()

go/raft/file_snapshot.go

  • Update FileSnapshotStore.Create() signature: (version, index, term, configuration, configurationIndex, trans) replacing old (index, term, peers)
  • Update SnapshotMeta initialization to use Configuration/ConfigurationIndex fields instead of Peers

Test plan

  • go build -o /dev/null ./go/cmd/orchestrator — full binary compiles cleanly
  • go test ./go/... -vet=off -count=1 — all 12 test packages pass
  • Integration test with multi-node raft cluster (manual, requires Docker environment)
  • Verify snapshot creation/restoration with new format
  • Test single-node bootstrap on fresh data directory
  • Test multi-node bootstrap and peer join/remove operations

Closes #61

Summary by CodeRabbit

Release Notes

  • Dependencies

    • Updated go-msgpack dependency from v0.5.5 to v2.1.2 for improved serialization handling.
  • Improvements

    • Enhanced snapshot creation and management with improved version and configuration handling.
    • Improved leadership transfer mechanisms in cluster operations.
    • Updated cluster membership and peer management logic for better operational stability.

Remove the replace directive that redirected github.com/hashicorp/raft
to the openark/raft fork (pinned at a 2017 commit). This switches to
upstream hashicorp/raft v1.7.3, which includes nine years of security
fixes, bug fixes, and improvements including the pre-vote protocol,
improved snapshot handling, and numerous race condition fixes.

Closes #61 (dependency update portion)
Migrate all orchestrator raft code from the removed openark/raft fork
APIs to their upstream hashicorp/raft v1.7 equivalents:

- Remove PeerStore field; replace with raft.BootstrapCluster() using
  raft.Configuration{Servers: [...]} for initial cluster setup
- Replace raft.AddUniquePeer() with local addUniquePeer() helper
- Remove EnableSingleNode/DisableBootstrapAfterElect config fields;
  single-node mode is now handled by bootstrapping with one server
- Update NewRaft() from 7-arg to 6-arg (no PeerStore parameter)
- Set config.LocalID = raft.ServerID(advertise) as required by upstream
- Replace AddPeer(addr) with AddVoter(ServerID, ServerAddress, 0, 0)
- Replace RemovePeer(addr) with RemoveServer(ServerID, 0, 0)
- Replace GetPeers() via PeerStore with GetConfiguration() to extract
  server addresses from the raft configuration
- Replace Yield() and StepDown() with LeadershipTransfer()
- Cast raft.Leader() return from ServerAddress to string
- Update FileSnapshotStore.Create() signature to match upstream:
  (version, index, term, configuration, configurationIndex, trans)
  replacing the old (index, term, peers []byte) signature

Closes #61
Copilot AI review requested due to automatic review settings March 24, 2026 15:18
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the project from the long-pinned openark/raft fork to upstream github.com/hashicorp/raft v1.7.x, updating APIs and bootstrapping/snapshot behavior to match the upstream Raft interfaces.

Changes:

  • Switch dependency wiring to upstream hashicorp/raft and update msgpack dependency to /v2.
  • Replace deprecated peer-store APIs with BootstrapCluster, GetConfiguration, and voter/server management APIs.
  • Update snapshot store Create() signature and snapshot metadata to use configuration fields.

Reviewed changes

Copilot reviewed 4 out of 82 changed files in this pull request and generated 5 comments.

File Description
go/raft/store.go Removes PeerStore usage; adds cluster bootstrapping via BootstrapCluster; updates join/remove to AddVoter/RemoveServer.
go/raft/raft.go Updates leader/peer discovery and leadership yield/stepdown to upstream APIs.
go/raft/file_snapshot.go Updates snapshot creation signature and metadata to the upstream raft snapshot format.
go.mod Removes raft fork replace and updates dependencies for upstream raft.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread go/raft/store.go
Comment on lines +125 to +131
log.Infof("bootstrapping cluster with peers: %+v", peers)
for _, peer := range peers {
servers = append(servers, raft.Server{
ID: raft.ServerID(peer),
Address: raft.ServerAddress(peer),
})
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the multi-node bootstrap path, the local server is only included if peerNodes happens to contain store.raftAdvertise. If the local address is not in peers, BootstrapCluster will create an initial configuration that excludes this node, which can prevent it from participating correctly (and can break join/leadership behavior). Ensure the bootstrapped configuration.Servers always includes the local server (and avoid duplicates if it’s already present).

Suggested change
log.Infof("bootstrapping cluster with peers: %+v", peers)
for _, peer := range peers {
servers = append(servers, raft.Server{
ID: raft.ServerID(peer),
Address: raft.ServerAddress(peer),
})
}
log.Infof("bootstrapping cluster with peers: %+v", peers)
localIncluded := false
for _, peer := range peers {
if peer == store.raftAdvertise {
localIncluded = true
}
servers = append(servers, raft.Server{
ID: raft.ServerID(peer),
Address: raft.ServerAddress(peer),
})
}
if !localIncluded {
// Ensure the local server is always part of the initial configuration
servers = append(servers, raft.Server{
ID: raft.ServerID(store.raftAdvertise),
Address: raft.ServerAddress(store.raftAdvertise),
})
}

Copilot uses AI. Check for mistakes.
Comment thread go/raft/store.go
config.SnapshotThreshold = 1
config.SnapshotInterval = snapshotInterval
config.ShutdownOnRemove = false
config.LocalID = raft.ServerID(store.raftAdvertise)
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using store.raftAdvertise as LocalID couples server identity to its network address. If the advertised address changes across restarts (IP change, port change, DNS change, etc.), raft will treat the node as a different server ID, which can complicate recovery and membership operations. Prefer a stable node identity (e.g., configured node name/UUID) for LocalID, while keeping ServerAddress as the address.

Suggested change
config.LocalID = raft.ServerID(store.raftAdvertise)
config.LocalID = raft.ServerID(store.raftDir)

Copilot uses AI. Check for mistakes.
Comment thread go/raft/store.go Outdated
log.Infof("received join request for remote node %s", addr)

f := store.raft.AddPeer(addr)
f := store.raft.AddVoter(raft.ServerID(addr), raft.ServerAddress(addr), 0, 0)
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing 0 as the timeout to AddVoter/RemoveServer can result in the call waiting indefinitely under some failure modes (unreachable peer, stalled leadership, network partition), which is risky for request handlers and operations. Consider supplying a bounded timeout (and optionally a prevIndex when appropriate) so join/remove requests fail predictably.

Copilot uses AI. Check for mistakes.
Comment thread go/raft/store.go Outdated
log.Infof("received remove request for remote node %s", addr)

f := store.raft.RemovePeer(addr)
f := store.raft.RemoveServer(raft.ServerID(addr), 0, 0)
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing 0 as the timeout to AddVoter/RemoveServer can result in the call waiting indefinitely under some failure modes (unreachable peer, stalled leadership, network partition), which is risky for request handlers and operations. Consider supplying a bounded timeout (and optionally a prevIndex when appropriate) so join/remove requests fail predictably.

Copilot uses AI. Check for mistakes.
Comment thread go/raft/raft.go Outdated
return store.peerStore.Peers()
future := store.raft.GetConfiguration()
if err := future.Error(); err != nil {
return nil, err
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetPeers() returns []string{} on ErrRaftNotRunning, but returns nil on GetConfiguration() errors. This inconsistency can surprise callers that expect a non-nil slice even when err != nil (especially since the function already establishes an explicit empty-slice convention). Consider returning []string{} consistently on error paths, or consistently returning nil slices on all error paths (including ErrRaftNotRunning).

Suggested change
return nil, err
return []string{}, err

Copilot uses AI. Check for mistakes.
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the project's core consensus mechanism by transitioning from a long-outdated fork to the actively maintained upstream HashiCorp Raft library. This change not only modernizes the codebase by adopting current API standards but also addresses critical security and maintenance concerns inherent in using unmaintained dependencies. The update ensures the project benefits from ongoing improvements and bug fixes from the official Raft project.

Highlights

  • Raft Library Migration: The project's Raft implementation has been migrated from an outdated openark/raft fork to the official hashicorp/raft library, specifically version 1.7.3.
  • API Modernization: All existing Raft API calls have been updated to their upstream equivalents, including changes from PeerStore to BootstrapCluster/GetConfiguration, AddPeer/RemovePeer to AddVoter/RemoveServer, and Yield/StepDown to LeadershipTransfer.
  • Dependency Update: The go.mod and go.sum files have been updated to remove the openark/raft replace directive and incorporate hashicorp/raft v1.7.3, along with its new dependency hashicorp/go-msgpack/v2.
  • Security and Maintainability Improvement: This migration eliminates years of accumulated security and maintenance risk associated with running an unmaintained consensus code fork.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist
Copy link
Copy Markdown

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@renecannao renecannao merged commit e7d5f0e into master Mar 24, 2026
3 of 6 checks passed
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2246b200-348f-48c6-8616-9460810f06c8

📥 Commits

Reviewing files that changed from the base of the PR and between c38a16b and ede4fb4.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (81)
  • go.mod
  • go/raft/file_snapshot.go
  • go/raft/raft.go
  • go/raft/store.go
  • vendor/github.com/hashicorp/go-msgpack/LICENSE
  • vendor/github.com/hashicorp/go-msgpack/codec/0doc.go
  • vendor/github.com/hashicorp/go-msgpack/codec/README.md
  • vendor/github.com/hashicorp/go-msgpack/codec/binc.go
  • vendor/github.com/hashicorp/go-msgpack/codec/decode.go
  • vendor/github.com/hashicorp/go-msgpack/codec/encode.go
  • vendor/github.com/hashicorp/go-msgpack/codec/helper.go
  • vendor/github.com/hashicorp/go-msgpack/codec/helper_internal.go
  • vendor/github.com/hashicorp/go-msgpack/codec/msgpack.go
  • vendor/github.com/hashicorp/go-msgpack/codec/rpc.go
  • vendor/github.com/hashicorp/go-msgpack/codec/simple.go
  • vendor/github.com/hashicorp/go-msgpack/codec/time.go
  • vendor/github.com/hashicorp/go-msgpack/v2/LICENSE
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/build.sh
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/codecgen.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/decode.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/doc.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/encode.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/fast-path.not.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen-dec-array.go.tmpl
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen-dec-map.go.tmpl
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen-enc-chan.go.tmpl
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen-helper.generated.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen-helper.go.tmpl
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen-internal.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen.generated.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/gen.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/helper.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/helper_internal.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/json.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/mammoth-test.go.tmpl
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/mammoth2-test.go.tmpl
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/msgpack.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/rpc.go
  • vendor/github.com/hashicorp/go-msgpack/v2/codec/test.py
  • vendor/github.com/hashicorp/raft/.gitignore
  • vendor/github.com/hashicorp/raft/.gitmodules
  • vendor/github.com/hashicorp/raft/.golangci-lint.yml
  • vendor/github.com/hashicorp/raft/.travis.yml
  • vendor/github.com/hashicorp/raft/CHANGELOG.md
  • vendor/github.com/hashicorp/raft/LICENSE
  • vendor/github.com/hashicorp/raft/Makefile
  • vendor/github.com/hashicorp/raft/README.md
  • vendor/github.com/hashicorp/raft/api.go
  • vendor/github.com/hashicorp/raft/commands.go
  • vendor/github.com/hashicorp/raft/commitment.go
  • vendor/github.com/hashicorp/raft/config.go
  • vendor/github.com/hashicorp/raft/configuration.go
  • vendor/github.com/hashicorp/raft/discard_snapshot.go
  • vendor/github.com/hashicorp/raft/file_snapshot.go
  • vendor/github.com/hashicorp/raft/fsm.go
  • vendor/github.com/hashicorp/raft/future.go
  • vendor/github.com/hashicorp/raft/inflight.go
  • vendor/github.com/hashicorp/raft/inmem_snapshot.go
  • vendor/github.com/hashicorp/raft/inmem_store.go
  • vendor/github.com/hashicorp/raft/inmem_transport.go
  • vendor/github.com/hashicorp/raft/log.go
  • vendor/github.com/hashicorp/raft/log_cache.go
  • vendor/github.com/hashicorp/raft/membership.md
  • vendor/github.com/hashicorp/raft/net_transport.go
  • vendor/github.com/hashicorp/raft/observer.go
  • vendor/github.com/hashicorp/raft/peer.go
  • vendor/github.com/hashicorp/raft/peersjson.go
  • vendor/github.com/hashicorp/raft/progress.go
  • vendor/github.com/hashicorp/raft/raft.go
  • vendor/github.com/hashicorp/raft/replication.go
  • vendor/github.com/hashicorp/raft/saturation.go
  • vendor/github.com/hashicorp/raft/snapshot.go
  • vendor/github.com/hashicorp/raft/stable.go
  • vendor/github.com/hashicorp/raft/state.go
  • vendor/github.com/hashicorp/raft/tag.sh
  • vendor/github.com/hashicorp/raft/tcp_transport.go
  • vendor/github.com/hashicorp/raft/testing.go
  • vendor/github.com/hashicorp/raft/testing_batch.go
  • vendor/github.com/hashicorp/raft/transport.go
  • vendor/github.com/hashicorp/raft/util.go
  • vendor/modules.txt

📝 Walkthrough

Walkthrough

This pull request migrates the Raft consensus library from the deprecated openark/raft fork (unmaintained since 2017) to upstream hashicorp/raft v1.7.x. The migration updates API calls across three core files, removes the PeerStore abstraction, implements new cluster bootstrapping logic, and upgrades go-msgpack from v0.5.5 to v2.1.2 as a transitive dependency. The upstream library is vendored into the repository.

Changes

Cohort / File(s) Summary
Raft Upstream Migration
go.mod, go/raft/raft.go, go/raft/store.go, go/raft/file_snapshot.go
Updated to hashicorp/raft v1.7.x API: replaced PeerStore with GetConfiguration, changed AddPeer/RemovePeer to AddVoter/RemoveServer, replaced Yield/StepDown with LeadershipTransfer, added explicit BootstrapCluster logic for single/multi-node clusters, updated SnapshotStore.Create signature to accept version/configuration instead of peers.
go-msgpack Library Replacement
vendor/github.com/hashicorp/go-msgpack/*, vendor/github.com/hashicorp/go-msgpack/v2/*
Removed old v0.5.5 msgpack codec library (encode.go, decode.go, binc.go, simple.go, msgpack.go, rpc.go and supporting files); vendored new v2.1.2 with restructured encoder/decoder implementation, JSON codec support, helper code generation templates, and build/test infrastructure.
Upstream Raft Vendoring
vendor/github.com/hashicorp/raft/*
Added upstream hashicorp/raft library including core API (api.go, commands.go, commitment.go), configuration management, RPC protocol definitions (RPCHeader, RequestPreVote, TimeoutNow message types), build tooling (.travis.yml, Makefile, .golangci-lint.yml), and documentation updates (README, CHANGELOG).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 Upstream we hop, with Raft held high,
Nine years of patches now applied,
Old forks fade as modern code
Leads clusters down a safer road!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue61-raft-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate from openark/raft fork to upstream hashicorp/raft v1.7

2 participants