github.com/weaviate/weaviate@v1.24.6/test/acceptance/replication/multi_tenancy_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 package replication 13 14 import ( 15 "context" 16 "encoding/json" 17 "fmt" 18 "testing" 19 "time" 20 21 "github.com/go-openapi/strfmt" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "github.com/weaviate/weaviate/client/objects" 25 "github.com/weaviate/weaviate/entities/models" 26 "github.com/weaviate/weaviate/entities/schema/crossref" 27 "github.com/weaviate/weaviate/test/docker" 28 "github.com/weaviate/weaviate/test/helper" 29 "github.com/weaviate/weaviate/test/helper/sample-schema/articles" 30 "github.com/weaviate/weaviate/usecases/replica" 31 ) 32 33 const ( 34 tenantID = strfmt.UUID("45e9e17e-8102-4011-95f0-3079ca188bbf") 35 ) 36 37 func multiTenancyEnabled(t *testing.T) { 38 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) 39 defer cancel() 40 41 compose, err := docker.New(). 42 WithWeaviateCluster(). 43 WithText2VecContextionary(). 44 Start(ctx) 45 require.Nil(t, err) 46 defer func() { 47 if err := compose.Terminate(ctx); err != nil { 48 t.Fatalf("failed to terminate test containers: %s", err.Error()) 49 } 50 }() 51 52 helper.SetupClient(compose.GetWeaviate().URI()) 53 paragraphClass := articles.ParagraphsClass() 54 articleClass := articles.ArticlesClass() 55 56 t.Run("create schema", func(t *testing.T) { 57 paragraphClass.ReplicationConfig = &models.ReplicationConfig{ 58 Factor: 2, 59 } 60 paragraphClass.MultiTenancyConfig = &models.MultiTenancyConfig{ 61 Enabled: true, 62 } 63 helper.CreateClass(t, paragraphClass) 64 articleClass.ReplicationConfig = &models.ReplicationConfig{ 65 Factor: 2, 66 } 67 articleClass.MultiTenancyConfig = &models.MultiTenancyConfig{ 68 Enabled: true, 69 } 70 helper.CreateClass(t, articleClass) 71 }) 72 73 t.Run("add tenants", func(t *testing.T) { 74 tenants := []*models.Tenant{{Name: tenantID.String()}} 75 helper.CreateTenants(t, paragraphClass.Class, tenants) 76 helper.CreateTenants(t, articleClass.Class, tenants) 77 }) 78 79 t.Run("insert paragraphs batch", func(t *testing.T) { 80 t.Run("create objects on node 1", func(t *testing.T) { 81 batch := make([]*models.Object, len(paragraphIDs)) 82 for i, id := range paragraphIDs { 83 batch[i] = articles.NewParagraph(). 84 WithID(id). 85 WithContents(fmt.Sprintf("paragraph#%d", i)). 86 WithTenant(tenantID.String()). 87 Object() 88 } 89 createTenantObjects(t, compose.GetWeaviate().URI(), batch) 90 }) 91 92 t.Run("stop node 1", func(t *testing.T) { 93 stopNode(ctx, t, compose, compose.GetWeaviate().Name()) 94 }) 95 96 t.Run("assert objects exist on node 2", func(t *testing.T) { 97 count := countTenantObjects(t, compose.GetWeaviateNode2().URI(), 98 "Paragraph", tenantID.String()) 99 assert.Equal(t, int64(len(paragraphIDs)), count) 100 }) 101 102 t.Run("restart node 1", func(t *testing.T) { 103 restartNode1(ctx, t, compose) 104 }) 105 }) 106 107 t.Run("insert articles individually", func(t *testing.T) { 108 t.Run("create objects on node 2", func(t *testing.T) { 109 for i, id := range articleIDs { 110 obj := articles.NewArticle(). 111 WithID(id). 112 WithTitle(fmt.Sprintf("Article#%d", i)). 113 WithTenant(tenantID.String()). 114 Object() 115 createTenantObject(t, compose.GetWeaviateNode2().URI(), obj) 116 } 117 }) 118 119 t.Run("stop node 2", func(t *testing.T) { 120 stopNode(ctx, t, compose, compose.GetWeaviateNode2().Name()) 121 }) 122 123 t.Run("assert objects exist on node 1", func(t *testing.T) { 124 count := countTenantObjects(t, compose.GetWeaviate().URI(), 125 "Article", tenantID.String()) 126 assert.Equal(t, int64(len(articleIDs)), count) 127 }) 128 129 t.Run("restart node 2", func(t *testing.T) { 130 err = compose.Start(ctx, compose.GetWeaviateNode2().Name()) 131 require.Nil(t, err) 132 }) 133 }) 134 135 t.Run("add references", func(t *testing.T) { 136 refs := make([]*models.BatchReference, len(articleIDs)) 137 for i := range articleIDs { 138 refs[i] = &models.BatchReference{ 139 From: strfmt.URI(crossref.NewSource("Article", "hasParagraphs", articleIDs[i]).String()), 140 To: strfmt.URI(crossref.NewLocalhost("Paragraph", paragraphIDs[i]).String()), 141 Tenant: tenantID.String(), 142 } 143 } 144 145 t.Run("add references to node 1", func(t *testing.T) { 146 addTenantReferences(t, compose.GetWeaviate().URI(), refs) 147 }) 148 149 t.Run("stop node 1", func(t *testing.T) { 150 stopNode(ctx, t, compose, compose.GetWeaviate().Name()) 151 }) 152 153 t.Run("assert references were added successfully to node 2", func(t *testing.T) { 154 type additional struct { 155 ID strfmt.UUID `json:"id"` 156 } 157 158 type article struct { 159 Additional additional `json:"_additional"` 160 HasParagraphs []struct { 161 Additional additional `json:"_additional"` 162 } `json:"hasParagraphs"` 163 } 164 165 // maps article id to referenced paragraph id 166 refPairs := make(map[strfmt.UUID]strfmt.UUID) 167 resp := gqlTenantGet(t, compose.GetWeaviateNode2().URI(), "Article", replica.One, 168 tenantID.String(), "_additional{id}", "hasParagraphs {... on Paragraph {_additional{id}}}") 169 assert.Len(t, resp, len(articleIDs)) 170 171 for _, r := range resp { 172 b, err := json.Marshal(r) 173 require.Nil(t, err) 174 var art article 175 err = json.Unmarshal(b, &art) 176 require.Nil(t, err) 177 require.Len(t, art.HasParagraphs, 1) 178 refPairs[art.Additional.ID] = art.HasParagraphs[0].Additional.ID 179 } 180 181 for i := range articleIDs { 182 paragraphID, ok := refPairs[articleIDs[i]] 183 require.True(t, ok, "expected %q to be in refPairs: %+v", articleIDs[i], refPairs) 184 assert.Equal(t, paragraphIDs[i], paragraphID) 185 } 186 }) 187 188 t.Run("restart node 1", func(t *testing.T) { 189 restartNode1(ctx, t, compose) 190 }) 191 }) 192 193 t.Run("patch an object", func(t *testing.T) { 194 before, err := getTenantObject(t, compose.GetWeaviate().URI(), "Article", articleIDs[0], tenantID.String()) 195 require.Nil(t, err) 196 newTitle := "Article#9000" 197 198 t.Run("execute object patch on node 2", func(t *testing.T) { 199 patch := &models.Object{ 200 ID: before.ID, 201 Class: "Article", 202 Properties: map[string]interface{}{"title": newTitle}, 203 Tenant: tenantID.String(), 204 } 205 patchTenantObject(t, compose.GetWeaviateNode2().URI(), patch) 206 }) 207 208 t.Run("stop node 2", func(t *testing.T) { 209 stopNode(ctx, t, compose, compose.GetWeaviateNode2().Name()) 210 }) 211 212 t.Run("assert object is patched on node 1", func(t *testing.T) { 213 after, err := getTenantObjectFromNode(t, compose.GetWeaviate().URI(), 214 "Article", articleIDs[0], "node1", tenantID.String()) 215 require.Nil(t, err) 216 217 newVal, ok := after.Properties.(map[string]interface{})["title"] 218 require.True(t, ok) 219 assert.Equal(t, newTitle, newVal) 220 }) 221 222 t.Run("restart node 2", func(t *testing.T) { 223 err = compose.Start(ctx, compose.GetWeaviateNode2().Name()) 224 require.Nil(t, err) 225 }) 226 }) 227 228 t.Run("delete an object", func(t *testing.T) { 229 t.Run("execute delete object on node 1", func(t *testing.T) { 230 deleteTenantObject(t, compose.GetWeaviate().URI(), "Article", articleIDs[0], tenantID.String()) 231 }) 232 233 t.Run("stop node 1", func(t *testing.T) { 234 stopNode(ctx, t, compose, compose.GetWeaviate().Name()) 235 }) 236 237 t.Run("assert object removed from node 2", func(t *testing.T) { 238 _, err := getTenantObjectFromNode(t, compose.GetWeaviateNode2().URI(), 239 "Article", articleIDs[0], "node2", tenantID.String()) 240 assert.Equal(t, &objects.ObjectsClassGetNotFound{}, err) 241 }) 242 243 t.Run("restart node 1", func(t *testing.T) { 244 restartNode1(ctx, t, compose) 245 }) 246 }) 247 248 t.Run("batch delete all objects", func(t *testing.T) { 249 t.Run("execute batch delete on node 2", func(t *testing.T) { 250 deleteTenantObjects(t, compose.GetWeaviateNode2().URI(), 251 "Article", []string{"title"}, "Article#*", tenantID.String()) 252 }) 253 254 t.Run("stop node 2", func(t *testing.T) { 255 stopNode(ctx, t, compose, compose.GetWeaviateNode2().Name()) 256 }) 257 258 t.Run("assert objects are removed from node 1", func(t *testing.T) { 259 count := countTenantObjects(t, compose.GetWeaviate().URI(), "Article", tenantID.String()) 260 assert.Zero(t, count) 261 }) 262 263 t.Run("restart node 2", func(t *testing.T) { 264 err = compose.Start(ctx, compose.GetWeaviateNode2().Name()) 265 require.Nil(t, err) 266 }) 267 }) 268 }