1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use mvhashmap::{MVHashMapView, Version};
use std::{fmt::Debug, hash::Hash};

/// The execution result of a transaction
#[derive(Debug)]
pub enum ExecutionStatus<T, E> {
    /// Transaction was executed successfully.
    Success(T),
    /// Transaction hit a none recoverable error during execution, halt the execution and propagate
    /// the error back to the caller.
    Abort(E),
    /// Transaction was executed successfully, but will skip the execution of the trailing
    /// transactions in the list
    SkipRest(T),
    /// Transaction has an unexpected read dependency that is blocked by `Version` transaction.
    /// Put the transaction back to the scheduler.
    Retry(/*blocked_by*/ Version),
}

/// Trait that defines a transaction that could be parallel executed by the scheduler. Each
/// transaction will write to a key value storage as their side effect.
pub trait Transaction: Clone + Sync + Send + 'static {
    type Key: PartialOrd + Send + Sync + Clone + Hash + Eq;
    type Value: Send + Sync;
}

/// Trait for inferencing the read and write set of a transaction.
pub trait ReadWriteSetInferencer: Sync {
    /// Type of transaction and its associated key.
    type T: Transaction;

    /// Get the read set of a transaction. Read set estimation is used simply to improve the
    /// performance by exposing the read dependencies. Imprecise estimation won't cause execution
    /// failure.
    fn infer_reads(&self, txn: &Self::T) -> Result<Vec<<Self::T as Transaction>::Key>>;

    /// Get the write set of a transaction. Write set estimation is crucial to the execution
    /// correctness as there's no way to resolve read-after-write conflict where a write is
    /// unexpected. Thus we require write to be an over approximation for now.
    fn infer_writes(&self, txn: &Self::T) -> Result<Vec<<Self::T as Transaction>::Key>>;
}

/// Trait for single threaded transaction executor.
// TODO: Sync should not be required. Sync is only introduced because this trait occurs as a phantom type of executor struct.
pub trait ExecutorTask: Sync {
    /// Type of transaction and its associated key and value.
    type T: Transaction;

    /// The output of a transaction. This should contain the side effect of this transaction.
    type Output: TransactionOutput<T = Self::T>;

    /// Type of error when the executor failed to process a transaction and needs to abort.
    type Error: Clone + Send + Sync;

    /// Type to intialize the single thread transaction executor. Copy and Sync are required because
    /// we will create an instance of executor on each individual thread.
    type Argument: Sync + Copy;

    /// Create an instance of the transaction executor.
    fn init(args: Self::Argument) -> Self;

    /// Execute one single transaction given the view of the current state.
    fn execute_transaction(
        &self,
        view: MVHashMapView<<Self::T as Transaction>::Key, <Self::T as Transaction>::Value>,
        txn: &Self::T,
    ) -> ExecutionStatus<Self::Output, Self::Error>;
}

/// Trait for execution result of a transaction.
pub trait TransactionOutput: Send + Sync {
    /// Type of transaction and its associated key and value.
    type T: Transaction;

    /// Get the side effect of a transaction from its output.
    fn get_writes(
        &self,
    ) -> Vec<(
        <Self::T as Transaction>::Key,
        <Self::T as Transaction>::Value,
    )>;

    /// Execution output for transactions that comes after SkipRest signal.
    fn skip_output() -> Self;
}