Distributed mutual exclusion
This page is an example of a CSE 452-style design doc for a distributed protocol.
The protocol
ensures mutual exclusion—when a node acquires the global lock, it is guaranteed that nobody else holds the lock. (For example, this might be useful
if the node needs to perform some actions with the guarantee that no other nodes
are performing similar actions at the same time.)
Preface
- Goals:
- Clients are assigned ownership of a global lock with the guarantee
that no other client
currently holds the lock. When they are done with the lock, they
release it so that other clients may acquire it.
- Clients that do not hold the lock are always requesting it.
- Clients are not guaranteed to eventually acquire
the lock if they ask for it - as we don't maintain a queue at the server.
Instead, a single client can repeatedly re-acquire the lock even though
others are also waiting.
- Desired fault model:
- Asynchronous unreliable message delivery with message reordering.
Assumes clients and the server obey the protocol, and that neither
clients nor the server crash.
- Challenges:
- An asynchronous, reordering, and duplicating network means that
it can be difficult for nodes to tell whether a message is current,
or a copy of an old already handled message.
Protocol
- Kinds of node:
- One unique server node that manages the lock state.
- Any number of client nodes trying to acquire the lock.
- State at each kind of node:
- At the server:
- current holder: client who currently holds the lock or None if nobody holds the lock
- numGranted: The number of times the lock has been acquired
- At the client:
- sequenceNum
- sequenceNum: 0
- 0 if the lock was never held by this client. If the lock is
currently held by this client, the number of times the the lock
has been granted to anyone.
Otherwise the client's sequenceNum at the last point in time when
the lock was held by this client.
- mode: {None, Held, Releasing, Released}
- init: None
- None means this client does not hold the lock.
- Held means that this client currently holds the lock.
- Releasing means that this client had the lock and has released it, but the
the server may or may not know.
- Released means that this client no longer holds the sequenceNum
version of the lock (and the server also knows); the server in the
meantime might have re-granted the lock.
- The client does not know whether other clients hold the lock or not,
except in state Held - in that case, it knows it is the only node with the lock.
Messages:
A figure sketching the relationship of the messages to one another
is at this link.
- Request
- Source: Client
- Destination: Server
- Contents: client ID
- Sent on startup and whenever the client takes a timeout and
the lock is not held. In other words, the client always
requests the lock.
- What happens at the destination when it is received?
- The server checks whether the lock is currently held by the requester.
- If yes, it replies with the current numGranted.
- If the lock is held by some other client, the server ignores the request. (The client will retry.)
- If the lock is not held, it increments numGranted, sets the current holder
to the sender of the request and sends a Grant message with the new value
of numGranted back to the sender.
- Grant
- Source: Server
- Destination: Client that sent Request
- Contents: current value of numGranted
- Sent whenever a Request arrives and either the lock is not held or the
the lock is currently held by the client making the request.
- What happens at the destination when it is received?
- The client compares the client sequenceNum against numGranted in the
message. If numGranted is higher, the lock is acquired, the
client goes into Held and updates the client sequenceNum.
Note that the client cannot already be in Held in this case.
Otherwise the message is ignored.
- The client can now perform whatever critical section it needs to
do. If there is no need for the lock, or when
the client is done with its critical section, it sends
Release and sets mode to Releasing.
- Release
- Source: client
- Destination: server
- Contents: sequenceNum - set to the current sequenceNum at the client
- When is it sent? When the critical section finishes, or when the client
timer expires and the client mode is Releasing (the client doesn't know
if the previous Release made it to the server).
- What happens at the destination when it is received? The server
checks the message sequenceNum against the current numGranted.
If they match, this is a valid Release, and the holder is set to None.
If the sequenceNum is smaller than numGranted, this is a duplicate.
(The sequenceNum can never be larger than numGranted.) Either way,
the server replies with a ReleaseAck to ensure that the client
knows that the lock has been released (possibly some time ago).
- ReleaseAck
- Source: server
- Destination: client that sent Release
- Contents: current value of numGranted
- When is it sent? In reply to a Release message
- What happens at the destination when it is received? If the
current value of the sequenceNum at the client is larger than
numGranted, this is a replay to an older Release and we can safely
ignore it (we've moved on and re-acquired the lock). Otherwise, if the
state is Releasing, changed to Released. (The state could already
be Released if this is a duplicate Ack.) We do not update
the sequenceNum until we re-acquire the lock, and we don't immediately
issue another Request but instead wait for a timeout to do that.
Timers:
-
ClientRetransmit: On expiration, if the client is in state None or
Released, it issues a Request message. If the client is in
state Releasing (the server may not have received the lock release),
we send a Release message. If the client is in Held, do nothing.
- The server does not have a timer.
Correctness/Liveness
- A table sketching the error handling for each message type is
at this link link.
- The table summarizes how message drops, duplicates, reorders,
and delays are handled. Unless the lock is currently held by
the client, the client will always retry, and so either the
lock will be released (if the release was lost) or the lock will
be acquired (if the lock is currently free).
- The system retries forever if delays are arbitrarily long.
- The system does not handle node failures. Server failures, or
client failures while holding the lock will cause the system to deadlock.
- The system does not handle partitions. These can cause the system to deadlock.
Conclusion
- Goals achieved: Mutual exclusion. Only one client at a time will hold the lock,
and it will never think it holds the lock when another client holds
the lock.
- Limitations: Does not handle failures or partitions.
- What goals are only achieved sometimes? Clients waiting for a lock
may only sometimes get the lock, if they are unlucky as to when their
requests arrive at the server.