github.com/decred/dcrlnd@v0.7.6/kvdb/etcd/commit_queue_test.go (about)

     1  //go:build kvdb_etcd
     2  // +build kvdb_etcd
     3  
     4  package etcd
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  	"sync/atomic"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  // TestCommitQueue tests that non-conflicting transactions commit concurrently,
    17  // while conflicting transactions are queued up.
    18  func TestCommitQueue(t *testing.T) {
    19  	// The duration of each commit.
    20  	const commitDuration = time.Millisecond * 500
    21  	const numCommits = 5
    22  
    23  	var wg sync.WaitGroup
    24  	commits := make([]string, numCommits)
    25  	idx := int32(-1)
    26  
    27  	commit := func(tag string, sleep bool) func() {
    28  		return func() {
    29  			defer wg.Done()
    30  
    31  			// Update our log of commit order. Avoid blocking
    32  			// by preallocating the commit log and increasing
    33  			// the log index atomically.
    34  			if sleep {
    35  				time.Sleep(commitDuration)
    36  			}
    37  
    38  			i := atomic.AddInt32(&idx, 1)
    39  			commits[i] = tag
    40  		}
    41  	}
    42  
    43  	ctx := context.Background()
    44  	ctx, cancel := context.WithCancel(ctx)
    45  	q := NewCommitQueue(ctx)
    46  	defer q.Stop()
    47  	defer cancel()
    48  
    49  	wg.Add(numCommits)
    50  	t1 := time.Now()
    51  
    52  	// Tx1 (long): reads: key1, key2, writes: key3, conflict: none
    53  	q.Add(
    54  		commit("free", true),
    55  		[]string{"key1", "key2"},
    56  		[]string{"key3"},
    57  	)
    58  	// Tx2: reads: key1, key2, writes: key3, conflict: Tx1
    59  	q.Add(
    60  		commit("blocked1", false),
    61  		[]string{"key1", "key2"},
    62  		[]string{"key3"},
    63  	)
    64  	// Tx3 (long): reads: key1, writes: key4, conflict: none
    65  	q.Add(
    66  		commit("free", true),
    67  		[]string{"key1", "key2"},
    68  		[]string{"key4"},
    69  	)
    70  	// Tx4 (long): reads: key1, writes: none, conflict: none
    71  	q.Add(
    72  		commit("free", true),
    73  		[]string{"key1", "key2"},
    74  		[]string{},
    75  	)
    76  	// Tx4: reads: key2, writes: key4 conflict: Tx3
    77  	q.Add(
    78  		commit("blocked2", false),
    79  		[]string{"key2"},
    80  		[]string{"key4"},
    81  	)
    82  
    83  	// Wait for all commits.
    84  	wg.Wait()
    85  	t2 := time.Now()
    86  
    87  	// Expected total execution time: delta.
    88  	// 2 * commitDuration <= delta < 3 * commitDuration
    89  	delta := t2.Sub(t1)
    90  	require.LessOrEqual(t, int64(commitDuration*2), int64(delta))
    91  	require.Greater(t, int64(commitDuration*3), int64(delta))
    92  
    93  	// Expect that the non-conflicting "free" transactions are executed
    94  	// before the blocking ones, and the blocking ones are executed in
    95  	// the order of addition.
    96  	require.Equal(t,
    97  		[]string{"free", "blocked1", "free", "free", "blocked2"},
    98  		commits,
    99  	)
   100  }