github.com/weaviate/weaviate@v1.24.6/test/acceptance/objects/rapid_updates_add_reference_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 test 13 14 import ( 15 "fmt" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/go-openapi/strfmt" 21 "github.com/stretchr/testify/assert" 22 "github.com/weaviate/weaviate/client/objects" 23 clschema "github.com/weaviate/weaviate/client/schema" 24 "github.com/weaviate/weaviate/entities/models" 25 "github.com/weaviate/weaviate/entities/schema" 26 "github.com/weaviate/weaviate/test/helper" 27 ) 28 29 // This aims to prevent a regression on 30 // https://github.com/weaviate/weaviate/issues/1016 31 // The issue was that rapid POST .../references/... request in succession would 32 // overwrite each other due to the eventual consistency nature of the used 33 // backend (esvector). This bug is considered fix if n items can be rapidly 34 // added and a subsequent GET request of the source resource contains all 35 // previously added references. 36 func Test_RapidlyAddingReferences(t *testing.T) { 37 sourceClass := "SequenceReferenceTestSource" 38 targetClass := "SequenceReferenceTestTarget" 39 40 sourceID := strfmt.UUID("96ce03ca-58ed-48e1-a0f1-51f63fa9aa12") 41 42 targetIDs := []strfmt.UUID{ 43 "ce1a4756-b7ce-44fa-b079-45a7ec400882", 44 "e1edb4ff-570c-4f0b-a1a1-18af118369aa", 45 "25d22c70-3df0-4e5c-b8c1-a88d4d2771ef", 46 "6f2a0708-3e8e-4a68-9763-26c465d8bf83", 47 "c4dfae47-ebcf-4808-9122-1c67898ec140", 48 "754bd925-1900-4f93-9f5d-27631eb618bb", 49 "babba820-e3f5-4e8d-a354-76f2cb13fdba", 50 "270942da-1999-40cd-a580-a91aa144b6c0", 51 "a7a06618-6d50-4654-be75-2c9f639a6368", 52 "47ba1d2b-6b8c-4b3b-92a8-46574a069ae8", 53 } 54 55 t.Run("adding the required schema", func(t *testing.T) { 56 t.Run("target class", func(t *testing.T) { 57 params := clschema.NewSchemaObjectsCreateParams().WithObjectClass( 58 &models.Class{ 59 Class: targetClass, 60 Properties: []*models.Property{ 61 { 62 DataType: schema.DataTypeText.PropString(), 63 Tokenization: models.PropertyTokenizationWhitespace, 64 Name: "name", 65 }, 66 }, 67 }, 68 ) 69 resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil) 70 helper.AssertRequestOk(t, resp, err, nil) 71 }) 72 73 t.Run("source class", func(t *testing.T) { 74 params := clschema.NewSchemaObjectsCreateParams().WithObjectClass( 75 &models.Class{ 76 Class: sourceClass, 77 Properties: []*models.Property{ 78 { 79 DataType: []string{targetClass}, 80 Name: "toTarget", 81 }, 82 { 83 DataType: schema.DataTypeText.PropString(), 84 Tokenization: models.PropertyTokenizationWhitespace, 85 Name: "name", 86 }, 87 }, 88 }, 89 ) 90 resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil) 91 helper.AssertRequestOk(t, resp, err, nil) 92 }) 93 }) 94 95 t.Run("adding all objects (without referencing)", func(t *testing.T) { 96 t.Run("source object", func(t *testing.T) { 97 assertCreateObjectWithID(t, sourceClass, "", sourceID, map[string]interface{}{ 98 "name": "Source Object", 99 }) 100 }) 101 102 t.Run("target objects", func(t *testing.T) { 103 for i, id := range targetIDs { 104 assertCreateObjectWithID(t, targetClass, "", id, map[string]interface{}{ 105 "name": fmt.Sprintf("target object %d", i), 106 }) 107 } 108 }) 109 }) 110 111 t.Run("waiting for the last added object to be present", func(t *testing.T) { 112 assertGetObjectEventually(t, targetIDs[len(targetIDs)-1]) 113 }) 114 115 t.Run("placing all references in succession", func(t *testing.T) { 116 for _, id := range targetIDs { 117 params := objects.NewObjectsReferencesCreateParams(). 118 WithID(sourceID). 119 WithPropertyName("toTarget"). 120 WithBody( 121 &models.SingleRef{ 122 Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id)), 123 }, 124 ) 125 126 res, err := helper.Client(t).Objects.ObjectsReferencesCreate(params, nil) 127 helper.AssertRequestOk(t, res, err, nil) 128 } 129 }) 130 131 // wait for index refresh 132 time.Sleep(2 * time.Second) // TODO: improve through polling 133 134 t.Run("checking which refs were set", func(t *testing.T) { 135 source := assertGetObject(t, sourceID) 136 137 var foundIDs []strfmt.UUID 138 // extract IDs 139 for _, ref := range source.Properties.(map[string]interface{})["toTarget"].([]interface{}) { 140 beacon := ref.(map[string]interface{})["beacon"].(string) 141 chunks := strings.Split(beacon, "/") 142 foundIDs = append(foundIDs, strfmt.UUID(chunks[len(chunks)-1])) 143 } 144 145 assert.ElementsMatch(t, targetIDs, foundIDs) 146 }) 147 148 // cleanup 149 helper.Client(t).Schema.SchemaObjectsDelete( 150 clschema.NewSchemaObjectsDeleteParams().WithClassName(sourceClass), nil) 151 helper.Client(t).Schema.SchemaObjectsDelete( 152 clschema.NewSchemaObjectsDeleteParams().WithClassName(targetClass), nil) 153 }