github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/shard_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 //go:build integrationTest 13 // +build integrationTest 14 15 package db 16 17 import ( 18 "context" 19 "crypto/rand" 20 "encoding/json" 21 "fmt" 22 "os" 23 "path" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "github.com/weaviate/weaviate/adapters/repos/db/lsmkv" 31 "github.com/weaviate/weaviate/entities/additional" 32 "github.com/weaviate/weaviate/entities/storagestate" 33 "github.com/weaviate/weaviate/entities/storobj" 34 ) 35 36 func TestShard_UpdateStatus(t *testing.T) { 37 ctx := testCtx() 38 className := "TestClass" 39 shd, idx := testShard(t, ctx, className) 40 41 amount := 10 42 43 defer func(path string) { 44 err := os.RemoveAll(path) 45 if err != nil { 46 fmt.Println(err) 47 } 48 }(shd.Index().Config.RootPath) 49 50 t.Run("insert data into shard", func(t *testing.T) { 51 for i := 0; i < amount; i++ { 52 obj := testObject(className) 53 54 err := shd.PutObject(ctx, obj) 55 require.Nil(t, err) 56 } 57 58 objs, err := shd.ObjectList(ctx, amount, nil, nil, additional.Properties{}, shd.Index().Config.ClassName) 59 require.Nil(t, err) 60 require.Equal(t, amount, len(objs)) 61 }) 62 63 t.Run("mark shard readonly and fail to insert", func(t *testing.T) { 64 err := shd.UpdateStatus(storagestate.StatusReadOnly.String()) 65 require.Nil(t, err) 66 67 err = shd.PutObject(ctx, testObject(className)) 68 require.EqualError(t, err, storagestate.ErrStatusReadOnly.Error()) 69 }) 70 71 t.Run("mark shard ready and insert successfully", func(t *testing.T) { 72 err := shd.UpdateStatus(storagestate.StatusReady.String()) 73 require.Nil(t, err) 74 75 err = shd.PutObject(ctx, testObject(className)) 76 require.Nil(t, err) 77 }) 78 79 require.Nil(t, idx.drop()) 80 require.Nil(t, os.RemoveAll(idx.Config.RootPath)) 81 } 82 83 func TestShard_ReadOnly_HaltCompaction(t *testing.T) { 84 amount := 10000 85 sizePerValue := 8 86 bucketName := "testbucket" 87 88 keys := make([][]byte, amount) 89 values := make([][]byte, amount) 90 91 shd, idx := testShard(t, context.Background(), "TestClass") 92 93 defer func(path string) { 94 err := os.RemoveAll(path) 95 if err != nil { 96 fmt.Println(err) 97 } 98 }(shd.Index().Config.RootPath) 99 100 err := shd.Store().CreateOrLoadBucket(context.Background(), bucketName, 101 lsmkv.WithMemtableThreshold(1024)) 102 require.Nil(t, err) 103 104 bucket := shd.Store().Bucket(bucketName) 105 require.NotNil(t, bucket) 106 dirName := path.Join(shd.Index().path(), shd.Name(), "lsm", bucketName) 107 108 t.Run("generate random data", func(t *testing.T) { 109 for i := range keys { 110 n, err := json.Marshal(i) 111 require.Nil(t, err) 112 113 keys[i] = n 114 values[i] = make([]byte, sizePerValue) 115 rand.Read(values[i]) 116 } 117 }) 118 119 t.Run("insert data into bucket", func(t *testing.T) { 120 for i := range keys { 121 err := bucket.Put(keys[i], values[i]) 122 assert.Nil(t, err) 123 time.Sleep(time.Microsecond) 124 } 125 126 t.Logf("insertion complete!") 127 }) 128 129 t.Run("halt compaction with readonly status", func(t *testing.T) { 130 err := shd.UpdateStatus(storagestate.StatusReadOnly.String()) 131 require.Nil(t, err) 132 133 // give the status time to propagate 134 // before grabbing the baseline below 135 time.Sleep(time.Second) 136 137 // once shard status is set to readonly, 138 // the number of segment files should 139 // not change 140 entries, err := os.ReadDir(dirName) 141 require.Nil(t, err) 142 numSegments := len(entries) 143 144 // if the number of segments remain the 145 // same for 30 seconds, we can be 146 // reasonably sure that the compaction 147 // process was halted 148 for i := 0; i < 30; i++ { 149 entries, err := os.ReadDir(dirName) 150 require.Nil(t, err) 151 152 require.Equal(t, numSegments, len(entries)) 153 t.Logf("iteration %d, sleeping", i) 154 time.Sleep(time.Second) 155 } 156 }) 157 158 t.Run("update shard status to ready", func(t *testing.T) { 159 err := shd.UpdateStatus(storagestate.StatusReady.String()) 160 require.Nil(t, err) 161 162 time.Sleep(time.Second) 163 }) 164 165 require.Nil(t, idx.drop()) 166 } 167 168 // tests adding multiple larger batches in parallel using different settings of the goroutine factor. 169 // In all cases all objects should be added 170 func TestShard_ParallelBatches(t *testing.T) { 171 r := getRandomSeed() 172 batches := make([][]*storobj.Object, 4) 173 for i := range batches { 174 batches[i] = createRandomObjects(r, "TestClass", 1000) 175 } 176 totalObjects := 1000 * len(batches) 177 ctx := testCtx() 178 shd, idx := testShard(t, context.Background(), "TestClass") 179 180 // add batches in parallel 181 wg := sync.WaitGroup{} 182 wg.Add(len(batches)) 183 for _, batch := range batches { 184 go func(localBatch []*storobj.Object) { 185 shd.PutObjectBatch(ctx, localBatch) 186 wg.Done() 187 }(batch) 188 } 189 wg.Wait() 190 191 require.Equal(t, totalObjects, int(shd.Counter().Get())) 192 require.Nil(t, idx.drop()) 193 }