github.com/weaviate/weaviate@v1.24.6/usecases/schema/commit_failure_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 schema 13 14 import ( 15 "context" 16 "encoding/json" 17 "fmt" 18 "testing" 19 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 "github.com/weaviate/weaviate/entities/models" 23 "github.com/weaviate/weaviate/entities/schema" 24 "github.com/weaviate/weaviate/usecases/sharding" 25 ) 26 27 // This test makes sure that even when a commit fails, the coordinator still 28 // honors the commit. This was introduced as part of 29 // https://github.com/weaviate/weaviate/issues/2616, where a schema 30 // inconsistency was guaranteed as soon as any node died in it's commit phase 31 // because the coordinator would behave differently than other (alive) which it 32 // told to commit. 33 func TestFailedCommits(t *testing.T) { 34 type test struct { 35 name string 36 // prepare runs before any commit errors occur to build an initial state 37 prepare func(*testing.T, *Manager) 38 // action runs with commit errors 39 action func(*testing.T, *Manager) 40 expSchema []*models.Class 41 } 42 43 ctx := context.Background() 44 vTrue := true 45 vFalse := false 46 47 tests := []test{ 48 { 49 name: "Add a class", 50 action: func(t *testing.T, sm *Manager) { 51 sm.AddClass(ctx, nil, &models.Class{ 52 Class: "MyClass", 53 VectorIndexType: "hnsw", 54 }) 55 }, 56 expSchema: []*models.Class{ 57 classWithDefaultsWithProps(t, "MyClass", nil), 58 }, 59 }, 60 { 61 name: "Delete a class", 62 prepare: func(t *testing.T, sm *Manager) { 63 sm.AddClass(ctx, nil, &models.Class{ 64 Class: "MyClass", 65 VectorIndexType: "hnsw", 66 }) 67 sm.AddClass(ctx, nil, &models.Class{ 68 Class: "OtherClass", 69 VectorIndexType: "hnsw", 70 }) 71 }, 72 action: func(t *testing.T, sm *Manager) { 73 assert.Nil(t, sm.DeleteClass(ctx, nil, "MyClass")) 74 }, 75 expSchema: []*models.Class{ 76 classWithDefaultsWithProps(t, "OtherClass", nil), 77 }, 78 }, 79 { 80 name: "Extend a class with a property", 81 prepare: func(t *testing.T, sm *Manager) { 82 sm.AddClass(ctx, nil, &models.Class{ 83 Class: "MyClass", 84 VectorIndexType: "hnsw", 85 }) 86 }, 87 action: func(t *testing.T, sm *Manager) { 88 err := sm.AddClassProperty(ctx, nil, "MyClass", &models.Property{ 89 Name: "prop_1", 90 DataType: schema.DataTypeInt.PropString(), 91 }) 92 assert.Nil(t, err) 93 }, 94 expSchema: []*models.Class{ 95 classWithDefaultsWithProps(t, "MyClass", []*models.Property{ 96 { 97 Name: "prop_1", 98 DataType: schema.DataTypeInt.PropString(), 99 IndexFilterable: &vTrue, 100 IndexSearchable: &vFalse, 101 }, 102 }), 103 }, 104 }, 105 } 106 107 for _, test := range tests { 108 t.Run(test.name, func(t *testing.T) { 109 clusterState := &fakeClusterState{ 110 hosts: []string{"node1", "node2"}, 111 } 112 113 // required for the startup sync 114 txJSON, _ := json.Marshal(ReadSchemaPayload{ 115 Schema: &State{ 116 ObjectSchema: &models.Schema{ 117 Classes: []*models.Class{}, 118 }, 119 }, 120 }) 121 122 txClient := &fakeTxClient{ 123 openInjectPayload: json.RawMessage(txJSON), // required for the startup sync 124 } 125 126 initialSchema := &State{ 127 ObjectSchema: &models.Schema{}, 128 ShardingState: map[string]*sharding.State{}, 129 } 130 131 sm, err := newManagerWithClusterAndTx(t, clusterState, txClient, initialSchema) 132 require.Nil(t, err) 133 134 sm.StartServing(context.Background()) 135 136 if test.prepare != nil { 137 test.prepare(t, sm) 138 } 139 140 txClient.commitErr = fmt.Errorf("Oh I, I just died in your arms tonight") 141 test.action(t, sm) 142 143 assert.ElementsMatch(t, test.expSchema, sm.GetSchemaSkipAuth().Objects.Classes) 144 }) 145 } 146 } 147 148 func classWithDefaultsWithProps(t *testing.T, name string, 149 props []*models.Property, 150 ) *models.Class { 151 class := &models.Class{Class: name, VectorIndexType: "hnsw"} 152 class.Vectorizer = "none" 153 154 sc, err := sharding.ParseConfig(map[string]interface{}{}, 1) 155 require.Nil(t, err) 156 157 class.ShardingConfig = sc 158 159 class.VectorIndexConfig = fakeVectorConfig{} 160 class.ReplicationConfig = &models.ReplicationConfig{Factor: 1} 161 class.MultiTenancyConfig = &models.MultiTenancyConfig{Enabled: false} 162 163 (&fakeModuleConfig{}).SetClassDefaults(class) 164 setInvertedConfigDefaults(class) 165 166 class.Properties = props 167 168 return class 169 }