go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/datachange_test.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kvscheduler 16 17 import ( 18 "context" 19 "errors" 20 "testing" 21 "time" 22 23 . "github.com/onsi/gomega" 24 "google.golang.org/protobuf/proto" 25 26 . "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 27 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/test" 28 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils" 29 . "go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler" 30 ) 31 32 var testCtx = WithSimulation(context.Background()) 33 34 func TestDataChangeTransactions(t *testing.T) { 35 RegisterTestingT(t) 36 37 // prepare KV Scheduler 38 scheduler := NewPlugin(UseDeps(func(deps *Deps) { 39 deps.HTTPHandlers = nil 40 })) 41 err := scheduler.Init() 42 Expect(err).To(BeNil()) 43 44 // prepare mocks 45 mockSB := test.NewMockSouthbound() 46 // -> descriptor1: 47 descriptor1 := test.NewMockDescriptor(&KVDescriptor{ 48 Name: descriptor1Name, 49 NBKeyPrefix: prefixA, 50 KeySelector: prefixSelector(prefixA), 51 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 52 DerivedValues: test.ArrayValueDerBuilder, 53 WithMetadata: true, 54 }, mockSB, 0) 55 // -> descriptor2: 56 descriptor2 := test.NewMockDescriptor(&KVDescriptor{ 57 Name: descriptor2Name, 58 NBKeyPrefix: prefixB, 59 KeySelector: prefixSelector(prefixB), 60 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 61 DerivedValues: test.ArrayValueDerBuilder, 62 Dependencies: func(key string, value proto.Message) []Dependency { 63 if key == prefixB+baseValue2+"/item1" { 64 depKey := prefixA + baseValue1 65 return []Dependency{ 66 {Label: depKey, Key: depKey}, 67 } 68 } 69 if key == prefixB+baseValue2+"/item2" { 70 depKey := prefixA + baseValue1 + "/item1" 71 return []Dependency{ 72 {Label: depKey, Key: depKey}, 73 } 74 } 75 return nil 76 }, 77 WithMetadata: true, 78 RetrieveDependencies: []string{descriptor1Name}, 79 }, mockSB, 0) 80 // -> descriptor3: 81 descriptor3 := test.NewMockDescriptor(&KVDescriptor{ 82 Name: descriptor3Name, 83 NBKeyPrefix: prefixC, 84 KeySelector: prefixSelector(prefixC), 85 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 86 DerivedValues: test.ArrayValueDerBuilder, 87 UpdateWithRecreate: func(key string, oldValue, newValue proto.Message, metadata Metadata) bool { 88 return key == prefixC+baseValue3 89 }, 90 WithMetadata: true, 91 RetrieveDependencies: []string{descriptor2Name}, 92 }, mockSB, 0) 93 94 // register all 3 descriptors with the scheduler 95 scheduler.RegisterKVDescriptor(descriptor1) 96 scheduler.RegisterKVDescriptor(descriptor2) 97 scheduler.RegisterKVDescriptor(descriptor3) 98 99 // get metadata map created for each descriptor 100 metadataMap := scheduler.GetMetadataMap(descriptor1.Name) 101 nameToInteger1, withMetadataMap := metadataMap.(test.NameToInteger) 102 Expect(withMetadataMap).To(BeTrue()) 103 metadataMap = scheduler.GetMetadataMap(descriptor2.Name) 104 nameToInteger2, withMetadataMap := metadataMap.(test.NameToInteger) 105 Expect(withMetadataMap).To(BeTrue()) 106 metadataMap = scheduler.GetMetadataMap(descriptor3.Name) 107 nameToInteger3, withMetadataMap := metadataMap.(test.NameToInteger) 108 Expect(withMetadataMap).To(BeTrue()) 109 110 // run non-resync transaction against empty SB 111 startTime := time.Now() 112 schedulerTxn := scheduler.StartNBTransaction() 113 schedulerTxn.SetValue(prefixB+baseValue2, test.NewArrayValue("item1", "item2")) 114 schedulerTxn.SetValue(prefixA+baseValue1, test.NewArrayValue("item2")) 115 schedulerTxn.SetValue(prefixC+baseValue3, test.NewArrayValue("item1", "item2")) 116 description := "testing data change" 117 seqNum, err := schedulerTxn.Commit(WithDescription(testCtx, description)) 118 stopTime := time.Now() 119 Expect(seqNum).To(BeEquivalentTo(0)) 120 Expect(err).ShouldNot(HaveOccurred()) 121 122 // check the state of SB 123 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 124 // -> base value 1 125 value := mockSB.GetValue(prefixA + baseValue1) 126 Expect(value).ToNot(BeNil()) 127 Expect(proto.Equal(value.Value, test.NewArrayValue("item2"))).To(BeTrue()) 128 Expect(value.Metadata).ToNot(BeNil()) 129 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 130 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 131 // -> item1 derived from base value was not added 132 value = mockSB.GetValue(prefixA + baseValue1 + "/item1") 133 Expect(value).To(BeNil()) 134 // -> item2 derived from base value 1 135 value = mockSB.GetValue(prefixA + baseValue1 + "/item2") 136 Expect(value).ToNot(BeNil()) 137 Expect(proto.Equal(value.Value, test.NewStringValue("item2"))).To(BeTrue()) 138 Expect(value.Metadata).To(BeNil()) 139 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 140 // -> base value 2 141 value = mockSB.GetValue(prefixB + baseValue2) 142 Expect(value).ToNot(BeNil()) 143 Expect(proto.Equal(value.Value, test.NewArrayValue("item1", "item2"))).To(BeTrue()) 144 Expect(value.Metadata).ToNot(BeNil()) 145 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 146 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 147 // -> item1 derived from base value 2 148 value = mockSB.GetValue(prefixB + baseValue2 + "/item1") 149 Expect(value).ToNot(BeNil()) 150 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 151 Expect(value.Metadata).To(BeNil()) 152 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 153 // -> item2 derived from base value 2 is pending 154 value = mockSB.GetValue(prefixB + baseValue2 + "/item2") 155 Expect(value).To(BeNil()) 156 // -> base value 3 157 value = mockSB.GetValue(prefixC + baseValue3) 158 Expect(value).ToNot(BeNil()) 159 Expect(proto.Equal(value.Value, test.NewArrayValue("item1", "item2"))).To(BeTrue()) 160 Expect(value.Metadata).ToNot(BeNil()) 161 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 162 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 163 // -> item1 derived from base value 3 164 value = mockSB.GetValue(prefixC + baseValue3 + "/item1") 165 Expect(value).ToNot(BeNil()) 166 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 167 Expect(value.Metadata).To(BeNil()) 168 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 169 // -> item2 derived from base value 3 170 value = mockSB.GetValue(prefixC + baseValue3 + "/item2") 171 Expect(value).ToNot(BeNil()) 172 Expect(proto.Equal(value.Value, test.NewStringValue("item2"))).To(BeTrue()) 173 Expect(value.Metadata).To(BeNil()) 174 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 175 Expect(mockSB.GetValues(nil)).To(HaveLen(7)) 176 177 // check metadata 178 metadata, exists := nameToInteger1.LookupByName(baseValue1) 179 Expect(exists).To(BeTrue()) 180 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 181 metadata, exists = nameToInteger2.LookupByName(baseValue2) 182 Expect(exists).To(BeTrue()) 183 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 184 metadata, exists = nameToInteger3.LookupByName(baseValue3) 185 Expect(exists).To(BeTrue()) 186 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 187 188 // check operations executed in SB 189 opHistory := mockSB.PopHistoryOfOps() 190 Expect(opHistory).To(HaveLen(7)) 191 operation := opHistory[0] 192 Expect(operation.OpType).To(Equal(test.MockCreate)) 193 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 194 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1)) 195 Expect(operation.Err).To(BeNil()) 196 operation = opHistory[1] 197 Expect(operation.OpType).To(Equal(test.MockCreate)) 198 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 199 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item2")) 200 Expect(operation.Err).To(BeNil()) 201 operation = opHistory[2] 202 Expect(operation.OpType).To(Equal(test.MockCreate)) 203 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor2Name)) 204 Expect(operation.Key).To(BeEquivalentTo(prefixB + baseValue2)) 205 Expect(operation.Err).To(BeNil()) 206 operation = opHistory[3] 207 Expect(operation.OpType).To(Equal(test.MockCreate)) 208 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor2Name)) 209 Expect(operation.Key).To(BeEquivalentTo(prefixB + baseValue2 + "/item1")) 210 Expect(operation.Err).To(BeNil()) 211 operation = opHistory[4] 212 Expect(operation.OpType).To(Equal(test.MockCreate)) 213 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 214 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 215 Expect(operation.Err).To(BeNil()) 216 operation = opHistory[5] 217 Expect(operation.OpType).To(Equal(test.MockCreate)) 218 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 219 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 220 Expect(operation.Err).To(BeNil()) 221 operation = opHistory[6] 222 Expect(operation.OpType).To(Equal(test.MockCreate)) 223 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 224 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item2")) 225 Expect(operation.Err).To(BeNil()) 226 227 // check transaction operations 228 txnHistory := scheduler.GetTransactionHistory(time.Time{}, time.Now()) 229 Expect(txnHistory).To(HaveLen(1)) 230 txn := txnHistory[0] 231 Expect(txn.PreRecord).To(BeFalse()) 232 Expect(txn.Start.After(startTime)).To(BeTrue()) 233 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 234 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 235 Expect(txn.SeqNum).To(BeEquivalentTo(0)) 236 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 237 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 238 Expect(txn.Description).To(Equal(description)) 239 checkRecordedValues(txn.Values, []RecordedKVPair{ 240 {Key: prefixA + baseValue1, Value: utils.RecordProtoMessage(test.NewArrayValue("item2")), Origin: FromNB}, 241 {Key: prefixB + baseValue2, Value: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), Origin: FromNB}, 242 {Key: prefixC + baseValue3, Value: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), Origin: FromNB}, 243 }) 244 245 txnOps := RecordedTxnOps{ 246 { 247 Operation: TxnOperation_CREATE, 248 Key: prefixA + baseValue1, 249 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item2")), 250 PrevState: ValueState_NONEXISTENT, 251 NewState: ValueState_CONFIGURED, 252 }, 253 { 254 Operation: TxnOperation_CREATE, 255 Key: prefixA + baseValue1 + "/item2", 256 IsDerived: true, 257 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 258 PrevState: ValueState_NONEXISTENT, 259 NewState: ValueState_CONFIGURED, 260 }, 261 { 262 Operation: TxnOperation_CREATE, 263 Key: prefixB + baseValue2, 264 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), 265 PrevState: ValueState_NONEXISTENT, 266 NewState: ValueState_CONFIGURED, 267 }, 268 { 269 Operation: TxnOperation_CREATE, 270 Key: prefixB + baseValue2 + "/item1", 271 IsDerived: true, 272 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 273 PrevState: ValueState_NONEXISTENT, 274 NewState: ValueState_CONFIGURED, 275 }, 276 { 277 Operation: TxnOperation_CREATE, 278 Key: prefixB + baseValue2 + "/item2", 279 IsDerived: true, 280 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 281 PrevState: ValueState_NONEXISTENT, 282 NewState: ValueState_PENDING, 283 NOOP: true, 284 }, 285 { 286 Operation: TxnOperation_CREATE, 287 Key: prefixC + baseValue3, 288 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), 289 PrevState: ValueState_NONEXISTENT, 290 NewState: ValueState_CONFIGURED, 291 }, 292 { 293 Operation: TxnOperation_CREATE, 294 Key: prefixC + baseValue3 + "/item1", 295 IsDerived: true, 296 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 297 PrevState: ValueState_NONEXISTENT, 298 NewState: ValueState_CONFIGURED, 299 }, 300 { 301 Operation: TxnOperation_CREATE, 302 Key: prefixC + baseValue3 + "/item2", 303 IsDerived: true, 304 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 305 PrevState: ValueState_NONEXISTENT, 306 NewState: ValueState_CONFIGURED, 307 }, 308 } 309 checkTxnOperations(txn.Planned, txnOps) 310 checkTxnOperations(txn.Executed, txnOps) 311 312 // check flag stats 313 graphR := scheduler.graph.Read() 314 errorStats := graphR.GetFlagStats(ErrorFlagIndex, nil) 315 Expect(errorStats.TotalCount).To(BeEquivalentTo(0)) 316 pendingStats := graphR.GetFlagStats(UnavailValueFlagIndex, nil) 317 Expect(pendingStats.TotalCount).To(BeEquivalentTo(1)) 318 derivedStats := graphR.GetFlagStats(DerivedFlagIndex, nil) 319 Expect(derivedStats.TotalCount).To(BeEquivalentTo(5)) 320 lastUpdateStats := graphR.GetFlagStats(LastUpdateFlagIndex, nil) 321 Expect(lastUpdateStats.TotalCount).To(BeEquivalentTo(8)) 322 descriptorStats := graphR.GetFlagStats(DescriptorFlagIndex, nil) 323 Expect(descriptorStats.TotalCount).To(BeEquivalentTo(8)) 324 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor1Name)) 325 Expect(descriptorStats.PerValueCount[descriptor1Name]).To(BeEquivalentTo(2)) 326 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor2Name)) 327 Expect(descriptorStats.PerValueCount[descriptor2Name]).To(BeEquivalentTo(3)) 328 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor3Name)) 329 Expect(descriptorStats.PerValueCount[descriptor3Name]).To(BeEquivalentTo(3)) 330 valueStateStats := graphR.GetFlagStats(ValueStateFlagIndex, nil) 331 Expect(valueStateStats.TotalCount).To(BeEquivalentTo(8)) 332 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_CONFIGURED.String())) 333 Expect(valueStateStats.PerValueCount[ValueState_CONFIGURED.String()]).To(BeEquivalentTo(7)) 334 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_PENDING.String())) 335 Expect(valueStateStats.PerValueCount[ValueState_PENDING.String()]).To(BeEquivalentTo(1)) 336 graphR.Release() 337 338 // check value dumps 339 views := []View{NBView, SBView, CachedView} 340 for _, view := range views { 341 // descriptor1 342 expValues := []KVWithMetadata{ 343 {Key: prefixA + baseValue1, Value: test.NewArrayValue("item2"), Origin: FromNB, Metadata: &test.OnlyInteger{Integer: 0}}, 344 } 345 dumpedValues, err := scheduler.DumpValuesByKeyPrefix(prefixA, view) 346 Expect(err).To(BeNil()) 347 checkValues(dumpedValues, expValues) 348 dumpedValues, err = scheduler.DumpValuesByDescriptor(descriptor1Name, view) 349 Expect(err).To(BeNil()) 350 checkValues(dumpedValues, expValues) 351 // descriptor2 352 expValues = []KVWithMetadata{ 353 {Key: prefixB + baseValue2, Value: test.NewArrayValue("item1", "item2"), Origin: FromNB, Metadata: &test.OnlyInteger{Integer: 0}}, 354 } 355 dumpedValues, err = scheduler.DumpValuesByKeyPrefix(prefixB, view) 356 Expect(err).To(BeNil()) 357 checkValues(dumpedValues, expValues) 358 dumpedValues, err = scheduler.DumpValuesByDescriptor(descriptor2Name, view) 359 Expect(err).To(BeNil()) 360 checkValues(dumpedValues, expValues) 361 // descriptor3 362 expValues = []KVWithMetadata{ 363 {Key: prefixC + baseValue3, Value: test.NewArrayValue("item1", "item2"), Origin: FromNB, Metadata: &test.OnlyInteger{Integer: 0}}, 364 } 365 dumpedValues, err = scheduler.DumpValuesByKeyPrefix(prefixC, view) 366 Expect(err).To(BeNil()) 367 checkValues(dumpedValues, expValues) 368 dumpedValues, err = scheduler.DumpValuesByDescriptor(descriptor3Name, view) 369 Expect(err).To(BeNil()) 370 checkValues(dumpedValues, expValues) 371 } 372 mockSB.PopHistoryOfOps() // remove Retrieve-s from the history 373 374 // check value states 375 status := scheduler.GetValueStatus(prefixA + baseValue1) 376 Expect(status).ToNot(BeNil()) 377 checkBaseValueStatus(status, &BaseValueStatus{ 378 Value: &ValueStatus{ 379 Key: prefixA + baseValue1, 380 State: ValueState_CONFIGURED, 381 LastOperation: TxnOperation_CREATE, 382 }, 383 DerivedValues: []*ValueStatus{ 384 { 385 Key: prefixA + baseValue1 + "/item2", 386 State: ValueState_CONFIGURED, 387 LastOperation: TxnOperation_CREATE, 388 }, 389 }, 390 }) 391 status = scheduler.GetValueStatus(prefixB + baseValue2) 392 Expect(status).ToNot(BeNil()) 393 checkBaseValueStatus(status, &BaseValueStatus{ 394 Value: &ValueStatus{ 395 Key: prefixB + baseValue2, 396 State: ValueState_CONFIGURED, 397 LastOperation: TxnOperation_CREATE, 398 }, 399 DerivedValues: []*ValueStatus{ 400 { 401 Key: prefixB + baseValue2 + "/item1", 402 State: ValueState_CONFIGURED, 403 LastOperation: TxnOperation_CREATE, 404 }, 405 { 406 Key: prefixB + baseValue2 + "/item2", 407 State: ValueState_PENDING, 408 LastOperation: TxnOperation_CREATE, 409 Details: []string{prefixA + baseValue1 + "/item1"}, 410 }, 411 }, 412 }) 413 status = scheduler.GetValueStatus(prefixC + baseValue3) 414 Expect(status).ToNot(BeNil()) 415 checkBaseValueStatus(status, &BaseValueStatus{ 416 Value: &ValueStatus{ 417 Key: prefixC + baseValue3, 418 State: ValueState_CONFIGURED, 419 LastOperation: TxnOperation_CREATE, 420 }, 421 DerivedValues: []*ValueStatus{ 422 { 423 Key: prefixC + baseValue3 + "/item1", 424 State: ValueState_CONFIGURED, 425 LastOperation: TxnOperation_CREATE, 426 }, 427 { 428 Key: prefixC + baseValue3 + "/item2", 429 State: ValueState_CONFIGURED, 430 LastOperation: TxnOperation_CREATE, 431 }, 432 }, 433 }) 434 435 // run 2nd non-resync transaction 436 startTime = time.Now() 437 schedulerTxn2 := scheduler.StartNBTransaction() 438 schedulerTxn2.SetValue(prefixC+baseValue3, test.NewArrayValue("item1")) 439 schedulerTxn2.SetValue(prefixA+baseValue1, test.NewArrayValue("item1")) 440 seqNum, err = schedulerTxn2.Commit(testCtx) 441 stopTime = time.Now() 442 Expect(seqNum).To(BeEquivalentTo(1)) 443 Expect(err).ShouldNot(HaveOccurred()) 444 445 // check the state of SB 446 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 447 // -> base value 1 448 value = mockSB.GetValue(prefixA + baseValue1) 449 Expect(value).ToNot(BeNil()) 450 Expect(proto.Equal(value.Value, test.NewArrayValue("item1"))).To(BeTrue()) 451 Expect(value.Metadata).ToNot(BeNil()) 452 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 453 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 454 // -> item1 derived from base value was added 455 value = mockSB.GetValue(prefixA + baseValue1 + "/item1") 456 Expect(value).ToNot(BeNil()) 457 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 458 Expect(value.Metadata).To(BeNil()) 459 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 460 // -> item2 derived from base value 1 was deleted 461 value = mockSB.GetValue(prefixA + baseValue1 + "/item2") 462 Expect(value).To(BeNil()) 463 // -> base value 2 464 value = mockSB.GetValue(prefixB + baseValue2) 465 Expect(value).ToNot(BeNil()) 466 Expect(proto.Equal(value.Value, test.NewArrayValue("item1", "item2"))).To(BeTrue()) 467 Expect(value.Metadata).ToNot(BeNil()) 468 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 469 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 470 // -> item1 derived from base value 2 471 value = mockSB.GetValue(prefixB + baseValue2 + "/item1") 472 Expect(value).ToNot(BeNil()) 473 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 474 Expect(value.Metadata).To(BeNil()) 475 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 476 // -> item2 derived from base value 2 is no longer pending 477 value = mockSB.GetValue(prefixB + baseValue2 + "/item2") 478 Expect(value).ToNot(BeNil()) 479 Expect(proto.Equal(value.Value, test.NewStringValue("item2"))).To(BeTrue()) 480 Expect(value.Metadata).To(BeNil()) 481 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 482 // -> base value 3 483 value = mockSB.GetValue(prefixC + baseValue3) 484 Expect(value).ToNot(BeNil()) 485 Expect(proto.Equal(value.Value, test.NewArrayValue("item1"))).To(BeTrue()) 486 Expect(value.Metadata).ToNot(BeNil()) 487 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(1)) 488 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 489 // -> item1 derived from base value 3 490 value = mockSB.GetValue(prefixC + baseValue3 + "/item1") 491 Expect(value).ToNot(BeNil()) 492 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 493 Expect(value.Metadata).To(BeNil()) 494 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 495 // -> item2 derived from base value 3 was deleted 496 value = mockSB.GetValue(prefixC + baseValue3 + "/item2") 497 Expect(value).To(BeNil()) 498 499 // check metadata 500 metadata, exists = nameToInteger1.LookupByName(baseValue1) 501 Expect(exists).To(BeTrue()) 502 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 503 metadata, exists = nameToInteger2.LookupByName(baseValue2) 504 Expect(exists).To(BeTrue()) 505 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 506 metadata, exists = nameToInteger3.LookupByName(baseValue3) 507 Expect(exists).To(BeTrue()) 508 Expect(metadata.GetInteger()).To(BeEquivalentTo(1)) // re-created 509 510 // check operations executed in SB 511 opHistory = mockSB.PopHistoryOfOps() 512 Expect(opHistory).To(HaveLen(9)) 513 operation = opHistory[0] 514 Expect(operation.OpType).To(Equal(test.MockDelete)) 515 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 516 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 517 Expect(operation.Err).To(BeNil()) 518 operation = opHistory[1] 519 Expect(operation.OpType).To(Equal(test.MockDelete)) 520 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 521 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item2")) 522 Expect(operation.Err).To(BeNil()) 523 operation = opHistory[2] 524 Expect(operation.OpType).To(Equal(test.MockDelete)) 525 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 526 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 527 Expect(operation.Err).To(BeNil()) 528 operation = opHistory[3] 529 Expect(operation.OpType).To(Equal(test.MockCreate)) 530 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 531 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 532 Expect(operation.Err).To(BeNil()) 533 operation = opHistory[4] 534 Expect(operation.OpType).To(Equal(test.MockCreate)) 535 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 536 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 537 Expect(operation.Err).To(BeNil()) 538 operation = opHistory[5] 539 Expect(operation.OpType).To(Equal(test.MockDelete)) 540 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 541 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item2")) 542 Expect(operation.Err).To(BeNil()) 543 operation = opHistory[6] 544 Expect(operation.OpType).To(Equal(test.MockUpdate)) 545 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 546 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1)) 547 Expect(operation.Err).To(BeNil()) 548 operation = opHistory[7] 549 Expect(operation.OpType).To(Equal(test.MockCreate)) 550 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 551 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item1")) 552 Expect(operation.Err).To(BeNil()) 553 operation = opHistory[8] 554 Expect(operation.OpType).To(Equal(test.MockCreate)) 555 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor2Name)) 556 Expect(operation.Key).To(BeEquivalentTo(prefixB + baseValue2 + "/item2")) 557 Expect(operation.Err).To(BeNil()) 558 559 // check transaction operations 560 txnHistory = scheduler.GetTransactionHistory(startTime, stopTime) // first txn not included 561 Expect(txnHistory).To(HaveLen(1)) 562 txn = txnHistory[0] 563 Expect(txn.PreRecord).To(BeFalse()) 564 Expect(txn.Start.After(startTime)).To(BeTrue()) 565 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 566 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 567 Expect(txn.SeqNum).To(BeEquivalentTo(1)) 568 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 569 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 570 Expect(txn.Description).To(BeEmpty()) 571 checkRecordedValues(txn.Values, []RecordedKVPair{ 572 {Key: prefixA + baseValue1, Value: utils.RecordProtoMessage(test.NewArrayValue("item1")), Origin: FromNB}, 573 {Key: prefixC + baseValue3, Value: utils.RecordProtoMessage(test.NewArrayValue("item1")), Origin: FromNB}, 574 }) 575 576 txnOps = RecordedTxnOps{ 577 { 578 Operation: TxnOperation_DELETE, 579 Key: prefixC + baseValue3 + "/item1", 580 IsDerived: true, 581 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 582 PrevState: ValueState_CONFIGURED, 583 NewState: ValueState_REMOVED, 584 IsRecreate: true, 585 }, 586 { 587 Operation: TxnOperation_DELETE, 588 Key: prefixC + baseValue3 + "/item2", 589 IsDerived: true, 590 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 591 PrevState: ValueState_CONFIGURED, 592 NewState: ValueState_REMOVED, 593 IsRecreate: true, 594 }, 595 { 596 Operation: TxnOperation_DELETE, 597 Key: prefixC + baseValue3, 598 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), 599 PrevState: ValueState_CONFIGURED, 600 NewState: ValueState_REMOVED, 601 IsRecreate: true, 602 }, 603 { 604 Operation: TxnOperation_CREATE, 605 Key: prefixC + baseValue3, 606 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 607 PrevState: ValueState_REMOVED, 608 NewState: ValueState_CONFIGURED, 609 IsRecreate: true, 610 }, 611 { 612 Operation: TxnOperation_CREATE, 613 Key: prefixC + baseValue3 + "/item1", 614 IsDerived: true, 615 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 616 PrevState: ValueState_NONEXISTENT, // TODO: derived value removed from the graph, ok? 617 NewState: ValueState_CONFIGURED, 618 IsRecreate: true, 619 }, 620 { 621 Operation: TxnOperation_DELETE, 622 Key: prefixA + baseValue1 + "/item2", 623 IsDerived: true, 624 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 625 PrevState: ValueState_CONFIGURED, 626 NewState: ValueState_REMOVED, 627 }, 628 { 629 Operation: TxnOperation_UPDATE, 630 Key: prefixA + baseValue1, 631 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item2")), 632 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 633 PrevState: ValueState_CONFIGURED, 634 NewState: ValueState_CONFIGURED, 635 }, 636 { 637 Operation: TxnOperation_CREATE, 638 Key: prefixA + baseValue1 + "/item1", 639 IsDerived: true, 640 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 641 PrevState: ValueState_NONEXISTENT, 642 NewState: ValueState_CONFIGURED, 643 }, 644 { 645 Operation: TxnOperation_CREATE, 646 Key: prefixB + baseValue2 + "/item2", 647 IsDerived: true, 648 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 649 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 650 PrevState: ValueState_PENDING, 651 NewState: ValueState_CONFIGURED, 652 }, 653 } 654 checkTxnOperations(txn.Planned, txnOps) 655 checkTxnOperations(txn.Executed, txnOps) 656 657 // check value states 658 status = scheduler.GetValueStatus(prefixA + baseValue1) 659 Expect(status).ToNot(BeNil()) 660 checkBaseValueStatus(status, &BaseValueStatus{ 661 Value: &ValueStatus{ 662 Key: prefixA + baseValue1, 663 State: ValueState_CONFIGURED, 664 LastOperation: TxnOperation_UPDATE, 665 }, 666 DerivedValues: []*ValueStatus{ 667 { 668 Key: prefixA + baseValue1 + "/item1", 669 State: ValueState_CONFIGURED, 670 LastOperation: TxnOperation_CREATE, 671 }, 672 }, 673 }) 674 status = scheduler.GetValueStatus(prefixB + baseValue2) 675 Expect(status).ToNot(BeNil()) 676 checkBaseValueStatus(status, &BaseValueStatus{ 677 Value: &ValueStatus{ 678 Key: prefixB + baseValue2, 679 State: ValueState_CONFIGURED, 680 LastOperation: TxnOperation_CREATE, 681 }, 682 DerivedValues: []*ValueStatus{ 683 { 684 Key: prefixB + baseValue2 + "/item1", 685 State: ValueState_CONFIGURED, 686 LastOperation: TxnOperation_CREATE, 687 }, 688 { 689 Key: prefixB + baseValue2 + "/item2", 690 State: ValueState_CONFIGURED, 691 LastOperation: TxnOperation_CREATE, 692 }, 693 }, 694 }) 695 status = scheduler.GetValueStatus(prefixC + baseValue3) 696 Expect(status).ToNot(BeNil()) 697 checkBaseValueStatus(status, &BaseValueStatus{ 698 Value: &ValueStatus{ 699 Key: prefixC + baseValue3, 700 State: ValueState_CONFIGURED, 701 LastOperation: TxnOperation_UPDATE, 702 }, 703 DerivedValues: []*ValueStatus{ 704 { 705 Key: prefixC + baseValue3 + "/item1", 706 State: ValueState_CONFIGURED, 707 LastOperation: TxnOperation_CREATE, 708 }, 709 }, 710 }) 711 712 // check flag stats 713 graphR = scheduler.graph.Read() 714 errorStats = graphR.GetFlagStats(ErrorFlagIndex, nil) 715 Expect(errorStats.TotalCount).To(BeEquivalentTo(0)) 716 pendingStats = graphR.GetFlagStats(UnavailValueFlagIndex, nil) 717 Expect(pendingStats.TotalCount).To(BeEquivalentTo(1)) 718 derivedStats = graphR.GetFlagStats(DerivedFlagIndex, nil) 719 Expect(derivedStats.TotalCount).To(BeEquivalentTo(8)) 720 lastUpdateStats = graphR.GetFlagStats(LastUpdateFlagIndex, nil) 721 Expect(lastUpdateStats.TotalCount).To(BeEquivalentTo(13)) 722 descriptorStats = graphR.GetFlagStats(DescriptorFlagIndex, nil) 723 Expect(descriptorStats.TotalCount).To(BeEquivalentTo(13)) 724 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor1Name)) 725 Expect(descriptorStats.PerValueCount[descriptor1Name]).To(BeEquivalentTo(4)) 726 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor2Name)) 727 Expect(descriptorStats.PerValueCount[descriptor2Name]).To(BeEquivalentTo(4)) 728 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor3Name)) 729 Expect(descriptorStats.PerValueCount[descriptor3Name]).To(BeEquivalentTo(5)) 730 valueStateStats = graphR.GetFlagStats(ValueStateFlagIndex, nil) 731 Expect(valueStateStats.TotalCount).To(BeEquivalentTo(13)) 732 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_CONFIGURED.String())) 733 Expect(valueStateStats.PerValueCount[ValueState_CONFIGURED.String()]).To(BeEquivalentTo(12)) 734 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_PENDING.String())) 735 Expect(valueStateStats.PerValueCount[ValueState_PENDING.String()]).To(BeEquivalentTo(1)) 736 graphR.Release() 737 738 // close scheduler 739 err = scheduler.Close() 740 Expect(err).To(BeNil()) 741 } 742 743 func TestDataChangeTransactionWithRevert(t *testing.T) { 744 RegisterTestingT(t) 745 746 // prepare KV Scheduler 747 scheduler := NewPlugin(UseDeps(func(deps *Deps) { 748 deps.HTTPHandlers = nil 749 })) 750 err := scheduler.Init() 751 Expect(err).To(BeNil()) 752 753 // prepare mocks 754 mockSB := test.NewMockSouthbound() 755 // -> descriptor1: 756 descriptor1 := test.NewMockDescriptor(&KVDescriptor{ 757 Name: descriptor1Name, 758 NBKeyPrefix: prefixA, 759 KeySelector: prefixSelector(prefixA), 760 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 761 DerivedValues: test.ArrayValueDerBuilder, 762 WithMetadata: true, 763 }, mockSB, 0) 764 // -> descriptor2: 765 descriptor2 := test.NewMockDescriptor(&KVDescriptor{ 766 Name: descriptor2Name, 767 NBKeyPrefix: prefixB, 768 KeySelector: prefixSelector(prefixB), 769 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 770 DerivedValues: test.ArrayValueDerBuilder, 771 Dependencies: func(key string, value proto.Message) []Dependency { 772 if key == prefixB+baseValue2+"/item1" { 773 depKey := prefixA + baseValue1 774 return []Dependency{ 775 {Label: depKey, Key: depKey}, 776 } 777 } 778 if key == prefixB+baseValue2+"/item2" { 779 depKey := prefixA + baseValue1 + "/item1" 780 return []Dependency{ 781 {Label: depKey, Key: depKey}, 782 } 783 } 784 return nil 785 }, 786 WithMetadata: true, 787 RetrieveDependencies: []string{descriptor1Name}, 788 }, mockSB, 0) 789 // -> descriptor3: 790 descriptor3 := test.NewMockDescriptor(&KVDescriptor{ 791 Name: descriptor3Name, 792 NBKeyPrefix: prefixC, 793 KeySelector: prefixSelector(prefixC), 794 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 795 DerivedValues: test.ArrayValueDerBuilder, 796 UpdateWithRecreate: func(key string, oldValue, newValue proto.Message, metadata Metadata) bool { 797 return key == prefixC+baseValue3 798 }, 799 WithMetadata: true, 800 RetrieveDependencies: []string{descriptor2Name}, 801 }, mockSB, 0) 802 803 // register all 3 descriptors with the scheduler 804 scheduler.RegisterKVDescriptor(descriptor1) 805 scheduler.RegisterKVDescriptor(descriptor2) 806 scheduler.RegisterKVDescriptor(descriptor3) 807 808 // get metadata map created for each descriptor 809 metadataMap := scheduler.GetMetadataMap(descriptor1.Name) 810 nameToInteger1, withMetadataMap := metadataMap.(test.NameToInteger) 811 Expect(withMetadataMap).To(BeTrue()) 812 metadataMap = scheduler.GetMetadataMap(descriptor2.Name) 813 nameToInteger2, withMetadataMap := metadataMap.(test.NameToInteger) 814 Expect(withMetadataMap).To(BeTrue()) 815 metadataMap = scheduler.GetMetadataMap(descriptor3.Name) 816 nameToInteger3, withMetadataMap := metadataMap.(test.NameToInteger) 817 Expect(withMetadataMap).To(BeTrue()) 818 819 // run 1st non-resync transaction against empty SB 820 schedulerTxn := scheduler.StartNBTransaction() 821 schedulerTxn.SetValue(prefixB+baseValue2, test.NewArrayValue("item1", "item2")) 822 schedulerTxn.SetValue(prefixA+baseValue1, test.NewArrayValue("item2")) 823 schedulerTxn.SetValue(prefixC+baseValue3, test.NewArrayValue("item1", "item2")) 824 seqNum, err := schedulerTxn.Commit(testCtx) 825 Expect(seqNum).To(BeEquivalentTo(0)) 826 Expect(err).ShouldNot(HaveOccurred()) 827 mockSB.PopHistoryOfOps() 828 829 // plan error before 2nd txn 830 failedModifyClb := func() { 831 mockSB.SetValue(prefixA+baseValue1, test.NewArrayValue(), 832 &test.OnlyInteger{Integer: 0}, FromNB, false) 833 } 834 mockSB.PlanError(prefixA+baseValue1, errors.New("failed to modify value"), failedModifyClb) 835 836 // subscribe to receive notifications about value state changes for prefixA 837 statusChan := make(chan *BaseValueStatus, 5) 838 scheduler.WatchValueStatus(statusChan, prefixSelector(prefixA)) 839 840 // run 2nd non-resync transaction against empty SB that will fail and will be reverted 841 startTime := time.Now() 842 schedulerTxn2 := scheduler.StartNBTransaction() 843 schedulerTxn2.SetValue(prefixC+baseValue3, test.NewArrayValue("item1")) 844 schedulerTxn2.SetValue(prefixA+baseValue1, test.NewArrayValue("item1")) 845 seqNum, err = schedulerTxn2.Commit(WithRevert(testCtx)) 846 stopTime := time.Now() 847 Expect(seqNum).To(BeEquivalentTo(1)) 848 Expect(err).ToNot(BeNil()) 849 txnErr := err.(*TransactionError) 850 Expect(txnErr.GetTxnInitError()).ShouldNot(HaveOccurred()) 851 kvErrors := txnErr.GetKVErrors() 852 Expect(kvErrors).To(HaveLen(1)) 853 Expect(kvErrors[0].Key).To(BeEquivalentTo(prefixA + baseValue1)) 854 Expect(kvErrors[0].TxnOperation).To(BeEquivalentTo(TxnOperation_UPDATE)) 855 Expect(kvErrors[0].Error.Error()).To(BeEquivalentTo("failed to modify value")) 856 857 // receive the status update with the value reverted back to the original 858 var valueStatus *BaseValueStatus 859 Eventually(statusChan, time.Second).Should(Receive(&valueStatus)) 860 checkBaseValueStatus(valueStatus, &BaseValueStatus{ 861 Value: &ValueStatus{ 862 Key: prefixA + baseValue1, 863 State: ValueState_CONFIGURED, 864 LastOperation: TxnOperation_UPDATE, 865 }, 866 DerivedValues: []*ValueStatus{ 867 { 868 Key: prefixA + baseValue1 + "/item2", 869 State: ValueState_CONFIGURED, 870 LastOperation: TxnOperation_CREATE, 871 }, 872 }, 873 }) 874 875 // check the state of SB 876 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 877 // -> base value 1 878 value := mockSB.GetValue(prefixA + baseValue1) 879 Expect(value).ToNot(BeNil()) 880 Expect(proto.Equal(value.Value, test.NewArrayValue("item2"))).To(BeTrue()) 881 Expect(value.Metadata).ToNot(BeNil()) 882 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 883 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 884 // -> item1 derived from base value was NOT added 885 value = mockSB.GetValue(prefixA + baseValue1 + "/item1") 886 Expect(value).To(BeNil()) 887 // -> item2 derived from base value 1 was first deleted by then added back 888 value = mockSB.GetValue(prefixA + baseValue1 + "/item2") 889 Expect(value).ToNot(BeNil()) 890 Expect(proto.Equal(value.Value, test.NewStringValue("item2"))).To(BeTrue()) 891 Expect(value.Metadata).To(BeNil()) 892 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 893 // -> base value 2 894 value = mockSB.GetValue(prefixB + baseValue2) 895 Expect(value).ToNot(BeNil()) 896 Expect(proto.Equal(value.Value, test.NewArrayValue("item1", "item2"))).To(BeTrue()) 897 Expect(value.Metadata).ToNot(BeNil()) 898 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 899 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 900 // -> item1 derived from base value 2 901 value = mockSB.GetValue(prefixB + baseValue2 + "/item1") 902 Expect(value).ToNot(BeNil()) 903 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 904 Expect(value.Metadata).To(BeNil()) 905 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 906 // -> item2 derived from base value 2 is still pending 907 value = mockSB.GetValue(prefixB + baseValue2 + "/item2") 908 Expect(value).To(BeNil()) 909 // -> base value 3 was reverted back to state after 1st txn 910 value = mockSB.GetValue(prefixC + baseValue3) 911 Expect(value).ToNot(BeNil()) 912 Expect(proto.Equal(value.Value, test.NewArrayValue("item1", "item2"))).To(BeTrue()) 913 Expect(value.Metadata).ToNot(BeNil()) 914 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(2)) 915 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 916 // -> item1 derived from base value 3 917 value = mockSB.GetValue(prefixC + baseValue3 + "/item1") 918 Expect(value).ToNot(BeNil()) 919 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 920 Expect(value.Metadata).To(BeNil()) 921 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 922 // -> item2 derived from base value 3 923 value = mockSB.GetValue(prefixC + baseValue3 + "/item2") 924 Expect(value).ToNot(BeNil()) 925 Expect(proto.Equal(value.Value, test.NewStringValue("item2"))).To(BeTrue()) 926 Expect(value.Metadata).To(BeNil()) 927 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 928 929 // check metadata 930 metadata, exists := nameToInteger1.LookupByName(baseValue1) 931 Expect(exists).To(BeTrue()) 932 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 933 metadata, exists = nameToInteger2.LookupByName(baseValue2) 934 Expect(exists).To(BeTrue()) 935 Expect(metadata.GetInteger()).To(BeEquivalentTo(0)) 936 metadata, exists = nameToInteger3.LookupByName(baseValue3) 937 Expect(exists).To(BeTrue()) 938 Expect(metadata.GetInteger()).To(BeEquivalentTo(2)) // re-created twice 939 940 // check operations executed in SB during 2nd txn 941 opHistory := mockSB.PopHistoryOfOps() 942 Expect(opHistory).To(HaveLen(15)) 943 operation := opHistory[0] 944 Expect(operation.OpType).To(Equal(test.MockDelete)) 945 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 946 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 947 Expect(operation.Err).To(BeNil()) 948 operation = opHistory[1] 949 Expect(operation.OpType).To(Equal(test.MockDelete)) 950 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 951 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item2")) 952 Expect(operation.Err).To(BeNil()) 953 operation = opHistory[2] 954 Expect(operation.OpType).To(Equal(test.MockDelete)) 955 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 956 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 957 Expect(operation.Err).To(BeNil()) 958 operation = opHistory[3] 959 Expect(operation.OpType).To(Equal(test.MockCreate)) 960 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 961 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 962 Expect(operation.Err).To(BeNil()) 963 operation = opHistory[4] 964 Expect(operation.OpType).To(Equal(test.MockCreate)) 965 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 966 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 967 Expect(operation.Err).To(BeNil()) 968 operation = opHistory[5] 969 Expect(operation.OpType).To(Equal(test.MockDelete)) 970 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 971 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item2")) 972 Expect(operation.Err).To(BeNil()) 973 operation = opHistory[6] 974 Expect(operation.OpType).To(Equal(test.MockUpdate)) 975 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 976 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1)) 977 Expect(operation.Err).ToNot(BeNil()) 978 Expect(operation.Err.Error()).To(BeEquivalentTo("failed to modify value")) 979 // reverting: 980 operation = opHistory[7] // refresh failed value 981 Expect(operation.OpType).To(Equal(test.MockRetrieve)) 982 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 983 checkValues(operation.CorrelateRetrieve, []KVWithMetadata{ 984 { 985 Key: prefixA + baseValue1, 986 Value: test.NewArrayValue("item1"), 987 Metadata: &test.OnlyInteger{Integer: 0}, 988 Origin: FromNB, 989 }, 990 }) 991 operation = opHistory[8] 992 Expect(operation.OpType).To(Equal(test.MockUpdate)) 993 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 994 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1)) 995 Expect(operation.Err).To(BeNil()) 996 operation = opHistory[9] 997 Expect(operation.OpType).To(Equal(test.MockCreate)) 998 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 999 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item2")) 1000 Expect(operation.Err).To(BeNil()) 1001 operation = opHistory[10] 1002 Expect(operation.OpType).To(Equal(test.MockDelete)) 1003 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 1004 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 1005 Expect(operation.Err).To(BeNil()) 1006 operation = opHistory[11] 1007 Expect(operation.OpType).To(Equal(test.MockDelete)) 1008 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 1009 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 1010 Expect(operation.Err).To(BeNil()) 1011 operation = opHistory[12] 1012 Expect(operation.OpType).To(Equal(test.MockCreate)) 1013 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 1014 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3)) 1015 Expect(operation.Err).To(BeNil()) 1016 operation = opHistory[13] 1017 Expect(operation.OpType).To(Equal(test.MockCreate)) 1018 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 1019 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item1")) 1020 Expect(operation.Err).To(BeNil()) 1021 operation = opHistory[14] 1022 Expect(operation.OpType).To(Equal(test.MockCreate)) 1023 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor3Name)) 1024 Expect(operation.Key).To(BeEquivalentTo(prefixC + baseValue3 + "/item2")) 1025 Expect(operation.Err).To(BeNil()) 1026 1027 // check transaction operations 1028 txnHistory := scheduler.GetTransactionHistory(startTime, time.Now()) 1029 Expect(txnHistory).To(HaveLen(1)) 1030 txn := txnHistory[0] 1031 Expect(txn.PreRecord).To(BeFalse()) 1032 Expect(txn.Start.After(startTime)).To(BeTrue()) 1033 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 1034 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 1035 Expect(txn.SeqNum).To(BeEquivalentTo(1)) 1036 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 1037 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 1038 Expect(txn.Description).To(BeEmpty()) 1039 checkRecordedValues(txn.Values, []RecordedKVPair{ 1040 {Key: prefixA + baseValue1, Value: utils.RecordProtoMessage(test.NewArrayValue("item1")), Origin: FromNB}, 1041 {Key: prefixC + baseValue3, Value: utils.RecordProtoMessage(test.NewArrayValue("item1")), Origin: FromNB}, 1042 }) 1043 1044 // planned operations 1045 txnOps := RecordedTxnOps{ 1046 { 1047 Operation: TxnOperation_DELETE, 1048 Key: prefixC + baseValue3 + "/item1", 1049 IsDerived: true, 1050 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1051 PrevState: ValueState_CONFIGURED, 1052 NewState: ValueState_REMOVED, 1053 IsRecreate: true, 1054 }, 1055 { 1056 Operation: TxnOperation_DELETE, 1057 Key: prefixC + baseValue3 + "/item2", 1058 IsDerived: true, 1059 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1060 PrevState: ValueState_CONFIGURED, 1061 NewState: ValueState_REMOVED, 1062 IsRecreate: true, 1063 }, 1064 { 1065 Operation: TxnOperation_DELETE, 1066 Key: prefixC + baseValue3, 1067 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), 1068 PrevState: ValueState_CONFIGURED, 1069 NewState: ValueState_REMOVED, 1070 IsRecreate: true, 1071 }, 1072 { 1073 Operation: TxnOperation_CREATE, 1074 Key: prefixC + baseValue3, 1075 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 1076 PrevState: ValueState_REMOVED, 1077 NewState: ValueState_CONFIGURED, 1078 IsRecreate: true, 1079 }, 1080 { 1081 Operation: TxnOperation_CREATE, 1082 Key: prefixC + baseValue3 + "/item1", 1083 IsDerived: true, 1084 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1085 PrevState: ValueState_NONEXISTENT, 1086 NewState: ValueState_CONFIGURED, 1087 IsRecreate: true, 1088 }, 1089 { 1090 Operation: TxnOperation_DELETE, 1091 Key: prefixA + baseValue1 + "/item2", 1092 IsDerived: true, 1093 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1094 PrevState: ValueState_CONFIGURED, 1095 NewState: ValueState_REMOVED, 1096 }, 1097 { 1098 Operation: TxnOperation_UPDATE, 1099 Key: prefixA + baseValue1, 1100 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item2")), 1101 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 1102 PrevState: ValueState_CONFIGURED, 1103 NewState: ValueState_CONFIGURED, 1104 }, 1105 { 1106 Operation: TxnOperation_CREATE, 1107 Key: prefixA + baseValue1 + "/item1", 1108 IsDerived: true, 1109 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1110 PrevState: ValueState_NONEXISTENT, 1111 NewState: ValueState_CONFIGURED, 1112 }, 1113 { 1114 Operation: TxnOperation_CREATE, 1115 Key: prefixB + baseValue2 + "/item2", 1116 IsDerived: true, 1117 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1118 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1119 PrevState: ValueState_PENDING, 1120 NewState: ValueState_CONFIGURED, 1121 }, 1122 } 1123 checkTxnOperations(txn.Planned, txnOps) 1124 1125 // executed operations 1126 txnOps = RecordedTxnOps{ 1127 { 1128 Operation: TxnOperation_DELETE, 1129 Key: prefixC + baseValue3 + "/item1", 1130 IsDerived: true, 1131 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1132 PrevState: ValueState_CONFIGURED, 1133 NewState: ValueState_REMOVED, 1134 IsRecreate: true, 1135 }, 1136 { 1137 Operation: TxnOperation_DELETE, 1138 Key: prefixC + baseValue3 + "/item2", 1139 IsDerived: true, 1140 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1141 PrevState: ValueState_CONFIGURED, 1142 NewState: ValueState_REMOVED, 1143 IsRecreate: true, 1144 }, 1145 { 1146 Operation: TxnOperation_DELETE, 1147 Key: prefixC + baseValue3, 1148 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), 1149 PrevState: ValueState_CONFIGURED, 1150 NewState: ValueState_REMOVED, 1151 IsRecreate: true, 1152 }, 1153 { 1154 Operation: TxnOperation_CREATE, 1155 Key: prefixC + baseValue3, 1156 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 1157 PrevState: ValueState_REMOVED, 1158 NewState: ValueState_CONFIGURED, 1159 IsRecreate: true, 1160 }, 1161 { 1162 Operation: TxnOperation_CREATE, 1163 Key: prefixC + baseValue3 + "/item1", 1164 IsDerived: true, 1165 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1166 PrevState: ValueState_NONEXISTENT, 1167 NewState: ValueState_CONFIGURED, 1168 IsRecreate: true, 1169 }, 1170 { 1171 Operation: TxnOperation_DELETE, 1172 Key: prefixA + baseValue1 + "/item2", 1173 IsDerived: true, 1174 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1175 PrevState: ValueState_CONFIGURED, 1176 NewState: ValueState_REMOVED, 1177 }, 1178 { 1179 Operation: TxnOperation_UPDATE, 1180 Key: prefixA + baseValue1, 1181 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item2")), 1182 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 1183 PrevState: ValueState_CONFIGURED, 1184 NewState: ValueState_FAILED, 1185 NewErr: errors.New("failed to modify value"), 1186 }, 1187 // reverting: 1188 { 1189 Operation: TxnOperation_UPDATE, 1190 Key: prefixA + baseValue1, 1191 PrevValue: utils.RecordProtoMessage(test.NewArrayValue()), 1192 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item2")), 1193 PrevState: ValueState_FAILED, 1194 NewState: ValueState_CONFIGURED, 1195 PrevErr: errors.New("failed to modify value"), 1196 IsRevert: true, 1197 }, 1198 { 1199 Operation: TxnOperation_CREATE, 1200 Key: prefixA + baseValue1 + "/item2", 1201 IsDerived: true, 1202 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1203 PrevState: ValueState_NONEXISTENT, 1204 NewState: ValueState_CONFIGURED, 1205 IsRevert: true, 1206 }, 1207 { 1208 Operation: TxnOperation_DELETE, 1209 Key: prefixC + baseValue3 + "/item1", 1210 IsDerived: true, 1211 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1212 PrevState: ValueState_CONFIGURED, 1213 NewState: ValueState_REMOVED, 1214 IsRevert: true, 1215 IsRecreate: true, 1216 }, 1217 { 1218 Operation: TxnOperation_DELETE, 1219 Key: prefixC + baseValue3, 1220 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 1221 PrevState: ValueState_CONFIGURED, 1222 NewState: ValueState_REMOVED, 1223 IsRevert: true, 1224 IsRecreate: true, 1225 }, 1226 { 1227 Operation: TxnOperation_CREATE, 1228 Key: prefixC + baseValue3, 1229 NewValue: utils.RecordProtoMessage(test.NewArrayValue("item1", "item2")), 1230 PrevState: ValueState_REMOVED, 1231 NewState: ValueState_CONFIGURED, 1232 IsRevert: true, 1233 IsRecreate: true, 1234 }, 1235 { 1236 Operation: TxnOperation_CREATE, 1237 Key: prefixC + baseValue3 + "/item1", 1238 IsDerived: true, 1239 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1240 PrevState: ValueState_NONEXISTENT, 1241 NewState: ValueState_CONFIGURED, 1242 IsRevert: true, 1243 IsRecreate: true, 1244 }, 1245 { 1246 Operation: TxnOperation_CREATE, 1247 Key: prefixC + baseValue3 + "/item2", 1248 IsDerived: true, 1249 NewValue: utils.RecordProtoMessage(test.NewStringValue("item2")), 1250 PrevState: ValueState_NONEXISTENT, 1251 NewState: ValueState_CONFIGURED, 1252 IsRevert: true, 1253 }, 1254 } 1255 checkTxnOperations(txn.Executed, txnOps) 1256 1257 // check flag stats 1258 graphR := scheduler.graph.Read() 1259 errorStats := graphR.GetFlagStats(ErrorFlagIndex, nil) 1260 Expect(errorStats.TotalCount).To(BeEquivalentTo(1)) 1261 pendingStats := graphR.GetFlagStats(UnavailValueFlagIndex, nil) 1262 Expect(pendingStats.TotalCount).To(BeEquivalentTo(1)) 1263 derivedStats := graphR.GetFlagStats(DerivedFlagIndex, nil) 1264 Expect(derivedStats.TotalCount).To(BeEquivalentTo(9)) 1265 lastUpdateStats := graphR.GetFlagStats(LastUpdateFlagIndex, nil) 1266 Expect(lastUpdateStats.TotalCount).To(BeEquivalentTo(16)) 1267 descriptorStats := graphR.GetFlagStats(DescriptorFlagIndex, nil) 1268 Expect(descriptorStats.TotalCount).To(BeEquivalentTo(16)) 1269 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor1Name)) 1270 Expect(descriptorStats.PerValueCount[descriptor1Name]).To(BeEquivalentTo(5)) 1271 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor2Name)) 1272 Expect(descriptorStats.PerValueCount[descriptor2Name]).To(BeEquivalentTo(3)) 1273 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor3Name)) 1274 Expect(descriptorStats.PerValueCount[descriptor3Name]).To(BeEquivalentTo(8)) 1275 valueStateStats := graphR.GetFlagStats(ValueStateFlagIndex, nil) 1276 Expect(valueStateStats.TotalCount).To(BeEquivalentTo(16)) 1277 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_CONFIGURED.String())) 1278 Expect(valueStateStats.PerValueCount[ValueState_CONFIGURED.String()]).To(BeEquivalentTo(14)) 1279 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_FAILED.String())) 1280 Expect(valueStateStats.PerValueCount[ValueState_FAILED.String()]).To(BeEquivalentTo(1)) 1281 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_PENDING.String())) 1282 Expect(valueStateStats.PerValueCount[ValueState_PENDING.String()]).To(BeEquivalentTo(1)) 1283 graphR.Release() 1284 1285 // close scheduler 1286 err = scheduler.Close() 1287 Expect(err).To(BeNil()) 1288 } 1289 1290 func TestDependencyCycles(t *testing.T) { 1291 RegisterTestingT(t) 1292 1293 // prepare KV Scheduler 1294 scheduler := NewPlugin(UseDeps(func(deps *Deps) { 1295 deps.HTTPHandlers = nil 1296 })) 1297 err := scheduler.Init() 1298 Expect(err).To(BeNil()) 1299 1300 // prepare mocks 1301 mockSB := test.NewMockSouthbound() 1302 // -> descriptor: 1303 descriptor := test.NewMockDescriptor(&KVDescriptor{ 1304 Name: descriptor1Name, 1305 KeySelector: prefixSelector(prefixA), 1306 NBKeyPrefix: prefixA, 1307 ValueTypeName: string(proto.MessageName(test.NewStringValue(""))), 1308 ValueComparator: test.StringValueComparator, 1309 Dependencies: func(key string, value proto.Message) []Dependency { 1310 if key == prefixA+baseValue1 { 1311 depKey := prefixA + baseValue2 1312 return []Dependency{ 1313 {Label: depKey, Key: depKey}, 1314 } 1315 } 1316 if key == prefixA+baseValue2 { 1317 depKey := prefixA + baseValue3 1318 return []Dependency{ 1319 {Label: depKey, Key: depKey}, 1320 } 1321 } 1322 if key == prefixA+baseValue3 { 1323 depKey1 := prefixA + baseValue1 1324 depKey2 := prefixA + baseValue4 1325 return []Dependency{ 1326 {Label: depKey1, Key: depKey1}, 1327 {Label: depKey2, Key: depKey2}, 1328 } 1329 } 1330 return nil 1331 }, 1332 WithMetadata: false, 1333 }, mockSB, 0, test.WithoutRetrieve) 1334 1335 // register the descriptor 1336 scheduler.RegisterKVDescriptor(descriptor) 1337 1338 // run non-resync transaction against empty SB 1339 startTime := time.Now() 1340 schedulerTxn := scheduler.StartNBTransaction() 1341 schedulerTxn.SetValue(prefixA+baseValue1, test.NewStringValue("base-value1-data")) 1342 schedulerTxn.SetValue(prefixA+baseValue2, test.NewStringValue("base-value2-data")) 1343 schedulerTxn.SetValue(prefixA+baseValue3, test.NewStringValue("base-value3-data")) 1344 description := "testing dependency cycles" 1345 seqNum, err := schedulerTxn.Commit(WithDescription(testCtx, description)) 1346 stopTime := time.Now() 1347 Expect(seqNum).To(BeEquivalentTo(0)) 1348 Expect(err).ShouldNot(HaveOccurred()) 1349 1350 // check the state of SB 1351 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 1352 Expect(mockSB.GetValues(nil)).To(HaveLen(0)) 1353 1354 // check value states 1355 status := scheduler.GetValueStatus(prefixA + baseValue1) 1356 Expect(status).ToNot(BeNil()) 1357 checkBaseValueStatus(status, &BaseValueStatus{ 1358 Value: &ValueStatus{ 1359 Key: prefixA + baseValue1, 1360 State: ValueState_PENDING, 1361 LastOperation: TxnOperation_CREATE, 1362 Details: []string{prefixA + baseValue2}, 1363 }, 1364 }) 1365 status = scheduler.GetValueStatus(prefixA + baseValue2) 1366 Expect(status).ToNot(BeNil()) 1367 checkBaseValueStatus(status, &BaseValueStatus{ 1368 Value: &ValueStatus{ 1369 Key: prefixA + baseValue2, 1370 State: ValueState_PENDING, 1371 LastOperation: TxnOperation_CREATE, 1372 Details: []string{prefixA + baseValue3}, 1373 }, 1374 }) 1375 status = scheduler.GetValueStatus(prefixA + baseValue3) 1376 Expect(status).ToNot(BeNil()) 1377 checkBaseValueStatus(status, &BaseValueStatus{ 1378 Value: &ValueStatus{ 1379 Key: prefixA + baseValue3, 1380 State: ValueState_PENDING, 1381 LastOperation: TxnOperation_CREATE, 1382 Details: []string{prefixA + baseValue4, prefixA + baseValue1}, 1383 }, 1384 }) 1385 1386 // check operations executed in SB 1387 opHistory := mockSB.PopHistoryOfOps() 1388 Expect(opHistory).To(HaveLen(0)) 1389 1390 // check transaction operations 1391 txnHistory := scheduler.GetTransactionHistory(time.Time{}, time.Now()) 1392 Expect(txnHistory).To(HaveLen(1)) 1393 txn := txnHistory[0] 1394 Expect(txn.PreRecord).To(BeFalse()) 1395 Expect(txn.Start.After(startTime)).To(BeTrue()) 1396 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 1397 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 1398 Expect(txn.SeqNum).To(BeEquivalentTo(0)) 1399 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 1400 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 1401 Expect(txn.Description).To(Equal(description)) 1402 checkRecordedValues(txn.Values, []RecordedKVPair{ 1403 {Key: prefixA + baseValue1, Value: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), Origin: FromNB}, 1404 {Key: prefixA + baseValue2, Value: utils.RecordProtoMessage(test.NewStringValue("base-value2-data")), Origin: FromNB}, 1405 {Key: prefixA + baseValue3, Value: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), Origin: FromNB}, 1406 }) 1407 1408 txnOps := RecordedTxnOps{ 1409 { 1410 Operation: TxnOperation_CREATE, 1411 Key: prefixA + baseValue1, 1412 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1413 PrevState: ValueState_NONEXISTENT, 1414 NewState: ValueState_PENDING, 1415 NOOP: true, 1416 }, 1417 { 1418 Operation: TxnOperation_CREATE, 1419 Key: prefixA + baseValue2, 1420 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value2-data")), 1421 PrevState: ValueState_NONEXISTENT, 1422 NewState: ValueState_PENDING, 1423 NOOP: true, 1424 }, 1425 { 1426 Operation: TxnOperation_CREATE, 1427 Key: prefixA + baseValue3, 1428 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1429 PrevState: ValueState_NONEXISTENT, 1430 NewState: ValueState_PENDING, 1431 NOOP: true, 1432 }, 1433 } 1434 checkTxnOperations(txn.Planned, txnOps) 1435 checkTxnOperations(txn.Executed, txnOps) 1436 1437 // check flag stats 1438 graphR := scheduler.graph.Read() 1439 errorStats := graphR.GetFlagStats(ErrorFlagIndex, nil) 1440 Expect(errorStats.TotalCount).To(BeEquivalentTo(0)) 1441 pendingStats := graphR.GetFlagStats(UnavailValueFlagIndex, nil) 1442 Expect(pendingStats.TotalCount).To(BeEquivalentTo(3)) 1443 derivedStats := graphR.GetFlagStats(DerivedFlagIndex, nil) 1444 Expect(derivedStats.TotalCount).To(BeEquivalentTo(0)) 1445 lastUpdateStats := graphR.GetFlagStats(LastUpdateFlagIndex, nil) 1446 Expect(lastUpdateStats.TotalCount).To(BeEquivalentTo(3)) 1447 descriptorStats := graphR.GetFlagStats(DescriptorFlagIndex, nil) 1448 Expect(descriptorStats.TotalCount).To(BeEquivalentTo(3)) 1449 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor1Name)) 1450 Expect(descriptorStats.PerValueCount[descriptor1Name]).To(BeEquivalentTo(3)) 1451 valueStateStats := graphR.GetFlagStats(ValueStateFlagIndex, nil) 1452 Expect(valueStateStats.TotalCount).To(BeEquivalentTo(3)) 1453 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_PENDING.String())) 1454 Expect(valueStateStats.PerValueCount[ValueState_PENDING.String()]).To(BeEquivalentTo(3)) 1455 graphR.Release() 1456 1457 // run second transaction that will make the cycle of values ready to be added 1458 startTime = time.Now() 1459 schedulerTxn = scheduler.StartNBTransaction() 1460 schedulerTxn.SetValue(prefixA+baseValue4, test.NewStringValue("base-value4-data")) 1461 seqNum, err = schedulerTxn.Commit(testCtx) 1462 stopTime = time.Now() 1463 Expect(seqNum).To(BeEquivalentTo(1)) 1464 Expect(err).ShouldNot(HaveOccurred()) 1465 1466 // check the state of SB 1467 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 1468 Expect(mockSB.GetValues(nil)).To(HaveLen(4)) 1469 // -> base value 1 1470 value := mockSB.GetValue(prefixA + baseValue1) 1471 Expect(value).ToNot(BeNil()) 1472 Expect(proto.Equal(value.Value, test.NewStringValue("base-value1-data"))).To(BeTrue()) 1473 Expect(value.Metadata).To(BeNil()) 1474 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1475 // -> base value 2 1476 value = mockSB.GetValue(prefixA + baseValue2) 1477 Expect(value).ToNot(BeNil()) 1478 Expect(proto.Equal(value.Value, test.NewStringValue("base-value2-data"))).To(BeTrue()) 1479 Expect(value.Metadata).To(BeNil()) 1480 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1481 // -> base value 3 1482 value = mockSB.GetValue(prefixA + baseValue3) 1483 Expect(value).ToNot(BeNil()) 1484 Expect(proto.Equal(value.Value, test.NewStringValue("base-value3-data"))).To(BeTrue()) 1485 Expect(value.Metadata).To(BeNil()) 1486 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1487 // -> base value 4 1488 value = mockSB.GetValue(prefixA + baseValue4) 1489 Expect(value).ToNot(BeNil()) 1490 Expect(proto.Equal(value.Value, test.NewStringValue("base-value4-data"))).To(BeTrue()) 1491 Expect(value.Metadata).To(BeNil()) 1492 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1493 1494 // check value states 1495 status = scheduler.GetValueStatus(prefixA + baseValue1) 1496 Expect(status).ToNot(BeNil()) 1497 checkBaseValueStatus(status, &BaseValueStatus{ 1498 Value: &ValueStatus{ 1499 Key: prefixA + baseValue1, 1500 State: ValueState_CONFIGURED, 1501 LastOperation: TxnOperation_CREATE, 1502 }, 1503 }) 1504 status = scheduler.GetValueStatus(prefixA + baseValue2) 1505 Expect(status).ToNot(BeNil()) 1506 checkBaseValueStatus(status, &BaseValueStatus{ 1507 Value: &ValueStatus{ 1508 Key: prefixA + baseValue2, 1509 State: ValueState_CONFIGURED, 1510 LastOperation: TxnOperation_CREATE, 1511 }, 1512 }) 1513 status = scheduler.GetValueStatus(prefixA + baseValue3) 1514 Expect(status).ToNot(BeNil()) 1515 checkBaseValueStatus(status, &BaseValueStatus{ 1516 Value: &ValueStatus{ 1517 Key: prefixA + baseValue3, 1518 State: ValueState_CONFIGURED, 1519 LastOperation: TxnOperation_CREATE, 1520 }, 1521 }) 1522 status = scheduler.GetValueStatus(prefixA + baseValue4) 1523 Expect(status).ToNot(BeNil()) 1524 checkBaseValueStatus(status, &BaseValueStatus{ 1525 Value: &ValueStatus{ 1526 Key: prefixA + baseValue4, 1527 State: ValueState_CONFIGURED, 1528 LastOperation: TxnOperation_CREATE, 1529 }, 1530 }) 1531 1532 // check operations executed in SB 1533 opHistory = mockSB.PopHistoryOfOps() 1534 Expect(opHistory).To(HaveLen(4)) 1535 operation := opHistory[0] 1536 Expect(operation.OpType).To(Equal(test.MockCreate)) 1537 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1538 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue4)) 1539 Expect(operation.Err).To(BeNil()) 1540 operation = opHistory[1] 1541 Expect(operation.OpType).To(Equal(test.MockCreate)) 1542 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1543 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue3)) 1544 Expect(operation.Err).To(BeNil()) 1545 operation = opHistory[2] 1546 Expect(operation.OpType).To(Equal(test.MockCreate)) 1547 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1548 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue2)) 1549 Expect(operation.Err).To(BeNil()) 1550 operation = opHistory[3] 1551 Expect(operation.OpType).To(Equal(test.MockCreate)) 1552 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1553 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1)) 1554 Expect(operation.Err).To(BeNil()) 1555 1556 // check transaction operations 1557 txnHistory = scheduler.GetTransactionHistory(time.Time{}, time.Now()) 1558 Expect(txnHistory).To(HaveLen(2)) 1559 txn = txnHistory[1] 1560 Expect(txn.PreRecord).To(BeFalse()) 1561 Expect(txn.Start.After(startTime)).To(BeTrue()) 1562 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 1563 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 1564 Expect(txn.SeqNum).To(BeEquivalentTo(1)) 1565 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 1566 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 1567 Expect(txn.Description).To(BeEmpty()) 1568 checkRecordedValues(txn.Values, []RecordedKVPair{ 1569 {Key: prefixA + baseValue4, Value: utils.RecordProtoMessage(test.NewStringValue("base-value4-data")), Origin: FromNB}, 1570 }) 1571 1572 txnOps = RecordedTxnOps{ 1573 { 1574 Operation: TxnOperation_CREATE, 1575 Key: prefixA + baseValue4, 1576 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value4-data")), 1577 PrevState: ValueState_NONEXISTENT, 1578 NewState: ValueState_CONFIGURED, 1579 }, 1580 { 1581 Operation: TxnOperation_CREATE, 1582 Key: prefixA + baseValue3, 1583 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1584 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1585 PrevState: ValueState_PENDING, 1586 NewState: ValueState_CONFIGURED, 1587 }, 1588 { 1589 Operation: TxnOperation_CREATE, 1590 Key: prefixA + baseValue2, 1591 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value2-data")), 1592 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value2-data")), 1593 PrevState: ValueState_PENDING, 1594 NewState: ValueState_CONFIGURED, 1595 }, 1596 { 1597 Operation: TxnOperation_CREATE, 1598 Key: prefixA + baseValue1, 1599 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1600 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1601 PrevState: ValueState_PENDING, 1602 NewState: ValueState_CONFIGURED, 1603 }, 1604 } 1605 checkTxnOperations(txn.Planned, txnOps) 1606 checkTxnOperations(txn.Executed, txnOps) 1607 1608 // check flag stats 1609 graphR = scheduler.graph.Read() 1610 errorStats = graphR.GetFlagStats(ErrorFlagIndex, nil) 1611 Expect(errorStats.TotalCount).To(BeEquivalentTo(0)) 1612 pendingStats = graphR.GetFlagStats(UnavailValueFlagIndex, nil) 1613 Expect(pendingStats.TotalCount).To(BeEquivalentTo(3)) 1614 derivedStats = graphR.GetFlagStats(DerivedFlagIndex, nil) 1615 Expect(derivedStats.TotalCount).To(BeEquivalentTo(0)) 1616 lastUpdateStats = graphR.GetFlagStats(LastUpdateFlagIndex, nil) 1617 Expect(lastUpdateStats.TotalCount).To(BeEquivalentTo(7)) 1618 descriptorStats = graphR.GetFlagStats(DescriptorFlagIndex, nil) 1619 Expect(descriptorStats.TotalCount).To(BeEquivalentTo(7)) 1620 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor1Name)) 1621 Expect(descriptorStats.PerValueCount[descriptor1Name]).To(BeEquivalentTo(7)) 1622 valueStateStats = graphR.GetFlagStats(ValueStateFlagIndex, nil) 1623 Expect(valueStateStats.TotalCount).To(BeEquivalentTo(7)) 1624 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_PENDING.String())) 1625 Expect(valueStateStats.PerValueCount[ValueState_PENDING.String()]).To(BeEquivalentTo(3)) 1626 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_CONFIGURED.String())) 1627 Expect(valueStateStats.PerValueCount[ValueState_CONFIGURED.String()]).To(BeEquivalentTo(4)) 1628 graphR.Release() 1629 1630 // plan error before 3rd txn 1631 mockSB.PlanError(prefixA+baseValue2, errors.New("failed to remove the value"), nil) 1632 1633 // run third transaction that will break the cycle even though the delete operation will fail 1634 startTime = time.Now() 1635 schedulerTxn = scheduler.StartNBTransaction() 1636 schedulerTxn.SetValue(prefixA+baseValue2, nil) 1637 seqNum, err = schedulerTxn.Commit(testCtx) 1638 stopTime = time.Now() 1639 Expect(seqNum).To(BeEquivalentTo(2)) 1640 Expect(err).ToNot(BeNil()) 1641 txnErr := err.(*TransactionError) 1642 Expect(txnErr.GetTxnInitError()).ShouldNot(HaveOccurred()) 1643 kvErrors := txnErr.GetKVErrors() 1644 Expect(kvErrors).To(HaveLen(1)) 1645 Expect(kvErrors[0].Key).To(BeEquivalentTo(prefixA + baseValue2)) 1646 Expect(kvErrors[0].TxnOperation).To(BeEquivalentTo(TxnOperation_DELETE)) 1647 Expect(kvErrors[0].Error.Error()).To(BeEquivalentTo("failed to remove the value")) 1648 1649 // check the state of SB 1650 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 1651 Expect(mockSB.GetValues(nil)).To(HaveLen(2)) 1652 // -> base value 1 - pending 1653 value = mockSB.GetValue(prefixA + baseValue1) 1654 Expect(value).To(BeNil()) 1655 // -> base value 2 - failed to remove 1656 value = mockSB.GetValue(prefixA + baseValue2) 1657 Expect(value).ToNot(BeNil()) 1658 Expect(proto.Equal(value.Value, test.NewStringValue("base-value2-data"))).To(BeTrue()) 1659 Expect(value.Metadata).To(BeNil()) 1660 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1661 // -> base value 3 - pending 1662 value = mockSB.GetValue(prefixA + baseValue3) 1663 Expect(value).To(BeNil()) 1664 // -> base value 4 1665 value = mockSB.GetValue(prefixA + baseValue4) 1666 Expect(value).ToNot(BeNil()) 1667 Expect(proto.Equal(value.Value, test.NewStringValue("base-value4-data"))).To(BeTrue()) 1668 Expect(value.Metadata).To(BeNil()) 1669 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1670 1671 // check value states 1672 status = scheduler.GetValueStatus(prefixA + baseValue1) 1673 Expect(status).ToNot(BeNil()) 1674 checkBaseValueStatus(status, &BaseValueStatus{ 1675 Value: &ValueStatus{ 1676 Key: prefixA + baseValue1, 1677 State: ValueState_PENDING, 1678 LastOperation: TxnOperation_DELETE, 1679 Details: []string{prefixA + baseValue2}, 1680 }, 1681 }) 1682 status = scheduler.GetValueStatus(prefixA + baseValue2) 1683 Expect(status).ToNot(BeNil()) 1684 checkBaseValueStatus(status, &BaseValueStatus{ 1685 Value: &ValueStatus{ 1686 Key: prefixA + baseValue2, 1687 State: ValueState_FAILED, 1688 LastOperation: TxnOperation_DELETE, 1689 Error: "failed to remove the value", 1690 }, 1691 }) 1692 status = scheduler.GetValueStatus(prefixA + baseValue3) 1693 Expect(status).ToNot(BeNil()) 1694 checkBaseValueStatus(status, &BaseValueStatus{ 1695 Value: &ValueStatus{ 1696 Key: prefixA + baseValue3, 1697 State: ValueState_PENDING, 1698 LastOperation: TxnOperation_DELETE, 1699 Details: []string{prefixA + baseValue1}, 1700 }, 1701 }) 1702 status = scheduler.GetValueStatus(prefixA + baseValue4) 1703 Expect(status).ToNot(BeNil()) 1704 checkBaseValueStatus(status, &BaseValueStatus{ 1705 Value: &ValueStatus{ 1706 Key: prefixA + baseValue4, 1707 State: ValueState_CONFIGURED, 1708 LastOperation: TxnOperation_CREATE, 1709 }, 1710 }) 1711 1712 // check operations executed in SB 1713 opHistory = mockSB.PopHistoryOfOps() 1714 Expect(opHistory).To(HaveLen(3)) 1715 operation = opHistory[0] 1716 Expect(operation.OpType).To(Equal(test.MockDelete)) 1717 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1718 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue3)) 1719 Expect(operation.Err).To(BeNil()) 1720 operation = opHistory[1] 1721 Expect(operation.OpType).To(Equal(test.MockDelete)) 1722 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1723 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue1)) 1724 Expect(operation.Err).To(BeNil()) 1725 operation = opHistory[2] 1726 Expect(operation.OpType).To(Equal(test.MockDelete)) 1727 Expect(operation.Descriptor).To(BeEquivalentTo(descriptor1Name)) 1728 Expect(operation.Key).To(BeEquivalentTo(prefixA + baseValue2)) 1729 Expect(operation.Err.Error()).To(BeEquivalentTo("failed to remove the value")) 1730 1731 // check transaction operations 1732 txnHistory = scheduler.GetTransactionHistory(time.Time{}, time.Now()) 1733 Expect(txnHistory).To(HaveLen(3)) 1734 txn = txnHistory[2] 1735 Expect(txn.PreRecord).To(BeFalse()) 1736 Expect(txn.Start.After(startTime)).To(BeTrue()) 1737 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 1738 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 1739 Expect(txn.SeqNum).To(BeEquivalentTo(2)) 1740 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 1741 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 1742 Expect(txn.Description).To(BeEmpty()) 1743 checkRecordedValues(txn.Values, []RecordedKVPair{ 1744 {Key: prefixA + baseValue2, Value: utils.RecordProtoMessage(nil), Origin: FromNB}, 1745 }) 1746 1747 // -> planned 1748 txnOps = RecordedTxnOps{ 1749 { 1750 Operation: TxnOperation_DELETE, 1751 Key: prefixA + baseValue3, 1752 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1753 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1754 PrevState: ValueState_CONFIGURED, 1755 NewState: ValueState_PENDING, 1756 }, 1757 { 1758 Operation: TxnOperation_DELETE, 1759 Key: prefixA + baseValue1, 1760 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1761 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1762 PrevState: ValueState_CONFIGURED, 1763 NewState: ValueState_PENDING, 1764 }, 1765 { 1766 Operation: TxnOperation_DELETE, 1767 Key: prefixA + baseValue2, 1768 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value2-data")), 1769 PrevState: ValueState_CONFIGURED, 1770 NewState: ValueState_REMOVED, 1771 }, 1772 } 1773 checkTxnOperations(txn.Planned, txnOps) 1774 1775 // -> executed 1776 txnOps = RecordedTxnOps{ 1777 { 1778 Operation: TxnOperation_DELETE, 1779 Key: prefixA + baseValue3, 1780 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1781 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value3-data")), 1782 PrevState: ValueState_CONFIGURED, 1783 NewState: ValueState_PENDING, 1784 }, 1785 { 1786 Operation: TxnOperation_DELETE, 1787 Key: prefixA + baseValue1, 1788 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1789 NewValue: utils.RecordProtoMessage(test.NewStringValue("base-value1-data")), 1790 PrevState: ValueState_CONFIGURED, 1791 NewState: ValueState_PENDING, 1792 }, 1793 { 1794 Operation: TxnOperation_DELETE, 1795 Key: prefixA + baseValue2, 1796 PrevValue: utils.RecordProtoMessage(test.NewStringValue("base-value2-data")), 1797 PrevState: ValueState_CONFIGURED, 1798 NewState: ValueState_FAILED, 1799 NewErr: errors.New("failed to remove the value"), 1800 }, 1801 } 1802 checkTxnOperations(txn.Executed, txnOps) 1803 1804 // check flag stats 1805 graphR = scheduler.graph.Read() 1806 errorStats = graphR.GetFlagStats(ErrorFlagIndex, nil) 1807 Expect(errorStats.TotalCount).To(BeEquivalentTo(1)) 1808 pendingStats = graphR.GetFlagStats(UnavailValueFlagIndex, nil) 1809 Expect(pendingStats.TotalCount).To(BeEquivalentTo(6)) 1810 derivedStats = graphR.GetFlagStats(DerivedFlagIndex, nil) 1811 Expect(derivedStats.TotalCount).To(BeEquivalentTo(0)) 1812 lastUpdateStats = graphR.GetFlagStats(LastUpdateFlagIndex, nil) 1813 Expect(lastUpdateStats.TotalCount).To(BeEquivalentTo(10)) 1814 descriptorStats = graphR.GetFlagStats(DescriptorFlagIndex, nil) 1815 Expect(descriptorStats.TotalCount).To(BeEquivalentTo(10)) 1816 Expect(descriptorStats.PerValueCount).To(HaveKey(descriptor1Name)) 1817 Expect(descriptorStats.PerValueCount[descriptor1Name]).To(BeEquivalentTo(10)) 1818 valueStateStats = graphR.GetFlagStats(ValueStateFlagIndex, nil) 1819 Expect(valueStateStats.TotalCount).To(BeEquivalentTo(10)) 1820 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_PENDING.String())) 1821 Expect(valueStateStats.PerValueCount[ValueState_PENDING.String()]).To(BeEquivalentTo(5)) 1822 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_CONFIGURED.String())) 1823 Expect(valueStateStats.PerValueCount[ValueState_CONFIGURED.String()]).To(BeEquivalentTo(4)) 1824 Expect(valueStateStats.PerValueCount).To(HaveKey(ValueState_FAILED.String())) 1825 Expect(valueStateStats.PerValueCount[ValueState_FAILED.String()]).To(BeEquivalentTo(1)) 1826 graphR.Release() 1827 1828 // finally, run 4th txn to get back the removed value 1829 schedulerTxn = scheduler.StartNBTransaction() 1830 schedulerTxn.SetValue(prefixA+baseValue2, test.NewStringValue("base-value2-data-new")) 1831 seqNum, err = schedulerTxn.Commit(testCtx) 1832 Expect(seqNum).To(BeEquivalentTo(3)) 1833 Expect(err).ShouldNot(HaveOccurred()) 1834 1835 // check the state of SB 1836 //Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) <- there is a validation error, but that's OK since descriptor does not define Retrieve 1837 Expect(mockSB.GetValues(nil)).To(HaveLen(4)) 1838 // -> base value 1 1839 value = mockSB.GetValue(prefixA + baseValue1) 1840 Expect(value).ToNot(BeNil()) 1841 Expect(proto.Equal(value.Value, test.NewStringValue("base-value1-data"))).To(BeTrue()) 1842 Expect(value.Metadata).To(BeNil()) 1843 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1844 // -> base value 2 1845 value = mockSB.GetValue(prefixA + baseValue2) 1846 Expect(value).ToNot(BeNil()) 1847 Expect(proto.Equal(value.Value, test.NewStringValue("base-value2-data-new"))).To(BeTrue()) 1848 Expect(value.Metadata).To(BeNil()) 1849 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1850 // -> base value 3 1851 value = mockSB.GetValue(prefixA + baseValue3) 1852 Expect(value).ToNot(BeNil()) 1853 Expect(proto.Equal(value.Value, test.NewStringValue("base-value3-data"))).To(BeTrue()) 1854 Expect(value.Metadata).To(BeNil()) 1855 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1856 // -> base value 4 1857 value = mockSB.GetValue(prefixA + baseValue4) 1858 Expect(value).ToNot(BeNil()) 1859 Expect(proto.Equal(value.Value, test.NewStringValue("base-value4-data"))).To(BeTrue()) 1860 Expect(value.Metadata).To(BeNil()) 1861 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1862 1863 // check value states 1864 status = scheduler.GetValueStatus(prefixA + baseValue1) 1865 Expect(status).ToNot(BeNil()) 1866 checkBaseValueStatus(status, &BaseValueStatus{ 1867 Value: &ValueStatus{ 1868 Key: prefixA + baseValue1, 1869 State: ValueState_CONFIGURED, 1870 LastOperation: TxnOperation_CREATE, 1871 }, 1872 }) 1873 status = scheduler.GetValueStatus(prefixA + baseValue2) 1874 Expect(status).ToNot(BeNil()) 1875 checkBaseValueStatus(status, &BaseValueStatus{ 1876 Value: &ValueStatus{ 1877 Key: prefixA + baseValue2, 1878 State: ValueState_CONFIGURED, 1879 LastOperation: TxnOperation_CREATE, 1880 }, 1881 }) 1882 status = scheduler.GetValueStatus(prefixA + baseValue3) 1883 Expect(status).ToNot(BeNil()) 1884 checkBaseValueStatus(status, &BaseValueStatus{ 1885 Value: &ValueStatus{ 1886 Key: prefixA + baseValue3, 1887 State: ValueState_CONFIGURED, 1888 LastOperation: TxnOperation_CREATE, 1889 }, 1890 }) 1891 status = scheduler.GetValueStatus(prefixA + baseValue4) 1892 Expect(status).ToNot(BeNil()) 1893 checkBaseValueStatus(status, &BaseValueStatus{ 1894 Value: &ValueStatus{ 1895 Key: prefixA + baseValue4, 1896 State: ValueState_CONFIGURED, 1897 LastOperation: TxnOperation_CREATE, 1898 }, 1899 }) 1900 } 1901 1902 func TestFailedDeleteOfDerivedValue(t *testing.T) { 1903 RegisterTestingT(t) 1904 1905 // prepare KV Scheduler 1906 scheduler := NewPlugin(UseDeps(func(deps *Deps) { 1907 deps.HTTPHandlers = nil 1908 })) 1909 err := scheduler.Init() 1910 Expect(err).To(BeNil()) 1911 1912 // prepare mocks 1913 mockSB := test.NewMockSouthbound() 1914 // descriptor: 1915 descriptor := test.NewMockDescriptor(&KVDescriptor{ 1916 Name: descriptor1Name, 1917 NBKeyPrefix: prefixA, 1918 KeySelector: prefixSelector(prefixA), 1919 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 1920 DerivedValues: test.ArrayValueDerBuilder, 1921 WithMetadata: true, 1922 }, mockSB, 0) 1923 scheduler.RegisterKVDescriptor(descriptor) 1924 1925 // run non-resync transaction against empty SB 1926 schedulerTxn := scheduler.StartNBTransaction() 1927 schedulerTxn.SetValue(prefixA+baseValue1, test.NewArrayValue("item1")) 1928 seqNum, err := schedulerTxn.Commit(testCtx) 1929 Expect(seqNum).To(BeEquivalentTo(0)) 1930 Expect(err).ShouldNot(HaveOccurred()) 1931 1932 // check the state of SB 1933 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 1934 // -> base value 1 1935 value := mockSB.GetValue(prefixA + baseValue1) 1936 Expect(value).ToNot(BeNil()) 1937 Expect(proto.Equal(value.Value, test.NewArrayValue("item1"))).To(BeTrue()) 1938 Expect(value.Metadata).ToNot(BeNil()) 1939 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 1940 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1941 // -> item1 derived from base value 1 1942 value = mockSB.GetValue(prefixA + baseValue1 + "/item1") 1943 Expect(value).ToNot(BeNil()) 1944 Expect(proto.Equal(value.Value, test.NewStringValue("item1"))).To(BeTrue()) 1945 Expect(value.Metadata).To(BeNil()) 1946 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 1947 1948 // plan error before 2nd txn 1949 failedDeleteClb := func() { 1950 mockSB.SetValue(prefixA+baseValue1, test.NewArrayValue("item1"), 1951 &test.OnlyInteger{Integer: 0}, FromNB, false) 1952 } 1953 mockSB.PlanError(prefixA+baseValue1+"/item1", errors.New("failed to delete value"), failedDeleteClb) 1954 1955 // run 2nd non-resync transaction that will have errors 1956 startTime := time.Now() 1957 schedulerTxn2 := scheduler.StartNBTransaction() 1958 schedulerTxn2.SetValue(prefixA+baseValue1, nil) 1959 seqNum, err = schedulerTxn2.Commit(testCtx) 1960 stopTime := time.Now() 1961 Expect(seqNum).To(BeEquivalentTo(1)) 1962 Expect(err).ToNot(BeNil()) 1963 txnErr := err.(*TransactionError) 1964 Expect(txnErr.GetTxnInitError()).ShouldNot(HaveOccurred()) 1965 kvErrors := txnErr.GetKVErrors() 1966 Expect(kvErrors).To(HaveLen(1)) 1967 Expect(kvErrors[0].Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item1")) 1968 Expect(kvErrors[0].TxnOperation).To(BeEquivalentTo(TxnOperation_DELETE)) 1969 Expect(kvErrors[0].Error.Error()).To(BeEquivalentTo("failed to delete value")) 1970 1971 // check transaction operations 1972 txnHistory := scheduler.GetTransactionHistory(time.Time{}, time.Now()) 1973 Expect(txnHistory).To(HaveLen(2)) 1974 txn := txnHistory[1] 1975 Expect(txn.PreRecord).To(BeFalse()) 1976 Expect(txn.Start.After(startTime)).To(BeTrue()) 1977 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 1978 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 1979 Expect(txn.SeqNum).To(BeEquivalentTo(1)) 1980 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 1981 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 1982 Expect(txn.Description).To(BeEmpty()) 1983 checkRecordedValues(txn.Values, []RecordedKVPair{ 1984 {Key: prefixA + baseValue1, Value: nil, Origin: FromNB}, 1985 }) 1986 1987 // -> planned 1988 txnOps := RecordedTxnOps{ 1989 { 1990 Operation: TxnOperation_DELETE, 1991 Key: prefixA + baseValue1 + "/item1", 1992 IsDerived: true, 1993 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 1994 PrevState: ValueState_CONFIGURED, 1995 NewState: ValueState_REMOVED, 1996 }, 1997 { 1998 Operation: TxnOperation_DELETE, 1999 Key: prefixA + baseValue1, 2000 PrevValue: utils.RecordProtoMessage(test.NewArrayValue("item1")), 2001 PrevState: ValueState_CONFIGURED, 2002 NewState: ValueState_REMOVED, 2003 }, 2004 } 2005 checkTxnOperations(txn.Planned, txnOps) 2006 2007 // -> executed 2008 txnOps = RecordedTxnOps{ 2009 { 2010 Operation: TxnOperation_DELETE, 2011 Key: prefixA + baseValue1 + "/item1", 2012 IsDerived: true, 2013 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1")), 2014 PrevState: ValueState_CONFIGURED, 2015 NewState: ValueState_FAILED, 2016 NewErr: errors.New("failed to delete value"), 2017 }, 2018 } 2019 checkTxnOperations(txn.Executed, txnOps) 2020 2021 // check value status 2022 status := scheduler.GetValueStatus(prefixA + baseValue1) 2023 Expect(status).ToNot(BeNil()) 2024 checkBaseValueStatus(status, &BaseValueStatus{ 2025 Value: &ValueStatus{ 2026 Key: prefixA + baseValue1, 2027 State: ValueState_CONFIGURED, 2028 LastOperation: TxnOperation_DELETE, 2029 }, 2030 DerivedValues: []*ValueStatus{ 2031 { 2032 Key: prefixA + baseValue1 + "/item1", 2033 State: ValueState_FAILED, 2034 LastOperation: TxnOperation_DELETE, 2035 Error: "failed to delete value", 2036 }, 2037 }, 2038 }) 2039 2040 // close scheduler 2041 err = scheduler.Close() 2042 Expect(err).To(BeNil()) 2043 } 2044 2045 func TestFailedRecreateOfDerivedValue(t *testing.T) { 2046 RegisterTestingT(t) 2047 2048 // prepare KV Scheduler 2049 scheduler := NewPlugin(UseDeps(func(deps *Deps) { 2050 deps.HTTPHandlers = nil 2051 })) 2052 err := scheduler.Init() 2053 Expect(err).To(BeNil()) 2054 2055 // prepare mocks 2056 mockSB := test.NewMockSouthbound() 2057 // descriptor: 2058 descriptor := test.NewMockDescriptor(&KVDescriptor{ 2059 Name: descriptor1Name, 2060 NBKeyPrefix: prefixA, 2061 KeySelector: prefixSelector(prefixA), 2062 ValueTypeName: string(proto.MessageName(test.NewArrayValue())), 2063 DerivedValues: test.ArrayValueDerBuilder, 2064 WithMetadata: true, 2065 UpdateWithRecreate: func(key string, oldValue, newValue proto.Message, metadata Metadata) bool { 2066 return key == prefixA+baseValue1+"/item1" 2067 }, 2068 }, mockSB, 0) 2069 scheduler.RegisterKVDescriptor(descriptor) 2070 2071 // run non-resync transaction against empty SB 2072 arrayVal1 := test.NewArrayValueWithSuffix("-v1", "item1") 2073 schedulerTxn := scheduler.StartNBTransaction() 2074 schedulerTxn.SetValue(prefixA+baseValue1, arrayVal1) 2075 seqNum, err := schedulerTxn.Commit(testCtx) 2076 Expect(seqNum).To(BeEquivalentTo(0)) 2077 Expect(err).ShouldNot(HaveOccurred()) 2078 2079 // check the state of SB 2080 Expect(mockSB.GetKeysWithInvalidData()).To(BeEmpty()) 2081 // -> base value 1 2082 value := mockSB.GetValue(prefixA + baseValue1) 2083 Expect(value).ToNot(BeNil()) 2084 Expect(proto.Equal(value.Value, arrayVal1)).To(BeTrue()) 2085 Expect(value.Metadata).ToNot(BeNil()) 2086 Expect(value.Metadata.(test.MetaWithInteger).GetInteger()).To(BeEquivalentTo(0)) 2087 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 2088 // -> item1 derived from base value 1 2089 value = mockSB.GetValue(prefixA + baseValue1 + "/item1") 2090 Expect(value).ToNot(BeNil()) 2091 Expect(proto.Equal(value.Value, test.NewStringValue("item1-v1"))).To(BeTrue()) 2092 Expect(value.Metadata).To(BeNil()) 2093 Expect(value.Origin).To(BeEquivalentTo(FromNB)) 2094 2095 // plan error before 2nd txn 2096 failedCreateClb := func() { 2097 mockSB.SetValue(prefixA+baseValue1, test.NewArrayValue(), 2098 &test.OnlyInteger{Integer: 0}, FromNB, false) 2099 } 2100 mockSB.PlanError(prefixA+baseValue1+"/item1", nil, nil) // Delete 2101 mockSB.PlanError(prefixA+baseValue1+"/item1", errors.New("failed to create value"), failedCreateClb) // (Re)Create 2102 2103 // run 2nd non-resync transaction that will have errors 2104 startTime := time.Now() 2105 schedulerTxn2 := scheduler.StartNBTransaction() 2106 arrayVal2 := test.NewArrayValueWithSuffix("-v2", "item1") 2107 schedulerTxn2.SetValue(prefixA+baseValue1, arrayVal2) 2108 seqNum, err = schedulerTxn2.Commit(testCtx) 2109 stopTime := time.Now() 2110 Expect(seqNum).To(BeEquivalentTo(1)) 2111 Expect(err).ToNot(BeNil()) 2112 txnErr := err.(*TransactionError) 2113 Expect(txnErr.GetTxnInitError()).ShouldNot(HaveOccurred()) 2114 kvErrors := txnErr.GetKVErrors() 2115 Expect(kvErrors).To(HaveLen(1)) 2116 Expect(kvErrors[0].Key).To(BeEquivalentTo(prefixA + baseValue1 + "/item1")) 2117 Expect(kvErrors[0].TxnOperation).To(BeEquivalentTo(TxnOperation_CREATE)) 2118 Expect(kvErrors[0].Error.Error()).To(BeEquivalentTo("failed to create value")) 2119 2120 // check transaction operations 2121 txnHistory := scheduler.GetTransactionHistory(time.Time{}, time.Now()) 2122 Expect(txnHistory).To(HaveLen(2)) 2123 txn := txnHistory[1] 2124 Expect(txn.PreRecord).To(BeFalse()) 2125 Expect(txn.Start.After(startTime)).To(BeTrue()) 2126 Expect(txn.Start.Before(txn.Stop)).To(BeTrue()) 2127 Expect(txn.Stop.Before(stopTime)).To(BeTrue()) 2128 Expect(txn.SeqNum).To(BeEquivalentTo(1)) 2129 Expect(txn.TxnType).To(BeEquivalentTo(NBTransaction)) 2130 Expect(txn.ResyncType).To(BeEquivalentTo(NotResync)) 2131 Expect(txn.Description).To(BeEmpty()) 2132 checkRecordedValues(txn.Values, []RecordedKVPair{ 2133 {Key: prefixA + baseValue1, Value: utils.RecordProtoMessage(arrayVal2), Origin: FromNB}, 2134 }) 2135 2136 // -> planned 2137 txnOps := RecordedTxnOps{ 2138 { 2139 Operation: TxnOperation_UPDATE, 2140 Key: prefixA + baseValue1, 2141 PrevValue: utils.RecordProtoMessage(arrayVal1), 2142 NewValue: utils.RecordProtoMessage(arrayVal2), 2143 PrevState: ValueState_CONFIGURED, 2144 NewState: ValueState_CONFIGURED, 2145 }, 2146 { 2147 Operation: TxnOperation_DELETE, 2148 Key: prefixA + baseValue1 + "/item1", 2149 IsDerived: true, 2150 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1-v1")), 2151 PrevState: ValueState_CONFIGURED, 2152 NewState: ValueState_REMOVED, 2153 IsRecreate: true, 2154 }, 2155 { 2156 Operation: TxnOperation_CREATE, 2157 Key: prefixA + baseValue1 + "/item1", 2158 IsDerived: true, 2159 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1-v2")), 2160 PrevState: ValueState_REMOVED, 2161 NewState: ValueState_CONFIGURED, 2162 IsRecreate: true, 2163 }, 2164 } 2165 checkTxnOperations(txn.Planned, txnOps) 2166 2167 // -> executed 2168 txnOps = RecordedTxnOps{ 2169 { 2170 Operation: TxnOperation_UPDATE, 2171 Key: prefixA + baseValue1, 2172 PrevValue: utils.RecordProtoMessage(arrayVal1), 2173 NewValue: utils.RecordProtoMessage(arrayVal2), 2174 PrevState: ValueState_CONFIGURED, 2175 NewState: ValueState_CONFIGURED, 2176 }, 2177 { 2178 Operation: TxnOperation_DELETE, 2179 Key: prefixA + baseValue1 + "/item1", 2180 IsDerived: true, 2181 PrevValue: utils.RecordProtoMessage(test.NewStringValue("item1-v1")), 2182 PrevState: ValueState_CONFIGURED, 2183 NewState: ValueState_REMOVED, 2184 IsRecreate: true, 2185 }, 2186 { 2187 Operation: TxnOperation_CREATE, 2188 Key: prefixA + baseValue1 + "/item1", 2189 IsDerived: true, 2190 NewValue: utils.RecordProtoMessage(test.NewStringValue("item1-v2")), 2191 PrevState: ValueState_REMOVED, 2192 NewState: ValueState_FAILED, 2193 NewErr: errors.New("failed to create value"), 2194 IsRecreate: true, 2195 }, 2196 } 2197 checkTxnOperations(txn.Executed, txnOps) 2198 2199 // close scheduler 2200 err = scheduler.Close() 2201 Expect(err).To(BeNil()) 2202 }