Raft Project 1 Review/Discussion (Winter 2020)
Lecture Notes for CS 190
Winter 2020
John Ousterhout
Class Design
- Small project, so not many opportunities for deep classes.
- Communication
- Persistence
- Most common problems:
- Fuzzy division of responsibility
- A class handles part of a problem, but not all of it.
- Or, mutliple implementations of the same thing (e.g.,
for clients and servers)
- Specialization: information about Raft leaks into the infrastructure
classes
- Thoughts for Project 2:
- 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
- Make classes general-purpose whenever possible
- Delay specialization: push it up to the highest layers
of the application
- Just because you know something doesn't mean you should use
that information
- Asynchronous I/O vs. multiple threads?
- Alternatives for connection topology:
- 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.
- State machine
- Separate code for each state
- Separate code for each message type
Synchronization and Threads
- At a low level, several projects had a scary number of mutexes
- Almost never need more than one mutex in a given object (only if
you have measured contention)
- At a high level: what concurrency is needed?
- Will the choice of synchronization mechanism have a big
impact on performance?
- What is the simplest synchronization/thread architecture that will meet needs?
Timers
- Two ways to implement timers
- Separate timer mechanism in its own thread(s)
- Timeouts on I/O operations
- Starting and stopping is awkward with separate thread
- Can use condition variables with timeouts
- Separate timers for elections and heartbeats can also be awkward
- Must keep track of which is running
- Awkward interfaces: relative vs. absolute time
- Can't use second-granularity timers
Exception Handling
- Common problems:
- Not enough error checks
- Not enough info in log messages
- Exceptions not handled in the best way
- 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).
(Example Log1)
- 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
- 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)
- Indentation affects readability (example Obvious4)
Documentation
- Many projects did a pretty good job.
- A few projects didn't have enough:
- Particularly: instance variables and parameters
- Documentation duplicates code (example Dup)
- Sometimes can have too much documentation (examples TMI1-3)
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)