github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/persist/fs/commitlog/commit_log_conc_test.go (about)

     1  // +build big
     2  //
     3  // Copyright (c) 2018 Uber Technologies, Inc.
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  package commitlog
    24  
    25  import (
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/dbnode/ts"
    30  	"github.com/m3db/m3/src/x/context"
    31  	xtime "github.com/m3db/m3/src/x/time"
    32  
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  // TestCommitLogActiveLogsConcurrency makes sure that the
    37  // ActiveLogs() API behaves correctly even while concurrent
    38  // writes are going on.
    39  func TestCommitLogActiveLogsConcurrency(t *testing.T) {
    40  	var (
    41  		opts, _ = newTestOptions(t, overrides{
    42  			strategy: StrategyWriteBehind,
    43  		})
    44  		numFilesRequired = 10
    45  	)
    46  
    47  	defer cleanup(t, opts)
    48  
    49  	var (
    50  		doneCh    = make(chan struct{})
    51  		commitLog = newTestCommitLog(t, opts)
    52  	)
    53  
    54  	// One goroutine continuously writing.
    55  	go func() {
    56  		for {
    57  			select {
    58  			case <-doneCh:
    59  				return
    60  			default:
    61  				time.Sleep(time.Millisecond)
    62  				err := commitLog.Write(
    63  					context.NewBackground(),
    64  					testSeries(t, opts, 0, "foo.bar", testTags1, 127),
    65  					ts.Datapoint{},
    66  					xtime.Second,
    67  					nil)
    68  				if err == errCommitLogClosed {
    69  					return
    70  				}
    71  				if err == ErrCommitLogQueueFull {
    72  					continue
    73  				}
    74  				if err != nil {
    75  					panic(err)
    76  				}
    77  			}
    78  		}
    79  	}()
    80  
    81  	// One goroutine continuously rotating the logs.
    82  	go func() {
    83  		for {
    84  			select {
    85  			case <-doneCh:
    86  				return
    87  			default:
    88  				time.Sleep(time.Millisecond)
    89  				_, err := commitLog.RotateLogs()
    90  				if err == errCommitLogClosed {
    91  					return
    92  				}
    93  				if err != nil {
    94  					panic(err)
    95  				}
    96  			}
    97  		}
    98  	}()
    99  
   100  	// One goroutine continuously checking active logs.
   101  	go func() {
   102  		var (
   103  			lastSeenFile string
   104  			numFilesSeen int
   105  		)
   106  		for numFilesSeen < numFilesRequired {
   107  			time.Sleep(100 * time.Millisecond)
   108  			logs, err := commitLog.ActiveLogs()
   109  			if err != nil {
   110  				panic(err)
   111  			}
   112  			require.Equal(t, 2, len(logs))
   113  			if logs[0].FilePath != lastSeenFile {
   114  				lastSeenFile = logs[0].FilePath
   115  				numFilesSeen++
   116  			}
   117  		}
   118  		close(doneCh)
   119  	}()
   120  
   121  	<-doneCh
   122  
   123  	require.NoError(t, commitLog.Close())
   124  }
   125  
   126  // TestCommitLogRotateLogsConcurrency makes sure that the
   127  // RotateLogs() API behaves correctly even while concurrent
   128  // writes are going on.
   129  func TestCommitLogRotateLogsConcurrency(t *testing.T) {
   130  	var (
   131  		opts, _ = newTestOptions(t, overrides{
   132  			strategy: StrategyWriteBehind,
   133  		})
   134  		numFilesRequired = 10
   135  	)
   136  
   137  	opts = opts.SetBlockSize(1 * time.Millisecond)
   138  	defer cleanup(t, opts)
   139  
   140  	var (
   141  		doneCh    = make(chan struct{})
   142  		commitLog = newTestCommitLog(t, opts)
   143  	)
   144  
   145  	// One goroutine continuously writing.
   146  	go func() {
   147  		for {
   148  			select {
   149  			case <-doneCh:
   150  				return
   151  			default:
   152  				time.Sleep(time.Millisecond)
   153  				err := commitLog.Write(
   154  					context.NewBackground(),
   155  					testSeries(t, opts, 0, "foo.bar", testTags1, 127),
   156  					ts.Datapoint{},
   157  					xtime.Second,
   158  					nil)
   159  				if err == errCommitLogClosed {
   160  					return
   161  				}
   162  				if err == ErrCommitLogQueueFull {
   163  					continue
   164  				}
   165  				if err != nil {
   166  					panic(err)
   167  				}
   168  			}
   169  		}
   170  	}()
   171  
   172  	// One goroutine continuously rotating logs.
   173  	go func() {
   174  		var (
   175  			lastSeenFile string
   176  			numFilesSeen int
   177  		)
   178  		for numFilesSeen < numFilesRequired {
   179  			time.Sleep(100 * time.Millisecond)
   180  			file, err := commitLog.RotateLogs()
   181  			if err != nil {
   182  				panic(err)
   183  			}
   184  			if file.FilePath != lastSeenFile {
   185  				lastSeenFile = file.FilePath
   186  				numFilesSeen++
   187  			}
   188  		}
   189  		close(doneCh)
   190  	}()
   191  
   192  	<-doneCh
   193  
   194  	require.NoError(t, commitLog.Close())
   195  }