17.1.2 RPC Operations
One can execute RPCs within a lock-based critical section, as well as
from within an RCU read-side critical section. What happens when you
attempt to execute an RPC from within a transaction?
If both the RPC request and its response are to be contained within the
transaction, and if some part of the transaction depends on the result
returned by the response, then it is not possible to use the memory-buffer
tricks that can be used in the case of buffered I/O.
Any attempt to
take this buffering approach would deadlock the transaction, as the
request could not be transmitted until the transaction was guaranteed
to succeed, but the transaction's success might not be knowable until
after the response is received, as is the case in the following example:
1 begin_trans();
2 rpc_request();
3 i = rpc_response();
4 a[i]++;
5 end_trans();
|
The transaction's memory footprint cannot be determined until after the
RPC response is received, and until the transaction's memory footprint
can be determined, it is impossible to determine whether the transaction
can be allowed to commit.
The only action consistent with transactional semantics is therefore to
unconditionally abort the transaction, which is, to say the least,
unhelpful.
Here are some options available to TM:
- Prohibit RPC within transactions, so that any attempt to execute
an RPC operation aborts the enclosing transaction (and perhaps
multiple nested transactions).
Alternatively, enlist the compiler to enforce RPC-free
transactions.
This approach does works, but will require TM to
interact with other synchronization primitives.
- Permit only one special
``inevitable'' transaction [SMS08]
to proceed at any given time, thus allowing inevitable
transactions to contain RPC operations.
This works in general, but severely limits the scalability and
performance of RPC operations.
Given that scalability and performance is a first-class goal of
parallelism, this approach's generality seems a bit self-limiting.
Furthermore, use of inevitable transactions to permit RPC
operations rules out manual transaction-abort operations
once the RPC operation has started.
- Identify special cases where the success of the transaction may
be determined before the RPC response is received, and
automatically convert these to inevitable transactions immediately
before sending the RPC request.
Of course, if several concurrent transactions attempt RPC calls
in this manner, it might be necessary to roll all but one of them
back, with consequent degradation of performance and scalability.
This approach nevertheless might be valuable given long-running
transactions ending with an RPC.
This approach still has problems with manual transaction-abort
operations.
- Identify special cases where the RPC response may be moved out
of the transaction, and then proceed using techniques similar
to those used for buffered I/O.
- Extend the transactional substrate to include the RPC server as
well as its client.
This is in theory possible, as has been demonstrated by
distributed databases.
However, it is unclear whether the requisite performance and
scalability requirements can be met by distributed-database
techniques, given that memory-based TM cannot hide such latencies
behind those of slow disk drives.
Of course, given the advent of solid-state disks, it is also unclear
how much longer databases will be permitted to hide their latencies
behind those of disks drives.
As noted in the prior section, I/O is a known weakness of TM, and RPC
is simply an especially problematic case of I/O.
Paul E. McKenney
2011-12-16