Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor raftexample using interfaces to better separate concerns #1

Open
wants to merge 32 commits into
base: raft-example
Choose a base branch
from

Commits on Jul 19, 2023

  1. Add a Linux implementation of file locking

    This is taken straight from
    
        github.com/etcd-io/etcd:client/pkg/fileutil/lock_flock.go
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    2de1d3c View commit details
    Browse the repository at this point in the history
  2. Fix/suppress some linter warnings

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    adc2baf View commit details
    Browse the repository at this point in the history
  3. Define SnapshotStorage interface

    This interface defines what `kvstore` needs for saving and loading
    snapshots.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    a106937 View commit details
    Browse the repository at this point in the history
  4. Define KVStore interface

    This interface defines what `httpKVAPI` needs as the backing key-value
    store.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    dab4875 View commit details
    Browse the repository at this point in the history
  5. raftNode: initialize snapshotStorage in newRaftNode()

    Initialize the snapshot storage in `newRaftNode()` rather than in
    `startRaft()`. This is a step towards getting rid of the
    `snapshotStorageReady` channel.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    5e02aa3 View commit details
    Browse the repository at this point in the history
  6. newRaftNode(): inline part of the goroutine's work

    This is another step towards getting rid of `snapshotStorageReady`.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    ce5fd50 View commit details
    Browse the repository at this point in the history
  7. startRaftNode(): replacement for newRaftNode()

    Rename `newRaftNode()` to `startRaftNode()`, and change it to replay
    the WAL synchronously, before returning. This doesn't change anything,
    because the callers were all waiting for `snapshotStorageReady` before
    proceeding anyway.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    0513333 View commit details
    Browse the repository at this point in the history
  8. raftNode.snapdir: remove member

    Use a local variable (in `startRaftNode()`) instead.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    4f23ef4 View commit details
    Browse the repository at this point in the history
  9. raftNode.id: convert type to uint64

    This is more consistent with the types used elsewhere for IDs.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    e13d27d View commit details
    Browse the repository at this point in the history
  10. startRaftNode(): take the SnapshotStorage as an argument

    This is the sort of thing that the caller should be able to inject,
    and it's not part of the raft algorithm itself. This change makes it
    clearer what a user of the raft algorithm must supply to it.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    6c51b15 View commit details
    Browse the repository at this point in the history
  11. kvstore.loadAndApplySnapshot(), applyCommits(): extract methods

    Extract two new methods, for clarity and to reduce code duplication.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    81cfae6 View commit details
    Browse the repository at this point in the history
  12. kvstore: separate initialization from startup

    Change `newKVStore()` to set up the initial state, but not start the
    `readCommits()` goroutine anymore. Instead, change the callers to call
    `processCommits()` (a renamed version of `readCommits()`) themselves
    (in a goroutine).
    
    The main benefit of this change is that the `kvstore` can be
    instantiated before calling `startRaftNode()`, meaning that
    `startRaftNode()` can be passed `kvs.getSnapshot` as an argument.
    Previously, it had to be passed an anonymous function that refers to a
    variable (`kvs`) that has not yet been initialized. This asynchrony
    was confusing and unnecessary.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    67e3892 View commit details
    Browse the repository at this point in the history
  13. kvstore.loadSnapshot(): inline method

    It wasn't doing anything useful.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    b928307 View commit details
    Browse the repository at this point in the history
  14. FSM: new interface, representing a finite state machine

    Add a new interface, `FSM`, which represents a finite state machine
    that can be driven by raft.
    
    Also add `kvfsm`, which is an `FSM` view of a `kvstore`. Treating
    `kvstore` and `kvfsm` as separate types means that the public
    interface of `kvstore` is not contaminated with internal methods that
    are only there to support raft and should not be invoked by the user.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    a4bad71 View commit details
    Browse the repository at this point in the history
  15. Move more functionality from kvstore to kvfsm

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    3062558 View commit details
    Browse the repository at this point in the history
  16. TestProposeOnCommit(): add some clarifying comments

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    093d670 View commit details
    Browse the repository at this point in the history
  17. raftexample_test: introduce peer type

    Make `cluster` hold an array of `peer`s, rather than one array for
    each peer attribute.
    
    Continue storing the names separately, since this `[]string` has to be
    passed to `startRaftNode()`.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    317f7c1 View commit details
    Browse the repository at this point in the history
  18. raftexample_test: give each peer its own FSM

    The tests can be made cleverer if each `peer` in a `cluster` can be
    instantiated with an arbitrary finite state machine. We'll use this to
    make some tests able to operate more like normal clients of
    `raftNode`s.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    5e6216d View commit details
    Browse the repository at this point in the history
  19. kvfsm.applyCommits(): return an error

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    88c1adf View commit details
    Browse the repository at this point in the history
  20. FSM.ProcessCommits(): return an error rather than calling log.Fatal()

    Handle such errors at the caller, instead.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    d368f8f View commit details
    Browse the repository at this point in the history
  21. FSM.ApplyCommits(): new method

    This is a step towards moving `ProcessCommits()` out of this interface
    and into `raftNode`.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    4c4fd23 View commit details
    Browse the repository at this point in the history
  22. newKVStore(): don't call LoadAndApplySnapshot()

    Instead, make `LoadAndApplySnapshot()` part of the `FSM` interface,
    and have the callers of `newKVStore()` invoke that method after
    calling `newKVStore()`. This is a step towards moving this
    functionality entirely out of `FSM`.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    51e4dc4 View commit details
    Browse the repository at this point in the history
  23. Make ProcessCommits() a method of raftNode

    Now that the `FSM` interface is more capable, there is no reason that
    this method, which has more to do with raft anyway, can't be
    implemented in `raftNode`. This makes it easier to implement new FSMs
    without having to reimplement this method.
    
    This means that `newRaftNode()` has to return a pointer to the
    `raftNode`, so make that change, too. This change will make other
    future changes simpler, too.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    ec0d6e2 View commit details
    Browse the repository at this point in the history
  24. newRaftNode(): call LoadAndApplySnapshot() here

    Make this a standard part of starting up a node.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    1a3e303 View commit details
    Browse the repository at this point in the history
  25. LoadAndApplySnapshot(): move method to raftNode and make it private

    This is the last use of `kvstore.snapshotStorage`, so remove that
    field as well.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    5a1eaee View commit details
    Browse the repository at this point in the history
  26. raftNode: add a new and better way to tell when the node is done

    The old method, monitoring the `errorC` channel, is not great because
    the error only pops out of that channel once. Which of the pieces of
    code that are waiting on that channel reads the error? Nobody knows!
    
    Instead, provide a `Done()` method and an `Err()` method that
    interested parties can use to determine when the node finishes and
    what error it returned.
    
    The callers haven't been changed yet.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    2b78781 View commit details
    Browse the repository at this point in the history
  27. serveHTTPKVAPI(): monitor the raft node using its "done" channel

    Use the `done` channel instead of the `errorC` channel for monitoring
    for the completion of the raft node. If the channel is closed, don't
    log the error, since the caller of `ProcessCommits()` is already
    taking care of that.
    
    Since this means that we're not killing the server by aborting the
    program, we now have to call `srv.Close()` explicitly.
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    3e4b1c4 View commit details
    Browse the repository at this point in the history
  28. cluster.Close(): read any error from the node directly

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    4f0c2ef View commit details
    Browse the repository at this point in the history
  29. TestProposeOnCommit(): read any error from the node directly

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    0504378 View commit details
    Browse the repository at this point in the history
  30. cluster.Cleanup(): new method, extracted from Close()

    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    75331cd View commit details
    Browse the repository at this point in the history
  31. TestProposeOnCommit(): change test to use ProcessCommits()

    Instead of driving the channels within the test, user a cleverer FSM
    and call `ProcessCommits()` to manage the channels.
    
    The old version of the test only looked at the first data item in each
    `commit`, even though multiple data items can be sent in a single
    `commit`. It also only looked at the first 100 commits, even though
    there is a multiplication factor because each node initiates 100
    proposals. The new version checks all data items, and checks that the
    total number of commits is as expected (namely, 100 for each node, or
    300 total).
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    41c3f43 View commit details
    Browse the repository at this point in the history
  32. newRaftNode(): don't return commitC and errorC

    Keep `commitC` and `errorC` internal to `raftNode`: don't return them
    from `newRaftNode()`, and don't require them as arguments to
    `(*raftNode).ProcessCommits()`.
    
    (Some tests still need them, but they can access the members directly
    on the `raftNode` instance.)
    
    Signed-off-by: Michael Haggerty <[email protected]>
    mhagger committed Jul 19, 2023
    Configuration menu
    Copy the full SHA
    b4562f9 View commit details
    Browse the repository at this point in the history