Raft Project 1 Review/Discussion (Winter 2021)

Lecture Notes for CS 190
Winter 2021
John Ousterhout

Class Design

  • Small project, so not many opportunities for deep classes.
    • Communication
    • Persistence
    • Raft server state machine
    • Client-side communication
    • Shell command execution
  • Most common problems:
    • Specialization: API tailored in ways that restrict its usage for things other than the current Raft implementation
    • Fuzzy division of responsibility
      • A class handles part of a problem, but not all of it.
      • Or, multiple implementations of the same thing (e.g., for clients and servers)
  • Classes for communication:
    • Connection topology alternatives:
      • Requester opens connections, responses sent back on the same connection as request.
      • Sender opens connections: all outgoing traffic (requests and responses uses sender's connection).
      • One connection between each pair of servers.
    • Threading architecture:
      • Must allow independent operation of each socket
        • If one socket blocks, this must not prevent communication to/from other sockets
      • Choice #1: single thread:
        • Simple and clean from synchronization standpoint
        • But, must use nonblocking I/O:
          • Reads may return only part of a message (must save it until the rest arrives).
          • Writes may send only part of the message (must save the remainder to try again later).
      • Choice #2: separate thread(s) for each socket
        • Introduces synchronization issues
        • If there are many connections, this becomes inefficient
        • Does the multi-threaded approach increase server throughput?
      • Observation: the sockets streaming API is awkward for RPCs
    • Unifying client-client and client-server communication:
      • What problems motivated the differences?
  • Persistence:
    • In most projects this was specialized for Raft:
      • No class: persistence implemented by Raft state machine
      • Separate class, but APIs reflect Raft details such as term and vote
  • Raft state machine
    • Collect all of this code into a single class
    • Very simple API:
      • Constructor
      • run method
    • Decomposition choice #1: separate code for each state:
      • One method or class per state
    • Decomposition choice #2: separate code for each message type
  • Client-side communication:
    • Implementations were too specialized (read from stdin, write to stdout)
  • Executing client commands:
    • A very small class, but has nothing in common with the Raft state machine.

Exception Handling

  • Common problems:
    • Not enough error checks
    • Not enough info in log messages
    • Exceptions not handled in the best way
  • In general, unsafe to assume anything about information coming from outside the process
    • Contents of files holding persistent data (e.g. std::stoi).
    • Message formats
  • Logging is essential:
    • Log as often as you can possibly afford
    • Include as much information in the log message as possible
    • Log at the scene of the crime, where the most information is available (or, incorporate the info into an exception).
  • Must check results of every kernel call
  • What to do when an error occurs? First, think about how it is likely to be handled.
  • Don't exit in low-level methods
    • Limits generality
    • Bad for unit testing
    • Instead, throw exception
  • Define specific exception types: don't just use std::exception (consider likely usage)
  • All threads should have top-level exception handlers: catch, log, exit

Writing Obvious Code

  • C++ constructs to avoid:
    • std::pair, std::tuple
    • auto
    • Use closures judiciously (examples Obvious1-2)
    • C++ I/O APIs are clunky (example Obvious3)
    • Spacing and indentation affect readability (example Obvious4)

Documentation

  • Most projects did a pretty good job.
  • A few projects didn't have enough:
    • Particularly: instance variables and parameters
  • Common error: implementation information in interface documentation (example Doc1)
  • It is possible to overdo documentation (example Doc2)

Miscellaneous

  • Interface comments:
    • In header file vs. code file
    • Private vs. public methods
    • Multiple headers (interface vs. implementation?)
  • Bad variable names (Example Misc1-2)
  • Using one variable for two different things (Misc3)

Thoughts for Project 2

  • Avoid specializations and restrictions.
    • Just because you know something doesn't mean you should use that information
    • Delay specialization: push it up to the highest layers of the application
  • Make classes general-purpose
  • Don't solve just part of a problem such as RPC communication or persistence; solve the whole problem in one place
  • Think about solving big problems, not little ones
    • Design top-down?