github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitree/bdb/simulation_test.go (about)

     1  // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bdb_test
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"math/rand"
    21  	"sync"
    22  	"sync/atomic"
    23  	"testing"
    24  
    25  	"github.com/cockroachdb/errors"
    26  	"github.com/zuoyebang/bitalosdb/bitree/bdb"
    27  	"github.com/zuoyebang/bitalosdb/internal/options"
    28  )
    29  
    30  func TestSimulate_1op_1p(t *testing.T)     { testSimulate(t, nil, 1, 1, 1) }
    31  func TestSimulate_10op_1p(t *testing.T)    { testSimulate(t, nil, 1, 10, 1) }
    32  func TestSimulate_100op_1p(t *testing.T)   { testSimulate(t, nil, 1, 100, 1) }
    33  func TestSimulate_1000op_1p(t *testing.T)  { testSimulate(t, nil, 1, 1000, 1) }
    34  func TestSimulate_10000op_1p(t *testing.T) { testSimulate(t, nil, 1, 10000, 1) }
    35  
    36  func TestSimulate_10op_10p(t *testing.T)    { testSimulate(t, nil, 1, 10, 10) }
    37  func TestSimulate_100op_10p(t *testing.T)   { testSimulate(t, nil, 1, 100, 10) }
    38  func TestSimulate_1000op_10p(t *testing.T)  { testSimulate(t, nil, 1, 1000, 10) }
    39  func TestSimulate_10000op_10p(t *testing.T) { testSimulate(t, nil, 1, 10000, 10) }
    40  
    41  func TestSimulate_100op_100p(t *testing.T)   { testSimulate(t, nil, 1, 100, 100) }
    42  func TestSimulate_1000op_100p(t *testing.T)  { testSimulate(t, nil, 1, 1000, 100) }
    43  func TestSimulate_10000op_100p(t *testing.T) { testSimulate(t, nil, 1, 10000, 100) }
    44  
    45  func TestSimulate_10000op_1000p(t *testing.T) { testSimulate(t, nil, 1, 10000, 1000) }
    46  
    47  func testSimulate(t *testing.T, openOption *options.BdbOptions, round, threadCount, parallelism int) {
    48  	if testing.Short() {
    49  		t.Skip("skipping test in short mode.")
    50  	}
    51  
    52  	rand.Seed(int64(qseed))
    53  
    54  	var readerHandlers = []simulateHandler{simulateGetHandler}
    55  	var writerHandlers = []simulateHandler{simulateGetHandler, simulatePutHandler}
    56  
    57  	var versions = make(map[int]*QuickDB)
    58  	versions[1] = NewQuickDB()
    59  
    60  	db := MustOpenWithOption(openOption)
    61  	defer db.MustClose()
    62  
    63  	var mutex sync.Mutex
    64  
    65  	for n := 0; n < round; n++ {
    66  		var threads = make(chan bool, parallelism)
    67  		var wg sync.WaitGroup
    68  
    69  		var opCount int64
    70  
    71  		var igCount int64
    72  
    73  		var errCh = make(chan error, threadCount)
    74  
    75  		var i int
    76  		for {
    77  			threads <- true
    78  			wg.Add(1)
    79  			writable := ((rand.Int() % 100) < 20)
    80  			var handler simulateHandler
    81  			if writable {
    82  				handler = writerHandlers[rand.Intn(len(writerHandlers))]
    83  			} else {
    84  				handler = readerHandlers[rand.Intn(len(readerHandlers))]
    85  			}
    86  
    87  			go func(writable bool, handler simulateHandler) {
    88  				defer wg.Done()
    89  				atomic.AddInt64(&opCount, 1)
    90  				tx, err := db.Begin(writable)
    91  				if err != nil {
    92  					errCh <- errors.Errorf("error tx begin: %v", err)
    93  					return
    94  				}
    95  
    96  				mutex.Lock()
    97  				var qdb = versions[tx.ID()]
    98  				if writable {
    99  					qdb = versions[tx.ID()-1].Copy()
   100  				}
   101  				mutex.Unlock()
   102  
   103  				if writable {
   104  					defer func() {
   105  						mutex.Lock()
   106  						versions[tx.ID()] = qdb
   107  						mutex.Unlock()
   108  
   109  						if err := tx.Commit(); err != nil {
   110  							errCh <- err
   111  							return
   112  						}
   113  					}()
   114  				} else {
   115  					defer func() { _ = tx.Rollback() }()
   116  				}
   117  
   118  				if qdb == nil {
   119  					atomic.AddInt64(&igCount, 1)
   120  					return
   121  				}
   122  
   123  				handler(tx, qdb)
   124  
   125  				<-threads
   126  			}(writable, handler)
   127  
   128  			i++
   129  			if i >= threadCount {
   130  				break
   131  			}
   132  		}
   133  
   134  		wg.Wait()
   135  		close(errCh)
   136  		for err := range errCh {
   137  			if err != nil {
   138  				t.Fatalf("error from inside goroutine: %v", err)
   139  			}
   140  		}
   141  
   142  		db.MustClose()
   143  		db.MustReopen()
   144  	}
   145  
   146  }
   147  
   148  type simulateHandler func(tx *bdb.Tx, qdb *QuickDB)
   149  
   150  func simulateGetHandler(tx *bdb.Tx, qdb *QuickDB) {
   151  	keys := qdb.Rand()
   152  	if len(keys) == 0 {
   153  		return
   154  	}
   155  
   156  	b := tx.Bucket(keys[0])
   157  	if b == nil {
   158  		panic(fmt.Sprintf("bucket[0] expected: %08x\n", trunc(keys[0], 4)))
   159  	}
   160  
   161  	for _, key := range keys[1 : len(keys)-1] {
   162  		b = b.Bucket(key)
   163  		if b == nil {
   164  			panic(fmt.Sprintf("bucket[n] expected: %v -> %v\n", keys, key))
   165  		}
   166  	}
   167  
   168  	expected := qdb.Get(keys)
   169  	actual := b.Get(keys[len(keys)-1])
   170  	if !bytes.Equal(actual, expected) {
   171  		fmt.Println("=== EXPECTED ===")
   172  		fmt.Println(expected)
   173  		fmt.Println("=== ACTUAL ===")
   174  		fmt.Println(actual)
   175  		fmt.Println("=== END ===")
   176  		panic("value mismatch")
   177  	}
   178  }
   179  
   180  func simulatePutHandler(tx *bdb.Tx, qdb *QuickDB) {
   181  	var err error
   182  	keys, value := randKeys(), randValue()
   183  
   184  	b := tx.Bucket(keys[0])
   185  	if b == nil {
   186  		b, err = tx.CreateBucket(keys[0])
   187  		if err != nil {
   188  			panic("create bucket: " + err.Error())
   189  		}
   190  	}
   191  
   192  	for _, key := range keys[1 : len(keys)-1] {
   193  		child := b.Bucket(key)
   194  		if child != nil {
   195  			b = child
   196  		} else {
   197  			b, err = b.CreateBucket(key)
   198  			if err != nil {
   199  				panic("create bucket: " + err.Error())
   200  			}
   201  		}
   202  	}
   203  
   204  	if err := b.Put(keys[len(keys)-1], value); err != nil {
   205  		panic("put: " + err.Error())
   206  	}
   207  
   208  	qdb.Put(keys, value)
   209  }
   210  
   211  type QuickDB struct {
   212  	sync.RWMutex
   213  	m map[string]interface{}
   214  }
   215  
   216  func NewQuickDB() *QuickDB {
   217  	return &QuickDB{m: make(map[string]interface{})}
   218  }
   219  
   220  func (db *QuickDB) Get(keys [][]byte) []byte {
   221  	db.RLock()
   222  	defer db.RUnlock()
   223  
   224  	m := db.m
   225  	for _, key := range keys[:len(keys)-1] {
   226  		value := m[string(key)]
   227  		if value == nil {
   228  			return nil
   229  		}
   230  		switch value := value.(type) {
   231  		case map[string]interface{}:
   232  			m = value
   233  		case []byte:
   234  			return nil
   235  		}
   236  	}
   237  
   238  	if value, ok := m[string(keys[len(keys)-1])].([]byte); ok {
   239  		return value
   240  	}
   241  	return nil
   242  }
   243  
   244  func (db *QuickDB) Put(keys [][]byte, value []byte) {
   245  	db.Lock()
   246  	defer db.Unlock()
   247  
   248  	m := db.m
   249  	for _, key := range keys[:len(keys)-1] {
   250  		if _, ok := m[string(key)].([]byte); ok {
   251  			return
   252  		}
   253  
   254  		if m[string(key)] == nil {
   255  			m[string(key)] = make(map[string]interface{})
   256  		}
   257  		m = m[string(key)].(map[string]interface{})
   258  	}
   259  
   260  	m[string(keys[len(keys)-1])] = value
   261  }
   262  
   263  func (db *QuickDB) Rand() [][]byte {
   264  	db.RLock()
   265  	defer db.RUnlock()
   266  	if len(db.m) == 0 {
   267  		return nil
   268  	}
   269  	var keys [][]byte
   270  	db.rand(db.m, &keys)
   271  	return keys
   272  }
   273  
   274  func (db *QuickDB) rand(m map[string]interface{}, keys *[][]byte) {
   275  	i, index := 0, rand.Intn(len(m))
   276  	for k, v := range m {
   277  		if i == index {
   278  			*keys = append(*keys, []byte(k))
   279  			if v, ok := v.(map[string]interface{}); ok {
   280  				db.rand(v, keys)
   281  			}
   282  			return
   283  		}
   284  		i++
   285  	}
   286  	panic("quickdb rand: out-of-range")
   287  }
   288  
   289  func (db *QuickDB) Copy() *QuickDB {
   290  	db.RLock()
   291  	defer db.RUnlock()
   292  	return &QuickDB{m: db.copy(db.m)}
   293  }
   294  
   295  func (db *QuickDB) copy(m map[string]interface{}) map[string]interface{} {
   296  	clone := make(map[string]interface{}, len(m))
   297  	for k, v := range m {
   298  		switch v := v.(type) {
   299  		case map[string]interface{}:
   300  			clone[k] = db.copy(v)
   301  		default:
   302  			clone[k] = v
   303  		}
   304  	}
   305  	return clone
   306  }
   307  
   308  func randKey() []byte {
   309  	var min, max = 1, 1024
   310  	n := rand.Intn(max-min) + min
   311  	b := make([]byte, n)
   312  	for i := 0; i < n; i++ {
   313  		b[i] = byte(rand.Intn(255))
   314  	}
   315  	return b
   316  }
   317  
   318  func randKeys() [][]byte {
   319  	var keys [][]byte
   320  	var count = rand.Intn(2) + 2
   321  	for i := 0; i < count; i++ {
   322  		keys = append(keys, randKey())
   323  	}
   324  	return keys
   325  }
   326  
   327  func randValue() []byte {
   328  	n := rand.Intn(8192)
   329  	b := make([]byte, n)
   330  	for i := 0; i < n; i++ {
   331  		b[i] = byte(rand.Intn(255))
   332  	}
   333  	return b
   334  }