github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/computation/computer/transaction_coordinator.go (about)

     1  package computer
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/onflow/flow-go/fvm"
     8  	"github.com/onflow/flow-go/fvm/storage"
     9  	"github.com/onflow/flow-go/fvm/storage/derived"
    10  	"github.com/onflow/flow-go/fvm/storage/logical"
    11  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    12  )
    13  
    14  type TransactionWriteBehindLogger interface {
    15  	AddTransactionResult(
    16  		txn TransactionRequest,
    17  		snapshot *snapshot.ExecutionSnapshot,
    18  		output fvm.ProcedureOutput,
    19  		timeSpent time.Duration,
    20  		numTxnConflictRetries int,
    21  	)
    22  }
    23  
    24  // transactionCoordinator provides synchronization functionality for driving
    25  // transaction execution.
    26  type transactionCoordinator struct {
    27  	vm fvm.VM
    28  
    29  	mutex *sync.Mutex
    30  	cond  *sync.Cond
    31  
    32  	snapshotTime logical.Time // guarded by mutex, cond broadcast on updates.
    33  	abortErr     error        // guarded by mutex, cond broadcast on updates.
    34  
    35  	// Note: database commit and result logging must occur within the same
    36  	// critical section (guraded by mutex).
    37  	database       *storage.BlockDatabase
    38  	writeBehindLog TransactionWriteBehindLogger
    39  }
    40  
    41  type transaction struct {
    42  	request            TransactionRequest
    43  	numConflictRetries int
    44  
    45  	coordinator *transactionCoordinator
    46  
    47  	startedAt time.Time
    48  	storage.Transaction
    49  	fvm.ProcedureExecutor
    50  }
    51  
    52  func newTransactionCoordinator(
    53  	vm fvm.VM,
    54  	storageSnapshot snapshot.StorageSnapshot,
    55  	cachedDerivedBlockData *derived.DerivedBlockData,
    56  	writeBehindLog TransactionWriteBehindLogger,
    57  ) *transactionCoordinator {
    58  	mutex := &sync.Mutex{}
    59  	cond := sync.NewCond(mutex)
    60  
    61  	database := storage.NewBlockDatabase(
    62  		storageSnapshot,
    63  		0,
    64  		cachedDerivedBlockData)
    65  
    66  	return &transactionCoordinator{
    67  		vm:             vm,
    68  		mutex:          mutex,
    69  		cond:           cond,
    70  		snapshotTime:   0,
    71  		abortErr:       nil,
    72  		database:       database,
    73  		writeBehindLog: writeBehindLog,
    74  	}
    75  }
    76  
    77  func (coordinator *transactionCoordinator) SnapshotTime() logical.Time {
    78  	coordinator.mutex.Lock()
    79  	defer coordinator.mutex.Unlock()
    80  
    81  	return coordinator.snapshotTime
    82  }
    83  
    84  func (coordinator *transactionCoordinator) Error() error {
    85  	coordinator.mutex.Lock()
    86  	defer coordinator.mutex.Unlock()
    87  
    88  	return coordinator.abortErr
    89  }
    90  
    91  func (coordinator *transactionCoordinator) AbortAllOutstandingTransactions(
    92  	err error,
    93  ) {
    94  	coordinator.mutex.Lock()
    95  	defer coordinator.mutex.Unlock()
    96  
    97  	if coordinator.abortErr != nil { // Transactions are already aborting.
    98  		return
    99  	}
   100  
   101  	coordinator.abortErr = err
   102  	coordinator.cond.Broadcast()
   103  }
   104  
   105  func (coordinator *transactionCoordinator) NewTransaction(
   106  	request TransactionRequest,
   107  	attempt int,
   108  ) (
   109  	*transaction,
   110  	error,
   111  ) {
   112  	err := coordinator.Error()
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	txn, err := coordinator.database.NewTransaction(
   118  		request.ExecutionTime(),
   119  		fvm.ProcedureStateParameters(request.ctx, request))
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	return &transaction{
   125  		request:            request,
   126  		coordinator:        coordinator,
   127  		numConflictRetries: attempt,
   128  		startedAt:          time.Now(),
   129  		Transaction:        txn,
   130  		ProcedureExecutor: coordinator.vm.NewExecutor(
   131  			request.ctx,
   132  			request.TransactionProcedure,
   133  			txn),
   134  	}, nil
   135  }
   136  
   137  func (coordinator *transactionCoordinator) commit(txn *transaction) error {
   138  	coordinator.mutex.Lock()
   139  	defer coordinator.mutex.Unlock()
   140  
   141  	if coordinator.abortErr != nil {
   142  		return coordinator.abortErr
   143  	}
   144  
   145  	executionSnapshot, err := txn.Transaction.Commit()
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	coordinator.writeBehindLog.AddTransactionResult(
   151  		txn.request,
   152  		executionSnapshot,
   153  		txn.Output(),
   154  		time.Since(txn.startedAt),
   155  		txn.numConflictRetries)
   156  
   157  	// Commit advances the database's snapshot.
   158  	coordinator.snapshotTime += 1
   159  	coordinator.cond.Broadcast()
   160  
   161  	return nil
   162  }
   163  
   164  func (txn *transaction) Commit() error {
   165  	return txn.coordinator.commit(txn)
   166  }
   167  
   168  func (coordinator *transactionCoordinator) waitForUpdatesNewerThan(
   169  	snapshotTime logical.Time,
   170  ) (
   171  	logical.Time,
   172  	error,
   173  	logical.Time,
   174  	error,
   175  ) {
   176  	coordinator.mutex.Lock()
   177  	defer coordinator.mutex.Unlock()
   178  
   179  	startTime := coordinator.snapshotTime
   180  	startErr := coordinator.abortErr
   181  	for coordinator.snapshotTime <= snapshotTime && coordinator.abortErr == nil {
   182  		coordinator.cond.Wait()
   183  	}
   184  
   185  	return startTime, startErr, coordinator.snapshotTime, coordinator.abortErr
   186  }
   187  
   188  func (txn *transaction) WaitForUpdates() error {
   189  	// Note: the frist three returned values are only used by tests to ensure
   190  	// the function correctly waited.
   191  	_, _, _, err := txn.coordinator.waitForUpdatesNewerThan(txn.SnapshotTime())
   192  	return err
   193  }