github.com/okex/exchain@v1.8.0/libs/tendermint/state/execution_async_db.go (about)

     1  package state
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  type asyncDBContext struct {
     9  	// switch to turn on async save abciResponse and state
    10  	isAsyncSaveDB bool
    11  	// channel to write abciResponse async
    12  	abciResponseQueue chan abciResponse
    13  	/// channel to write state async
    14  	stateQueue chan State
    15  	// channel to feed back height of saved abci response and stat response
    16  	asyncFeedbackQueue chan int64
    17  	// flag to avoid waiting async state result for the first block
    18  	isWaitingLastBlock bool
    19  	//flag to avoid stop twice
    20  	isAsyncQueueStop bool
    21  	//wait group for quiting
    22  	wg sync.WaitGroup
    23  }
    24  
    25  const (
    26  	MAXCHAN_LEN           = 2
    27  	FEEDBACK_LEN          = 2
    28  	QUIT_SIG              = -99
    29  	MAX_WAIT_TIME_SECONDS = 30
    30  )
    31  
    32  type abciResponse struct {
    33  	height    int64
    34  	responses *ABCIResponses
    35  }
    36  
    37  func (blockExec *BlockExecutor) initAsyncDBContext() {
    38  	blockExec.abciResponseQueue = make(chan abciResponse, MAXCHAN_LEN)
    39  	blockExec.stateQueue = make(chan State, MAXCHAN_LEN)
    40  	blockExec.asyncFeedbackQueue = make(chan int64, FEEDBACK_LEN)
    41  
    42  	blockExec.wg.Add(3)
    43  	go blockExec.asyncSaveStateRoutine()
    44  	go blockExec.asyncSaveABCIRespRoutine()
    45  	go blockExec.fireEventsRountine()
    46  }
    47  
    48  func (blockExec *BlockExecutor) stopAsyncDBContext() {
    49  	if blockExec.isAsyncQueueStop {
    50  		return
    51  	}
    52  
    53  	blockExec.abciResponseQueue <- abciResponse{height: QUIT_SIG}
    54  	blockExec.stateQueue <- State{LastBlockHeight: QUIT_SIG}
    55  	blockExec.eventsChan <- event{}
    56  
    57  	blockExec.wg.Wait()
    58  
    59  	blockExec.isAsyncQueueStop = true
    60  }
    61  
    62  func (blockExec *BlockExecutor) SaveABCIResponsesAsync(height int64, responses *ABCIResponses) {
    63  	blockExec.abciResponseQueue <- abciResponse{height, responses}
    64  }
    65  
    66  func (blockExec *BlockExecutor) SaveStateAsync(state State) {
    67  	blockExec.stateQueue <- state
    68  }
    69  
    70  // asyncSaveRoutine handle writing state work
    71  func (blockExec *BlockExecutor) asyncSaveStateRoutine() {
    72  	for stateMsg := range blockExec.stateQueue {
    73  		if stateMsg.LastBlockHeight == QUIT_SIG {
    74  			break
    75  		}
    76  
    77  		SaveState(blockExec.db, stateMsg)
    78  		blockExec.asyncFeedbackQueue <- stateMsg.LastBlockHeight
    79  	}
    80  
    81  	blockExec.wg.Done()
    82  }
    83  
    84  // asyncSaveRoutine handle writing abciResponse work
    85  func (blockExec *BlockExecutor) asyncSaveABCIRespRoutine() {
    86  	for abciMsg := range blockExec.abciResponseQueue {
    87  		if abciMsg.height == QUIT_SIG {
    88  			break
    89  		}
    90  		SaveABCIResponses(blockExec.db, abciMsg.height, abciMsg.responses)
    91  		blockExec.asyncFeedbackQueue <- abciMsg.height
    92  	}
    93  	blockExec.wg.Done()
    94  }
    95  
    96  // SetIsAsyncSaveDB switches to open async write db feature
    97  func (blockExec *BlockExecutor) SetIsAsyncSaveDB(isAsyncSaveDB bool) {
    98  	blockExec.isAsyncSaveDB = isAsyncSaveDB
    99  }
   100  
   101  // wait for the last sate and abciResponse to be saved
   102  func (blockExec *BlockExecutor) tryWaitLastBlockSave(lastHeight int64) {
   103  	timeoutCh := time.After(MAX_WAIT_TIME_SECONDS * time.Second)
   104  	if blockExec.isAsyncSaveDB && blockExec.isWaitingLastBlock {
   105  		i := 0
   106  		for {
   107  			select {
   108  			case r := <-blockExec.asyncFeedbackQueue:
   109  				if r != lastHeight {
   110  					panic("Incorrect synced aysnc feed Height")
   111  				}
   112  				if i++; i == FEEDBACK_LEN {
   113  					return
   114  				}
   115  			case <-timeoutCh:
   116  				// It shouldn't be timeout. something must be wrong here
   117  				panic("Can't get last block aysnc result")
   118  			}
   119  		}
   120  	}
   121  }
   122  
   123  // try to save the abciReponse async
   124  func (blockExec *BlockExecutor) trySaveABCIResponsesAsync(height int64, abciResponses *ABCIResponses) {
   125  	if blockExec.isAsyncSaveDB {
   126  		blockExec.isWaitingLastBlock = true
   127  		blockExec.SaveABCIResponsesAsync(height, abciResponses)
   128  	} else {
   129  		SaveABCIResponses(blockExec.db, height, abciResponses)
   130  	}
   131  }
   132  
   133  // try to save the state async
   134  func (blockExec *BlockExecutor) trySaveStateAsync(state State) {
   135  	if blockExec.isAsyncSaveDB {
   136  		blockExec.SaveStateAsync(state)
   137  	} else {
   138  		//Async save state
   139  		SaveState(blockExec.db, state)
   140  	}
   141  }