package conflictdetection
- Alphabetic
- Public
- Protected
Type Members
- final case class ActivenessResult(contracts: ActivenessCheckResult[LfContractId, Status], inactiveTransfers: Set[TransferId], keys: ActivenessCheckResult[LfGlobalKey, Status]) extends PrettyPrinting with Product with Serializable
The result of the activeness check for an ActivenessSet.
The result of the activeness check for an ActivenessSet. If all sets are empty, the activeness check was successful.
- contracts
The contracts whose activeness check has failed
- inactiveTransfers
The transfers that shall be completed, but that are not active.
- final case class ActivenessSet(contracts: ActivenessCheck[LfContractId], transferIds: Set[TransferId], keys: ActivenessCheck[LfGlobalKey]) extends PrettyPrinting with Product with Serializable
Defines the contracts and transfers for conflict detection.
Defines the contracts and transfers for conflict detection. Transfers are not locked because the transferred contracts are already being locked.
- final case class CommitSet(archivals: Map[LfContractId, WithContractHash[ArchivalCommit]], creations: Map[LfContractId, WithContractHash[CreationCommit]], transferOuts: Map[LfContractId, WithContractHash[TransferOutCommit]], transferIns: Map[LfContractId, WithContractHash[TransferInCommit]], keyUpdates: Map[LfGlobalKey, Status]) extends PrettyPrinting with Product with Serializable
Describes the effect of a confirmation request on the active contracts, contract keys, and transfers.
Describes the effect of a confirmation request on the active contracts, contract keys, and transfers. Transient contracts appear the following two sets:
- The union of creations and transferIns
- The union of archivals or transferOuts
- archivals
The contracts to be archived, along with their stakeholders. Must not contain contracts in transferOuts.
- creations
The contracts to be created.
- transferOuts
The contracts to be transferred out, along with their target domains and stakeholders. Must not contain contracts in archivals.
- transferIns
The contracts to be transferred in, along with their transfer IDs.
- keyUpdates
The contract keys with their new state.
- Exceptions thrown
java.lang.IllegalArgumentException
iftransferOuts
overlap witharchivals
orcreations
overlaps withtransferIns
.
- trait RequestTracker extends RequestTrackerLookup with AutoCloseable with NamedLogging
The request tracker handles all the tasks around conflict detection that are difficult to parallelize.
The request tracker handles all the tasks around conflict detection that are difficult to parallelize. In detail, it tracks in-flight requests, performs activeness checks, detects conflicts between requests, keeps track of the sequencer time, checks for timeouts, and orchestrates the updates to the com.digitalasset.canton.participant.store.ActiveContractStore. It does not write to the com.digitalasset.canton.participant.protocol.RequestJournal, though. The request tracker lives entirely in memory on a single compute node.
The request tracker is a deterministic component that depends only on the ACS state and the sequencer messages. The order and timing in which the request tracker receives this information does not matter. (Even more broadly, the participant has no functional notion of “wall-clock” time; all time and timestamps come from sequencer messages.) These properties of determinism and sequencer time are both important parts of Canton and critical to the crash recovery model.
To keep track of the sequencer time, the request tracker must be notified of every message that is received from the sequencer. These notifications need not happen in order, but they must happen eventually. Every such message has an associated monotonically increasing SequencerCounter. If one of the sequencer counters is skipped, the request tracker may stall indefinitely. Every message has an associated timestamp. Time must increase with every message in the order given by the sequencer counter. This means that for sequencer counters
sc1
andsc2
withsc1
<sc2
, the associated timestampsts1
andts2
must satisfyts1
<ts2
.Requests are identified by RequestCounters, which themselves must be a monotonically increasing sequence without gaps.
The request tracker uses these time signals to determine on which requests a verdict can be issued, which requests have timed out, and which requests and contract states can be safely evicted from the tracker's internal state. We say that the request tracker has observed a timestamp if it has been signalled this timestamp or a later one. The request tracker can progress to a timestamp
ts
if it has observed all timestamps up to and includingts
and all commit sets with commit times up tots
have been added with RequestTracker.addCommitSet.If the request tracker can progress to a timestamp
ts
, then it must eventually complete all futures that RequestTracker.addRequest and RequestTracker.addCommitSet have returned and that are associated with a timestamp equal or prior tots
. The futures are associated to the following timestamps:- Activeness result: the activeness time of the request, typically the timestamp on the request
- Timeout result: the decision time of the request
- Finalization result: the commit time of the request
The three methods RequestTracker.addRequest, RequestTracker.addResult, and RequestTracker.addCommitSet must normally be called in the given sequence; the latter two need not be called if the request has timed out. A request is in flight from the corresponding call to RequestTracker.addRequest until the request tracker has progressed to its decision time (if the request times out) or its commit time.
These three methods are idempotent while the request is in flight. That is, if one of the methods is called twice with the same arguments, provided that the first one succeeded, then the second call has no effect, but its return value is equivalent to what the first call returned. If the method is called twice for the same request counter with different arguments, the subsequent behavior of the request tracker becomes unspecified. The same applies if the same sequencer counter is supplied several times with different timestamps.
A request tracker is typically initialized with a SequencerCounter and a com.digitalasset.canton.data.CantonTimestamp, causing it to be ready to handle requests starting from the given sequencer counter (inclusive) and timestamp (exclusive).
Conflict detection
The request tracker checks whether a request uses only contracts that are active at the activeness timestamp of the request. To that end, it determines the ActivenessResult of the request. A non-conflicting request should be approved in a response by the participant. A conflicting request should be rejected.
To describe conflict behavior, we introduce a few concepts:
A conflict detection time is a triple (com.digitalasset.canton.data.CantonTimestamp,
Kind
, SequencerCounter). There are three kinds, and they are ordered byFinalization < Timeout < Activeness
. Conflict detection times are ordered lexicographically. In other words,
(ts1, kind1, sc1) < (ts2, kind2, sc2)
if and only if
ts1 < ts2
or
ts1 == ts2 && kind1 < kind2
or
ts1 == ts2 && kind1 == kind2 && sc1 < sc2.
A request with SequencerCounter
sc
and com.digitalasset.canton.data.CantonTimestampts
induces two conflict detection times: The sequencing time at(ts, Activeness, sc)
and the timeout time at (decisionTime, Timeout, sc) wheredecisionTime
is the decision time of the request. Without logical reordering, the sequencing time is also the request's activeness time. (Technically, the activeness time must lie between the sequencing time and the decision time; see RequestTracker.addRequest.) A result with SequencerCountersc
and com.digitalasset.canton.data.CantonTimestampts
also defines two conflict detection times: The verdict time(ts, Result, sc)
and the finalization time(commitTime, Finalize, sc)
wherecommitTime
is the commit time of the request.A request is active at some conflict detection time
t
if all of the following holds:- Its sequencing time is equal or prior to
t
. - If no finalization time is known for the request, then
t
is equal or prior to the request's timeout time. - If a finalization time is known for the request, then
t
is equal or prior to the finalization time.
The finalization time of a request is known if the result has been successfully signalled to the request tracker using RequestTracker.addResult.
A request is finalized at some conflict detection time
t
if its finalization time is known and equal or prior tot
.A contract is active at time
t
if a request with activeness time at mostt
activates it, and there is no finalized request at timet
that has deactivated it. Activation happens through creation and transfer-in. Deactivation happens through archival and transfer-out. An active contractc
is locked at timet
if one of the following cases holds:- There is an active request at time
t
that activatesc
. In that case, we say that the contract is in activation. - There is an active request at time
t
that deactivatesc
and the request does not activatec
.
A contract may be locked because of activation and deactivation at the same time if there are two active requests, one activating the contract and another deactivating it. For example, if one request
r1
creates a contract with sequencing timet1
and finalization timet1'
and another requestr2
with sequencing timet2
betweent1
andt1'
archives it (despite thatr2
's activeness check fails), then the contract is created at timet1
and archived att2
.Activeness of contracts is checked at the request's activeness time. The ActivenessResult lists all contracts from the ActivenessSet of the request that are either locked or whose precondition fails. The activeness check succeeds if the activeness result is empty.
- trait RequestTrackerLookup extends AutoCloseable with NamedLogging
Value Members
- object ActivenessSet extends Serializable
- object CommitSet extends Serializable
- object RequestTracker