github.com/weaviate/weaviate@v1.24.6/test/helper/journey/backup_journey.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  package journey
    13  
    14  import (
    15  	"math/rand"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	"github.com/weaviate/weaviate/entities/backup"
    22  	"github.com/weaviate/weaviate/entities/models"
    23  	"github.com/weaviate/weaviate/entities/schema"
    24  	"github.com/weaviate/weaviate/test/helper"
    25  	moduleshelper "github.com/weaviate/weaviate/test/helper/modules"
    26  )
    27  
    28  type journeyType int
    29  
    30  const (
    31  	singleNodeJourney journeyType = iota
    32  	clusterJourney
    33  )
    34  
    35  type dataIntegrityCheck int
    36  
    37  const (
    38  	checkClassPresenceOnly = iota
    39  	checkClassAndDataPresence
    40  )
    41  
    42  const (
    43  	singleTenant = ""
    44  	multiTenant  = true
    45  )
    46  
    47  func backupJourney(t *testing.T, className, backend, backupID string,
    48  	journeyType journeyType, dataIntegrityCheck dataIntegrityCheck,
    49  	tenantNames []string, pqEnabled bool,
    50  ) {
    51  	if journeyType == clusterJourney && backend == "filesystem" {
    52  		t.Run("should fail backup/restore with local filesystem backend", func(t *testing.T) {
    53  			backupResp, err := helper.CreateBackup(t, helper.DefaultBackupConfig(), className, backend, backupID)
    54  			assert.Nil(t, backupResp)
    55  			assert.Error(t, err)
    56  
    57  			restoreResp, err := helper.RestoreBackup(t, helper.DefaultRestoreConfig(), className, backend, backupID, map[string]string{})
    58  			assert.Nil(t, restoreResp)
    59  			assert.Error(t, err)
    60  		})
    61  		return
    62  	}
    63  
    64  	t.Run("create backup", func(t *testing.T) {
    65  		resp, err := helper.CreateBackup(t, helper.DefaultBackupConfig(), className, backend, backupID)
    66  		helper.AssertRequestOk(t, resp, err, nil)
    67  		// wait for create success
    68  		createTime := time.Now()
    69  		for {
    70  			if time.Now().After(createTime.Add(time.Minute)) {
    71  				break
    72  			}
    73  
    74  			resp, err := helper.CreateBackupStatus(t, backend, backupID)
    75  			helper.AssertRequestOk(t, resp, err, func() {
    76  				require.NotNil(t, resp)
    77  				require.NotNil(t, resp.Payload)
    78  				require.NotNil(t, resp.Payload.Status)
    79  			})
    80  
    81  			if *resp.Payload.Status == string(backup.Success) {
    82  				break
    83  			}
    84  			time.Sleep(time.Second * 1)
    85  		}
    86  
    87  		statusResp, err := helper.CreateBackupStatus(t, backend, backupID)
    88  		helper.AssertRequestOk(t, resp, err, func() {
    89  			require.NotNil(t, statusResp)
    90  			require.NotNil(t, statusResp.Payload)
    91  			require.NotNil(t, statusResp.Payload.Status)
    92  		})
    93  
    94  		require.Equal(t, *statusResp.Payload.Status,
    95  			string(backup.Success), statusResp.Payload.Error)
    96  	})
    97  
    98  	t.Run("delete class for restoration", func(t *testing.T) {
    99  		helper.DeleteClass(t, className)
   100  	})
   101  
   102  	t.Run("restore backup", func(t *testing.T) {
   103  		_, err := helper.RestoreBackup(t, helper.DefaultRestoreConfig(), className, backend, backupID, map[string]string{})
   104  		require.Nil(t, err, "expected nil, got: %v", err)
   105  
   106  		// wait for restore success
   107  		restoreTime := time.Now()
   108  		for {
   109  			if time.Now().After(restoreTime.Add(time.Minute)) {
   110  				break
   111  			}
   112  
   113  			resp, err := helper.RestoreBackupStatus(t, backend, backupID)
   114  			helper.AssertRequestOk(t, resp, err, func() {
   115  				require.NotNil(t, resp)
   116  				require.NotNil(t, resp.Payload)
   117  				require.NotNil(t, resp.Payload.Status)
   118  			})
   119  
   120  			if *resp.Payload.Status == string(backup.Success) {
   121  				break
   122  			}
   123  
   124  			time.Sleep(time.Second)
   125  		}
   126  
   127  		statusResp, err := helper.RestoreBackupStatus(t, backend, backupID)
   128  		helper.AssertRequestOk(t, statusResp, err, func() {
   129  			require.NotNil(t, statusResp)
   130  			require.NotNil(t, statusResp.Payload)
   131  			require.NotNil(t, statusResp.Payload.Status)
   132  		})
   133  
   134  		require.Equal(t, *statusResp.Payload.Status, string(backup.Success))
   135  	})
   136  
   137  	// assert class exists again it its entirety
   138  	if tenantNames != nil {
   139  		for _, name := range tenantNames {
   140  			moduleshelper.EnsureClassExists(t, className, name)
   141  			if dataIntegrityCheck == checkClassAndDataPresence {
   142  				count := moduleshelper.GetClassCount(t, className, name)
   143  				assert.Equal(t, int64(500/len(tenantNames)), count)
   144  			}
   145  		}
   146  	} else {
   147  		moduleshelper.EnsureClassExists(t, className, singleTenant)
   148  		if dataIntegrityCheck == checkClassAndDataPresence {
   149  			count := moduleshelper.GetClassCount(t, className, singleTenant)
   150  			assert.Equal(t, int64(500), count)
   151  			if pqEnabled {
   152  				moduleshelper.EnsureCompressedVectorsRestored(t, className)
   153  			}
   154  		}
   155  	}
   156  }
   157  
   158  func nodeMappingBackupJourney_Backup(t *testing.T, className, backend, backupID string, tenantNames []string,
   159  ) {
   160  	t.Run("create backup", func(t *testing.T) {
   161  		resp, err := helper.CreateBackup(t, helper.DefaultBackupConfig(), className, backend, backupID)
   162  		helper.AssertRequestOk(t, resp, err, nil)
   163  		// wait for create success
   164  		createTime := time.Now()
   165  		for {
   166  			if time.Now().After(createTime.Add(time.Minute)) {
   167  				break
   168  			}
   169  
   170  			resp, err := helper.CreateBackupStatus(t, backend, backupID)
   171  			helper.AssertRequestOk(t, resp, err, func() {
   172  				require.NotNil(t, resp)
   173  				require.NotNil(t, resp.Payload)
   174  				require.NotNil(t, resp.Payload.Status)
   175  			})
   176  
   177  			if *resp.Payload.Status == string(backup.Success) {
   178  				break
   179  			}
   180  			time.Sleep(time.Second * 1)
   181  		}
   182  
   183  		statusResp, err := helper.CreateBackupStatus(t, backend, backupID)
   184  		helper.AssertRequestOk(t, resp, err, func() {
   185  			require.NotNil(t, statusResp)
   186  			require.NotNil(t, statusResp.Payload)
   187  			require.NotNil(t, statusResp.Payload.Status)
   188  		})
   189  
   190  		require.Equal(t, *statusResp.Payload.Status, string(backup.Success))
   191  	})
   192  }
   193  
   194  func nodeMappingBackupJourney_Restore(t *testing.T, className, backend, backupID string, tenantNames []string, nodeMapping map[string]string) {
   195  	t.Run("restore backup", func(t *testing.T) {
   196  		_, err := helper.RestoreBackup(t, helper.DefaultRestoreConfig(), className, backend, backupID, nodeMapping)
   197  		require.Nil(t, err, "expected nil, got: %v", err)
   198  
   199  		// wait for restore success
   200  		restoreTime := time.Now()
   201  		for {
   202  			if time.Now().After(restoreTime.Add(time.Minute)) {
   203  				break
   204  			}
   205  
   206  			resp, err := helper.RestoreBackupStatus(t, backend, backupID)
   207  			helper.AssertRequestOk(t, resp, err, func() {
   208  				require.NotNil(t, resp)
   209  				require.NotNil(t, resp.Payload)
   210  				require.NotNil(t, resp.Payload.Status)
   211  			})
   212  
   213  			if *resp.Payload.Status == string(backup.Success) {
   214  				break
   215  			}
   216  
   217  			time.Sleep(time.Second)
   218  		}
   219  
   220  		statusResp, err := helper.RestoreBackupStatus(t, backend, backupID)
   221  		helper.AssertRequestOk(t, statusResp, err, func() {
   222  			require.NotNil(t, statusResp)
   223  			require.NotNil(t, statusResp.Payload)
   224  			require.NotNil(t, statusResp.Payload.Status)
   225  		})
   226  
   227  		require.Equal(t, *statusResp.Payload.Status, string(backup.Success))
   228  	})
   229  
   230  	// assert class exists again it its entirety
   231  	if tenantNames != nil {
   232  		for _, name := range tenantNames {
   233  			count := moduleshelper.GetClassCount(t, className, name)
   234  			assert.Equal(t, int64(500/len(tenantNames)), count)
   235  		}
   236  	} else {
   237  		count := moduleshelper.GetClassCount(t, className, singleTenant)
   238  		assert.Equal(t, int64(500), count)
   239  	}
   240  }
   241  
   242  func addTestClass(t *testing.T, className string, multiTenant bool) {
   243  	class := &models.Class{
   244  		Class: className,
   245  		ModuleConfig: map[string]interface{}{
   246  			"text2vec-contextionary": map[string]interface{}{
   247  				"vectorizeClassName": true,
   248  			},
   249  		},
   250  		Properties: []*models.Property{
   251  			{
   252  				Name:         "contents",
   253  				DataType:     schema.DataTypeText.PropString(),
   254  				Tokenization: models.PropertyTokenizationWhitespace,
   255  			},
   256  		},
   257  	}
   258  
   259  	if multiTenant {
   260  		class.MultiTenancyConfig = &models.MultiTenancyConfig{
   261  			Enabled: true,
   262  		}
   263  	}
   264  
   265  	helper.CreateClass(t, class)
   266  }
   267  
   268  func addTestObjects(t *testing.T, className string, tenantNames []string) {
   269  	const (
   270  		noteLengthMin = 4
   271  		noteLengthMax = 1024
   272  
   273  		batchSize  = 10
   274  		numBatches = 50
   275  	)
   276  
   277  	seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
   278  	multiTenant := len(tenantNames) > 0
   279  
   280  	for i := 0; i < numBatches; i++ {
   281  		batch := make([]*models.Object, batchSize)
   282  		for j := 0; j < batchSize; j++ {
   283  			contentsLength := noteLengthMin + seededRand.Intn(noteLengthMax-noteLengthMin+1)
   284  			contents := helper.GetRandomString(contentsLength)
   285  
   286  			obj := models.Object{
   287  				Class:      className,
   288  				Properties: map[string]interface{}{"contents": contents},
   289  			}
   290  			if multiTenant {
   291  				obj.Tenant = tenantNames[i]
   292  			}
   293  			batch[j] = &obj
   294  		}
   295  		helper.CreateObjectsBatch(t, batch)
   296  
   297  	}
   298  }