github.com/koko1123/flow-go-1@v0.29.6/engine/execution/ingestion/stop_control_test.go (about)

     1  package ingestion
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/koko1123/flow-go-1/storage"
     8  
     9  	testifyMock "github.com/stretchr/testify/mock"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/koko1123/flow-go-1/engine/execution/state/mock"
    13  
    14  	"github.com/koko1123/flow-go-1/utils/unittest"
    15  )
    16  
    17  // If stopping mechanism has caused any changes to execution flow (skipping execution of blocks)
    18  // we disallow setting new values
    19  func TestCannotSetNewValuesAfterStoppingCommenced(t *testing.T) {
    20  
    21  	t.Run("when processing block at stop height", func(t *testing.T) {
    22  		sc := NewStopControl(unittest.Logger(), false, 0)
    23  
    24  		require.Equal(t, sc.GetState(), StopControlOff)
    25  
    26  		// first update is always successful
    27  		_, _, err := sc.SetStopHeight(21, false)
    28  		require.NoError(t, err)
    29  
    30  		require.Equal(t, sc.GetState(), StopControlSet)
    31  
    32  		// no stopping has started yet, block below stop height
    33  		header := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(20))
    34  		sc.blockProcessable(header)
    35  
    36  		require.Equal(t, sc.GetState(), StopControlSet)
    37  
    38  		_, _, err = sc.SetStopHeight(37, false)
    39  		require.NoError(t, err)
    40  
    41  		// block at stop height, it should be skipped
    42  		header = unittest.BlockHeaderFixture(unittest.WithHeaderHeight(37))
    43  		sc.blockProcessable(header)
    44  
    45  		require.Equal(t, sc.GetState(), StopControlCommenced)
    46  
    47  		_, _, err = sc.SetStopHeight(2137, false)
    48  		require.Error(t, err)
    49  
    50  		// state did not change
    51  		require.Equal(t, sc.GetState(), StopControlCommenced)
    52  	})
    53  
    54  	t.Run("when processing finalized blocks", func(t *testing.T) {
    55  
    56  		execState := new(mock.ReadOnlyExecutionState)
    57  
    58  		sc := NewStopControl(unittest.Logger(), false, 0)
    59  
    60  		require.Equal(t, sc.GetState(), StopControlOff)
    61  
    62  		// first update is always successful
    63  		_, _, err := sc.SetStopHeight(21, false)
    64  		require.NoError(t, err)
    65  		require.Equal(t, sc.GetState(), StopControlSet)
    66  
    67  		// make execution check pretends block has been executed
    68  		execState.On("StateCommitmentByBlockID", testifyMock.Anything, testifyMock.Anything).Return(nil, nil)
    69  
    70  		// no stopping has started yet, block below stop height
    71  		header := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(20))
    72  		sc.blockFinalized(context.TODO(), execState, header)
    73  
    74  		_, _, err = sc.SetStopHeight(37, false)
    75  		require.NoError(t, err)
    76  		require.Equal(t, sc.GetState(), StopControlSet)
    77  
    78  		// block at stop height, it should be trigger stop
    79  		header = unittest.BlockHeaderFixture(unittest.WithHeaderHeight(37))
    80  		sc.blockFinalized(context.TODO(), execState, header)
    81  
    82  		// since we set crash to false, execution should be paused
    83  		require.Equal(t, sc.GetState(), StopControlPaused)
    84  
    85  		_, _, err = sc.SetStopHeight(2137, false)
    86  		require.Error(t, err)
    87  
    88  		execState.AssertExpectations(t)
    89  	})
    90  }
    91  
    92  // TestExecutionFallingBehind check if StopControl behaves properly even if EN runs behind
    93  // and blocks are finalized before they are executed
    94  func TestExecutionFallingBehind(t *testing.T) {
    95  
    96  	execState := new(mock.ReadOnlyExecutionState)
    97  
    98  	headerA := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(20))
    99  	headerB := unittest.BlockHeaderWithParentFixture(headerA) // 21
   100  	headerC := unittest.BlockHeaderWithParentFixture(headerB) // 22
   101  	headerD := unittest.BlockHeaderWithParentFixture(headerC) // 23
   102  
   103  	sc := NewStopControl(unittest.Logger(), false, 0)
   104  
   105  	require.Equal(t, sc.GetState(), StopControlOff)
   106  
   107  	// set stop at 22, so 21 is the last height which should be processed
   108  	_, _, err := sc.SetStopHeight(22, false)
   109  	require.NoError(t, err)
   110  	require.Equal(t, sc.GetState(), StopControlSet)
   111  
   112  	execState.On("StateCommitmentByBlockID", testifyMock.Anything, headerC.ParentID).Return(nil, storage.ErrNotFound)
   113  
   114  	// finalize blocks first
   115  	sc.blockFinalized(context.TODO(), execState, headerA)
   116  	require.Equal(t, StopControlSet, sc.GetState())
   117  
   118  	sc.blockFinalized(context.TODO(), execState, headerB)
   119  	require.Equal(t, StopControlSet, sc.GetState())
   120  
   121  	sc.blockFinalized(context.TODO(), execState, headerC)
   122  	require.Equal(t, StopControlSet, sc.GetState())
   123  
   124  	sc.blockFinalized(context.TODO(), execState, headerD)
   125  	require.Equal(t, StopControlSet, sc.GetState())
   126  
   127  	// simulate execution
   128  	sc.blockExecuted(headerA)
   129  	require.Equal(t, StopControlSet, sc.GetState())
   130  
   131  	sc.blockExecuted(headerB)
   132  	require.Equal(t, StopControlPaused, sc.GetState())
   133  
   134  	execState.AssertExpectations(t)
   135  }
   136  
   137  // TestCannotSetHeightBelowLastExecuted check if StopControl
   138  // tracks last executed height and prevents from setting stop height
   139  // below or too close to it
   140  func TestCannotSetHeightBelowLastExecuted(t *testing.T) {
   141  
   142  	sc := NewStopControl(unittest.Logger(), false, 0)
   143  
   144  	require.Equal(t, sc.GetState(), StopControlOff)
   145  
   146  	sc.executingBlockHeight(20)
   147  	require.Equal(t, StopControlOff, sc.GetState())
   148  
   149  	_, _, err := sc.SetStopHeight(20, false)
   150  	require.Error(t, err)
   151  	require.Equal(t, StopControlOff, sc.GetState())
   152  
   153  	_, _, err = sc.SetStopHeight(25, false)
   154  	require.NoError(t, err)
   155  	require.Equal(t, StopControlSet, sc.GetState())
   156  }
   157  
   158  // StopControl started as paused will keep the state
   159  func TestStartingPaused(t *testing.T) {
   160  
   161  	sc := NewStopControl(unittest.Logger(), true, 0)
   162  	require.Equal(t, StopControlPaused, sc.GetState())
   163  }
   164  
   165  func TestPausedStateRejectsAllBlocksAndChanged(t *testing.T) {
   166  
   167  	sc := NewStopControl(unittest.Logger(), true, 0)
   168  	require.Equal(t, StopControlPaused, sc.GetState())
   169  
   170  	_, _, err := sc.SetStopHeight(2137, true)
   171  	require.Error(t, err)
   172  
   173  	// make sure we don't even query executed status if paused
   174  	// mock should fail test on any method call
   175  	execState := new(mock.ReadOnlyExecutionState)
   176  
   177  	header := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(20))
   178  
   179  	sc.blockFinalized(context.TODO(), execState, header)
   180  	require.Equal(t, StopControlPaused, sc.GetState())
   181  
   182  	execState.AssertExpectations(t)
   183  }