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  }