In order to complete this ticket, we will first need to build on the work done in
SERVER-35873. First, we should preserve OpTimes instead of Timestamps since we calculate the stable timestamp using OpTimes. Next, we want to keep a separate data structure that tracks all "oldest oplog entry OpTimes" for each transaction whose corresponding abort/commit oplog entry has not been majority committed. To do this, when we remove an "oldest active OpTime" from oldestActiveOplogEntryOpTimes, we will also add it to oldestActiveUncommittedOpTimes along with its corresponding "finishOpTime" (which is the OpTime of the commit/abort oplog entry).
oldestActiveUncommittedOpTimes will be a set of OpTime pairs -> (oldest active OpTime per transaction, corresponding commit/abort oplog entry OpTime).
This should be enough information to set the stable timestamp back to the "oldest active OpTime of transactions whose corresponding commit/abort oplog entries have not been majority committed". Let's refer to this as the "oldest active uncommitted txn OpTime" for now. In ReplicationCoordinatorImpl, we have a function that calculates the stable OpTime. Instead of just calculating the min of the "all committed timestamp" and the current "commit point," we will also add the "oldest active uncommitted txn Timestamp" to this comparison and take the min of all three.
The last thing we need to do is properly remove an OpTime pair once the commit point advances past an entry's commit/abort oplog entry. This would mean that the entry is majority committed and we are able to move the stable timestamp.
Since this code will be separate from the code maintaining the oldestActiveOpTime, it should be simpler to remove once we no longer need to hold the stable timestamp back.