github.com/ergo-services/ergo@v1.999.224/tests/stage_test.go (about) 1 package tests 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/ergo-services/ergo" 9 "github.com/ergo-services/ergo/etf" 10 "github.com/ergo-services/ergo/gen" 11 "github.com/ergo-services/ergo/lib" 12 "github.com/ergo-services/ergo/node" 13 ) 14 15 const ( 16 defaultDispatcherBufferSize = 10000 17 defaultAutoDemandCount = 3 18 ) 19 20 type StageProducerTest struct { 21 gen.Stage 22 value chan interface{} 23 dispatcher gen.StageDispatcherBehavior 24 } 25 26 type sendEvents struct { 27 events etf.List 28 } 29 type cancelSubscription struct { 30 subscription gen.StageSubscription 31 reason string 32 } 33 34 type demandRequest struct { 35 subscription gen.StageSubscription 36 count uint 37 } 38 type newSubscription struct { 39 producer etf.Term 40 opts gen.StageSubscribeOptions 41 } 42 type demandHandle struct { 43 enable bool 44 } 45 type autoDemand struct { 46 subscription gen.StageSubscription 47 enable bool 48 } 49 50 // 51 // a simple Stage Producer 52 // 53 54 func (gs *StageProducerTest) InitStage(process *gen.StageProcess, args ...etf.Term) (gen.StageOptions, error) { 55 opts := gen.StageOptions{ 56 Dispatcher: gs.dispatcher, 57 } 58 return opts, nil 59 } 60 func (gs *StageProducerTest) HandleDemand(process *gen.StageProcess, subscription gen.StageSubscription, count uint) (etf.List, gen.StageStatus) { 61 gs.value <- etf.Tuple{subscription, count} 62 return nil, gen.StageStatusOK 63 } 64 65 func (gs *StageProducerTest) HandleSubscribe(process *gen.StageProcess, subscription gen.StageSubscription, options gen.StageSubscribeOptions) gen.StageStatus { 66 gs.value <- subscription 67 return gen.StageStatusOK 68 } 69 func (gs *StageProducerTest) HandleCancel(process *gen.StageProcess, subscription gen.StageSubscription, reason string) gen.StageStatus { 70 gs.value <- etf.Tuple{"cancel", subscription} 71 return gen.StageStatusOK 72 } 73 74 // add this callback only for the 'not a producer' case 75 func (gs *StageProducerTest) HandleCanceled(process *gen.StageProcess, subscription gen.StageSubscription, reason string) gen.StageStatus { 76 gs.value <- etf.Tuple{"canceled", subscription, reason} 77 return gen.StageStatusOK 78 } 79 80 func (s *StageProducerTest) SendEvents(p gen.Process, events etf.List) error { 81 message := sendEvents{ 82 events: events, 83 } 84 _, err := p.Direct(message) 85 return err 86 } 87 88 func (s *StageProducerTest) Cancel(p gen.Process, subscription gen.StageSubscription, reason string) error { 89 message := cancelSubscription{ 90 subscription: subscription, 91 reason: reason, 92 } 93 _, err := p.Direct(message) 94 return err 95 } 96 func (gs *StageProducerTest) Subscribe(p gen.Process, producer etf.Term, opts gen.StageSubscribeOptions) (gen.StageSubscription, error) { 97 message := newSubscription{ 98 producer: producer, 99 opts: opts, 100 } 101 s, err := p.Direct(message) 102 if err != nil { 103 return gen.StageSubscription{}, err 104 } 105 return s.(gen.StageSubscription), nil 106 } 107 108 func (gs *StageProducerTest) SetDemandHandle(p gen.Process, enable bool) error { 109 message := demandHandle{ 110 enable: enable, 111 } 112 _, err := p.Direct(message) 113 if err != nil { 114 return err 115 } 116 return nil 117 } 118 func (gs *StageProducerTest) SetAutoDemand(p gen.Process, subscription gen.StageSubscription, enable bool) error { 119 message := autoDemand{ 120 subscription: subscription, 121 enable: enable, 122 } 123 _, err := p.Direct(message) 124 if err != nil { 125 return err 126 } 127 return nil 128 } 129 130 func (s *StageProducerTest) HandleStageDirect(process *gen.StageProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) { 131 switch m := message.(type) { 132 case demandHandle: 133 process.SetDemandHandle(m.enable) 134 return nil, nil 135 case newSubscription: 136 return process.Subscribe(m.producer, m.opts) 137 case sendEvents: 138 process.SendEvents(m.events) 139 return nil, nil 140 141 case cancelSubscription: 142 err := process.Cancel(m.subscription, m.reason) 143 return nil, err 144 case makeCall: 145 return process.Call(m.to, m.message) 146 case makeCast: 147 return nil, process.Cast(m.to, m.message) 148 149 default: 150 return nil, lib.ErrUnsupportedRequest 151 } 152 } 153 154 // 155 // a simple Stage Consumer 156 // 157 type StageConsumerTest struct { 158 gen.Stage 159 value chan interface{} 160 } 161 162 func (gs *StageConsumerTest) InitStage(process *gen.StageProcess, args ...etf.Term) (gen.StageOptions, error) { 163 return gen.StageOptions{}, nil 164 } 165 func (gs *StageConsumerTest) HandleEvents(process *gen.StageProcess, subscription gen.StageSubscription, events etf.List) gen.StageStatus { 166 gs.value <- etf.Tuple{"events", subscription, events} 167 return gen.StageStatusOK 168 } 169 func (gs *StageConsumerTest) HandleSubscribed(process *gen.StageProcess, subscription gen.StageSubscription, opts gen.StageSubscribeOptions) (bool, gen.StageStatus) { 170 gs.value <- subscription 171 return opts.ManualDemand, gen.StageStatusOK 172 } 173 func (gs *StageConsumerTest) HandleCanceled(process *gen.StageProcess, subscription gen.StageSubscription, reason string) gen.StageStatus { 174 gs.value <- etf.Tuple{"canceled", subscription, reason} 175 return gen.StageStatusOK 176 } 177 func (gs *StageConsumerTest) HandleStageCall(process *gen.StageProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) { 178 gs.value <- message 179 return "ok", gen.ServerStatusOK 180 } 181 func (gs *StageConsumerTest) HandleStageCast(process *gen.StageProcess, message etf.Term) gen.ServerStatus { 182 gs.value <- message 183 return gen.ServerStatusOK 184 } 185 func (gs *StageConsumerTest) HandleStageInfo(process *gen.StageProcess, message etf.Term) gen.ServerStatus { 186 gs.value <- message 187 return gen.ServerStatusOK 188 } 189 190 func (s *StageConsumerTest) HandleStageDirect(p *gen.StageProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) { 191 switch m := message.(type) { 192 case newSubscription: 193 return p.Subscribe(m.producer, m.opts) 194 case demandRequest: 195 err := p.Ask(m.subscription, m.count) 196 return nil, err 197 case autoDemand: 198 err := p.SetAutoDemand(m.subscription, m.enable) 199 return nil, err 200 201 case cancelSubscription: 202 err := p.Cancel(m.subscription, m.reason) 203 return nil, err 204 case makeCall: 205 return p.Call(m.to, m.message) 206 case makeCast: 207 return nil, p.Cast(m.to, m.message) 208 } 209 return nil, lib.ErrUnsupportedRequest 210 } 211 212 func (s *StageConsumerTest) HandleStageTerminate(p *gen.StageProcess, reason string) { 213 //fmt.Println("StageConsumerTest process terminated with reason", reason) 214 } 215 216 func (gs *StageConsumerTest) SetAutoDemand(p gen.Process, subscription gen.StageSubscription, enable bool) error { 217 message := autoDemand{ 218 subscription: subscription, 219 enable: enable, 220 } 221 _, err := p.Direct(message) 222 if err != nil { 223 return err 224 } 225 return nil 226 } 227 228 func (gs *StageConsumerTest) Subscribe(p gen.Process, producer etf.Term, opts gen.StageSubscribeOptions) (gen.StageSubscription, error) { 229 message := newSubscription{ 230 producer: producer, 231 opts: opts, 232 } 233 s, err := p.Direct(message) 234 if err != nil { 235 return gen.StageSubscription{}, err 236 } 237 return s.(gen.StageSubscription), nil 238 } 239 240 func (s *StageConsumerTest) Cancel(p gen.Process, subscription gen.StageSubscription, reason string) error { 241 message := cancelSubscription{ 242 subscription: subscription, 243 reason: reason, 244 } 245 _, err := p.Direct(message) 246 return err 247 } 248 249 func (s *StageConsumerTest) Ask(p gen.Process, subscription gen.StageSubscription, count uint) error { 250 message := demandRequest{ 251 subscription: subscription, 252 count: count, 253 } 254 _, err := p.Direct(message) 255 return err 256 } 257 258 func TestStageSimple(t *testing.T) { 259 260 fmt.Printf("\n=== Test StageSimple\n") 261 fmt.Printf("Starting node: nodeStageSimple01@localhost...") 262 263 node, _ := ergo.StartNode("nodeStageSimple01@localhost", "cookies", node.Options{}) 264 265 if node == nil { 266 t.Fatal("can't start node") 267 return 268 } 269 fmt.Println("OK") 270 271 producer := &StageProducerTest{ 272 value: make(chan interface{}, 2), 273 } 274 consumer := &StageConsumerTest{ 275 value: make(chan interface{}, 2), 276 } 277 // producerProcess, _ := 278 fmt.Printf("... starting Producer and Consumer processes: ") 279 producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 280 if errP != nil { 281 t.Fatal(errP) 282 } 283 consumerProcess, errC := node.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil) 284 if errC != nil { 285 t.Fatal(errC) 286 } 287 fmt.Println("OK") 288 289 subOpts := gen.StageSubscribeOptions{ 290 MinDemand: 4, 291 MaxDemand: 5, 292 ManualDemand: true, 293 // use Temporary to keep this process running 294 Cancel: gen.StageCancelTemporary, 295 } 296 297 // case 1: subscribe 298 fmt.Println("Subscribing/resubscribing/cancelation:") 299 sub, _ := consumer.Subscribe(consumerProcess, "stageProducer", subOpts) 300 fmt.Printf("... Producer handled subscription request from Consumer: ") 301 waitForResultWithValue(t, producer.value, sub) 302 fmt.Printf("... Consumer handled subscription confirmation from Producer: ") 303 waitForResultWithValue(t, consumer.value, sub) 304 // case 2: subscribe one more time (prev subscription should be canceled automatically) 305 fmt.Printf("... Consumer subscribes to Producer without cancelation previous subscription: ") 306 sub1, _ := consumer.Subscribe(consumerProcess, "stageProducer", subOpts) 307 fmt.Println("OK") 308 // previous subscription should be canceled 309 fmt.Printf("... Producer canceled previous subscription and handled the new subscription request from Consumer: ") 310 waitForResultWithValue(t, producer.value, sub1) 311 fmt.Printf("... Consumer handled cancelation and subscription confirmation from Producer: ") 312 waitForResultWithMultiValue(t, consumer.value, etf.List{sub1, etf.Tuple{"canceled", sub, "resubscribed"}}) 313 314 fmt.Printf("... Consumer invokes Cancel subscription explicitly:") 315 if err := consumer.Cancel(consumerProcess, sub1, "normal"); err != nil { 316 t.Fatal(err) 317 } 318 fmt.Println("OK") 319 fmt.Printf("... Producer handled cancelation request from Consumer: ") 320 waitForResultWithValue(t, producer.value, etf.Tuple{"cancel", sub1}) 321 fmt.Printf("... Consumer handled cancelation confirmation from Producer: ") 322 waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub1, "normal"}) 323 324 fmt.Println("make another subscription for the testing of explicit cancelation from the Producer side") 325 sub2, _ := consumer.Subscribe(consumerProcess, "stageProducer", subOpts) 326 fmt.Printf("... Producer handled subscription request from Consumer: ") 327 waitForResultWithValue(t, producer.value, sub2) 328 fmt.Printf("... Consumer handled subscription confirmation from Producer: ") 329 waitForResultWithValue(t, consumer.value, sub2) 330 if err := producer.Cancel(producerProcess, sub2, "normal"); err != nil { 331 t.Fatal(err) 332 } 333 fmt.Printf("... Producer handled cancelation request from itself: ") 334 waitForResultWithValue(t, producer.value, etf.Tuple{"cancel", sub2}) 335 fmt.Printf("... Consumer handled cancelation confirmation from Producer: ") 336 waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub2, "normal"}) 337 waitForTimeout(t, producer.value) 338 waitForTimeout(t, consumer.value) 339 340 // case 3: 341 fmt.Printf("... trying to subscribe on Consumer (should fail with error 'not a producer'): ") 342 // let's subscribe using Pid instead of registered name "stageConsumer" 343 sub3, _ := producer.Subscribe(producerProcess, consumerProcess.Self(), subOpts) 344 waitForResultWithValue(t, producer.value, etf.Tuple{"canceled", sub3, "not a producer"}) 345 346 // case 4: invoking Server callbacks 347 fmt.Printf("... Invoking Server's callback handlers HandleStageCall: ") 348 call := makeCall{ 349 to: "stageConsumer", 350 message: "test call", 351 } 352 if _, err := producerProcess.Direct(call); err != nil { 353 t.Fatal(err) 354 } 355 waitForResultWithValue(t, consumer.value, "test call") 356 fmt.Printf("... Invoking Server's callback handlers HandleStageCast: ") 357 cast := makeCast{ 358 to: "stageConsumer", 359 message: "test cast", 360 } 361 producerProcess.Direct(cast) 362 waitForResultWithValue(t, consumer.value, "test cast") 363 fmt.Printf("... Invoking Server's callback handlers HandleStageInfo: ") 364 producerProcess.Send("stageConsumer", "test info") 365 waitForResultWithValue(t, consumer.value, "test info") 366 367 // case 5: 368 // - subscribe, ask and send events 369 // - resubscribe with updated min/max, ask and send events 370 // keep this subscription for the next case 371 subOpts.MinDemand = 2 372 subOpts.MaxDemand = 4 373 subOpts.ManualDemand = true 374 fmt.Println("make yet another subscription for the testing subscribe/ask/send_events") 375 sub4, _ := consumer.Subscribe(consumerProcess, producerProcess.Self(), subOpts) 376 fmt.Printf("... Producer handled subscription request from Consumer: ") 377 waitForResultWithValue(t, producer.value, sub4) 378 fmt.Printf("... Consumer handled subscription confirmation from Producer: ") 379 waitForResultWithValue(t, consumer.value, sub4) 380 fmt.Printf("... Consumer sent 'Ask' request with count = 1 (min 2, max 4) : ") 381 consumer.Ask(consumerProcess, sub4, 1) 382 waitForResultWithValue(t, producer.value, etf.Tuple{sub4, uint(1)}) 383 waitForTimeout(t, consumer.value) // shouldn't receive anything here 384 events := etf.List{ 385 "a", "b", "c", "d", "e", 386 } 387 fmt.Printf("... Consumer sent 'Ask' request with count = 4 (min 2, max 4) : ") 388 consumer.Ask(consumerProcess, sub4, 4) 389 waitForResultWithValue(t, producer.value, etf.Tuple{sub4, uint(4)}) 390 fmt.Printf("... Producer sent 5 events. Consumer should receive 4: ") 391 producer.SendEvents(producerProcess, events) 392 expected := etf.Tuple{"events", sub4, events[0:4]} 393 waitForResultWithValue(t, consumer.value, expected) 394 events = etf.List{ 395 "f", 396 } 397 fmt.Printf("... Producer sent 1 event. Consumer shouldn't receive anything until make yet another 'Ask' request: ") 398 producer.SendEvents(producerProcess, events) 399 waitForTimeout(t, consumer.value) // shouldn't receive anything here 400 fmt.Println("OK") 401 fmt.Printf("1... Consumer sent 'Ask' request with count = 1 (min 2, max 4) : ") 402 if err := consumer.Ask(consumerProcess, sub4, 1); err != nil { 403 t.Fatal(err) 404 } 405 waitForResultWithValue(t, producer.value, etf.Tuple{sub4, uint(1)}) 406 fmt.Printf("... Consumer should receive 2 events: ") 407 expected = etf.Tuple{"events", sub4, etf.List{"e", "f"}} 408 waitForResultWithValue(t, consumer.value, expected) 409 410 // case 6: 411 // - ask, disable forwarding, send events, ask, enable forwarding 412 // keep this subscription for the next case 413 414 fmt.Printf("... Disable handling demand on Producer: ") 415 if err := producer.SetDemandHandle(producerProcess, false); err != nil { 416 t.Fatal(err) 417 } 418 fmt.Println("OK") 419 fmt.Printf("... Consumer sent 'Ask' request with count = 1 (min 2, max 4). Stage shouldn't call HandleDemand : ") 420 consumer.Ask(consumerProcess, sub4, 1) 421 waitForTimeout(t, producer.value) // shouldn't receive anything here 422 fmt.Println("OK") 423 424 events = etf.List{ 425 "a", "b", "c", "d", "e", "f", "g", "h", "1", "2", 426 } 427 producer.SendEvents(producerProcess, events) 428 fmt.Printf("... Producer sent 10 events. Dispatcher should keep them all since demand was buffered: ") 429 waitForTimeout(t, consumer.value) // shouldn't receive anything here 430 fmt.Println("OK") 431 fmt.Printf("... Consumer sent yet another 'Ask'. Stage shouldn't call HandleDemand as well: ") 432 consumer.Ask(consumerProcess, sub4, 9) 433 waitForTimeout(t, producer.value) // shouldn't receive anything here 434 fmt.Println("OK") 435 436 fmt.Printf("... Enable handling demand on Producer: ") 437 if err := producer.SetDemandHandle(producerProcess, true); err != nil { 438 t.Fatal(err) 439 } 440 fmt.Println("OK") 441 fmt.Printf("... Producer should receive demands count =1 and count =9: ") 442 expected1 := etf.Tuple{sub4, uint(1)} 443 expected2 := etf.Tuple{sub4, uint(9)} 444 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 445 fmt.Printf("... Customer should receive 3 messages with 4, 4 and 2 events: ") 446 expected1 = etf.Tuple{"events", sub4, etf.List{"a", "b", "c", "d"}} 447 expected2 = etf.Tuple{"events", sub4, etf.List{"e", "f", "g", "h"}} 448 expected3 := etf.Tuple{"events", sub4, etf.List{"1", "2"}} 449 waitForResultWithMultiValue(t, consumer.value, etf.List{expected1, expected2, expected3}) 450 451 waitForTimeout(t, consumer.value) // shouldn't receive anything here 452 waitForTimeout(t, producer.value) // shouldn't receive anything here 453 454 // case 7: 455 // - enable auto demand, send events, try to ask (should get error), disable auto demands 456 fmt.Printf("... Enable AutoDemand on the Producer (should fail): ") 457 if err := producer.SetAutoDemand(producerProcess, sub4, true); err == nil { 458 t.Fatal("should fail here") 459 } 460 fmt.Println("OK") 461 fmt.Printf("... Enable AutoDemand on the Consumer. Producer should receive demand with count %d: ", defaultAutoDemandCount) 462 if err := consumer.SetAutoDemand(consumerProcess, sub4, true); err != nil { 463 t.Fatal(err) 464 } 465 waitForResultWithValue(t, producer.value, etf.Tuple{sub4, subOpts.MaxDemand}) 466 fmt.Printf("... Customer sent 'Ask' request (should fail): ") 467 if err := consumer.Ask(consumerProcess, sub4, 2); err == nil { 468 t.Fatal("should fail here") 469 } 470 fmt.Println("OK") 471 events = etf.List{ 472 "a", "b", "c", "d", "e", 473 } 474 producer.SendEvents(producerProcess, events) 475 fmt.Printf("... Producer sent 5 events. Consumer should receive 4. Demands counter = 2 now: ") 476 expected = etf.Tuple{"events", sub4, etf.List{"a", "b", "c", "d"}} 477 waitForResultWithValue(t, consumer.value, expected) 478 fmt.Printf("... Consumer should send auto demand with count %d: ", subOpts.MaxDemand) 479 waitForResultWithValue(t, producer.value, etf.Tuple{sub4, subOpts.MaxDemand}) 480 481 node.Stop() 482 } 483 484 func TestStageDistributed(t *testing.T) { 485 fmt.Printf("\n=== Test StageDistributed\n") 486 fmt.Printf("Starting node: nodeStageDistributed01@localhost...") 487 node1, _ := ergo.StartNode("nodeStageDistributed01@localhost", "cookies", node.Options{}) 488 if node1 == nil { 489 t.Fatal("can't start node") 490 return 491 } 492 fmt.Println("OK") 493 fmt.Printf("Starting node: nodeStageDistributed02@localhost...") 494 node2, _ := ergo.StartNode("nodeStageDistributed02@localhost", "cookies", node.Options{}) 495 if node2 == nil { 496 t.Fatal("can't start node") 497 return 498 } 499 fmt.Println("OK") 500 501 producer := &StageProducerTest{ 502 value: make(chan interface{}, 2), 503 } 504 consumer := &StageConsumerTest{ 505 value: make(chan interface{}, 2), 506 } 507 fmt.Printf("... starting Producer and Consumer processes: ") 508 producerProcess, errP := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 509 if errP != nil { 510 t.Fatal(errP) 511 } 512 consumerProcess, errC := node2.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil) 513 if errC != nil { 514 t.Fatal(errC) 515 } 516 fmt.Println("OK") 517 subOpts := gen.StageSubscribeOptions{ 518 MinDemand: 2, 519 MaxDemand: 4, 520 ManualDemand: true, 521 // use Temporary to keep this process running 522 Cancel: gen.StageCancelTemporary, 523 } 524 fmt.Println("Consumer@node2 subscribes on Producer@node1: ") 525 sub, _ := consumer.Subscribe(consumerProcess, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts) 526 fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ") 527 waitForResultWithValue(t, producer.value, sub) 528 fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1: ") 529 waitForResultWithValue(t, consumer.value, sub) 530 531 fmt.Printf("... Consumer@node2 sent 'Ask' request with count = 4 (min 2, max 4) : ") 532 if err := consumer.Ask(consumerProcess, sub, 4); err != nil { 533 t.Fatal(err) 534 } 535 waitForResultWithValue(t, producer.value, etf.Tuple{sub, uint(4)}) 536 waitForTimeout(t, consumer.value) // shouldn't receive anything here 537 events := etf.List{ 538 "a", "b", "c", "d", "e", 539 } 540 fmt.Printf("... Producer@node1 sent 5 events. Consumer@node2 should receive 4: ") 541 producer.SendEvents(producerProcess, events) 542 expected := etf.Tuple{"events", sub, events[0:4]} 543 waitForResultWithValue(t, consumer.value, expected) 544 545 producerProcess.Kill() 546 fmt.Printf("... Producer process killed. Consumer should receive 'canceled' with reason 'kill': ") 547 waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub, "kill"}) 548 if !consumerProcess.IsAlive() { 549 t.Fatal("Consumer process should be alive here") 550 } 551 552 // case 2: StageCancelTransient 553 554 fmt.Printf("... Starting Producer process (test StageCancelTransient) : ") 555 producerProcess1, errP1 := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 556 if errP1 != nil { 557 t.Fatal(errP1) 558 } 559 fmt.Println("OK") 560 561 subOpts.Cancel = gen.StageCancelTransient 562 sub1, _ := consumer.Subscribe(consumerProcess, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts) 563 fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ") 564 waitForResultWithValue(t, producer.value, sub1) 565 fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1 (StageCancelTransient): ") 566 waitForResultWithValue(t, consumer.value, sub1) 567 568 producerProcess1.Kill() 569 if err := producerProcess1.WaitWithTimeout(500 * time.Millisecond); err != nil { 570 t.Fatal(err) 571 } 572 fmt.Printf("... Producer process killed. Consumer should receive 'canceled' with reason 'kill': ") 573 waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub1, "kill"}) 574 fmt.Printf("... Consumer process should be terminated due to reason 'kill': ") 575 if err := consumerProcess.WaitWithTimeout(500 * time.Millisecond); err != nil { 576 t.Fatal(err) 577 } 578 fmt.Println("OK") 579 580 // case 3: StageCancelPermanent 581 582 fmt.Printf("... Starting Producer process (test StageCancelPermanent) : ") 583 producerProcess2, errP2 := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 584 if errP2 != nil { 585 t.Fatal(errP2) 586 } 587 consumerProcess1, errC := node2.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil) 588 if errC != nil { 589 t.Fatal(errC) 590 } 591 fmt.Println("OK") 592 593 subOpts.Cancel = gen.StageCancelPermanent 594 sub2, _ := consumer.Subscribe(consumerProcess1, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts) 595 fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ") 596 waitForResultWithValue(t, producer.value, sub2) 597 fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1 (StageCancelPermanent): ") 598 waitForResultWithValue(t, consumer.value, sub2) 599 600 producerProcess2.Exit("normal") 601 if err := producerProcess2.WaitWithTimeout(500 * time.Millisecond); err != nil { 602 t.Fatal(err) 603 } 604 fmt.Printf("... Producer process terminated normally. Consumer should receive 'canceled' with reason 'normal': ") 605 waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub2, "normal"}) 606 fmt.Printf("... Consumer process should be terminated due to StageCancelPermanent mode: ") 607 if err := consumerProcess1.WaitWithTimeout(500 * time.Millisecond); err != nil { 608 t.Fatal(err) 609 } 610 fmt.Println("OK") 611 612 // case 4: Cancel on producer's node termination 613 fmt.Printf("... Starting Producer process (test cancel on node termination) : ") 614 _, errP3 := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 615 if errP3 != nil { 616 t.Fatal(errP3) 617 } 618 consumerProcess2, errC := node2.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil) 619 if errC != nil { 620 t.Fatal(errC) 621 } 622 fmt.Println("OK") 623 subOpts.Cancel = gen.StageCancelTemporary 624 sub3, err := consumer.Subscribe(consumerProcess2, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts) 625 if err != nil { 626 t.Fatal(err) 627 } 628 fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ") 629 waitForResultWithValue(t, producer.value, sub3) 630 fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1 (StageCancelTemporary): ") 631 waitForResultWithValue(t, consumer.value, sub3) 632 633 node2.Disconnect(node1.Name()) 634 node1.Stop() 635 fmt.Printf("... Stopping node1: ") 636 if err := node1.WaitWithTimeout(1000 * time.Millisecond); err != nil { 637 t.Fatal(err) 638 } 639 fmt.Println("OK") 640 waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub3, "noconnection"}) 641 642 node2.Stop() 643 } 644 645 func TestStageDispatcherDemand(t *testing.T) { 646 fmt.Printf("\n=== Test StageDispatcherDemand\n") 647 fmt.Printf("Starting node: StageDispatcherDemand@localhost...") 648 node, _ := ergo.StartNode("StageDispatcherDemand@localhost", "cookies", node.Options{}) 649 if node == nil { 650 t.Fatal("can't start node") 651 return 652 } 653 fmt.Println("OK") 654 655 subOpts := gen.StageSubscribeOptions{ 656 MinDemand: 4, 657 MaxDemand: 4, 658 } 659 660 producer := &StageProducerTest{ 661 value: make(chan interface{}, 2), 662 // StageDispatcherDemand - this is default dispatcher, so we shouldn't create it explicitly 663 // dispatcher: CreateStageDispatcherDemand(), 664 } 665 consumer := &StageConsumerTest{ 666 value: make(chan interface{}, 2), 667 } 668 fmt.Printf("... starting Producer (StageDispatcherDemand): ") 669 producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 670 if errP != nil { 671 t.Fatal(errP) 672 } 673 fmt.Println("OK") 674 fmt.Printf("... starting Consumer1: ") 675 consumer1Process, errC1 := node.Spawn("stageConsumer1", gen.ProcessOptions{}, consumer, nil) 676 if errC1 != nil { 677 t.Fatal(errC1) 678 } 679 fmt.Println("OK") 680 sub1, _ := consumer.Subscribe(consumer1Process, "stageProducer", subOpts) 681 fmt.Printf("... Producer handled subscription request from Consumer1: ") 682 expected1 := etf.Tuple{sub1, uint(subOpts.MaxDemand)} 683 expected2 := sub1 684 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 685 686 fmt.Printf("... Consumer1 handled subscription confirmation from Producer: ") 687 waitForResultWithValue(t, consumer.value, sub1) 688 689 fmt.Printf("... starting Consumer2: ") 690 consumer2Process, errC2 := node.Spawn("stageConsumer2", gen.ProcessOptions{}, consumer, nil) 691 if errC2 != nil { 692 t.Fatal(errC2) 693 } 694 fmt.Println("OK") 695 sub2, _ := consumer.Subscribe(consumer2Process, "stageProducer", subOpts) 696 fmt.Printf("... Producer handled subscription request from Consumer2: ") 697 expected1 = etf.Tuple{sub2, uint(subOpts.MaxDemand)} 698 expected2 = sub2 699 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 700 fmt.Printf("... Consumer2 handled subscription confirmation from Producer: ") 701 waitForResultWithValue(t, consumer.value, sub2) 702 703 fmt.Printf("... starting Consumer3: ") 704 consumer3Process, errC3 := node.Spawn("stageConsumer3", gen.ProcessOptions{}, consumer, nil) 705 if errC3 != nil { 706 t.Fatal(errC3) 707 } 708 fmt.Println("OK") 709 sub3, _ := consumer.Subscribe(consumer3Process, "stageProducer", subOpts) 710 fmt.Printf("... Producer handled subscription request from Consumer3: ") 711 expected1 = etf.Tuple{sub3, uint(subOpts.MaxDemand)} 712 expected2 = sub3 713 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 714 fmt.Printf("... Consumer3 handled subscription confirmation from Producer: ") 715 waitForResultWithValue(t, consumer.value, sub3) 716 717 fmt.Printf("... Producer sent 5 events. Consumer1 should receive 4: ") 718 events := etf.List{ 719 "a", "b", "c", "d", "e", 720 } 721 producer.SendEvents(producerProcess, events) 722 expected := etf.Tuple{"events", sub1, events[0:4]} 723 waitForResultWithValue(t, consumer.value, expected) 724 725 fmt.Printf("... Producer sent 5 events. Consumer2 should receive 4: ") 726 events = etf.List{ 727 "f", "g", "h", "1", "2", 728 } 729 producer.SendEvents(producerProcess, events) 730 expected = etf.Tuple{"events", sub2, etf.List{"e", "f", "g", "h"}} 731 waitForResultWithValue(t, consumer.value, expected) 732 733 fmt.Printf("... Producer sent 2 events. Consumer3 should receive 4: ") 734 events = etf.List{ 735 "3", "4", 736 } 737 producer.SendEvents(producerProcess, events) 738 expected = etf.Tuple{"events", sub3, etf.List{"1", "2", "3", "4"}} 739 waitForResultWithValue(t, consumer.value, expected) 740 741 node.Stop() 742 } 743 744 func TestStageDispatcherBroadcast(t *testing.T) { 745 fmt.Printf("\n=== Test StageDispatcherBroadcast\n") 746 fmt.Printf("Starting node: StageDispatcherBroadcast@localhost...") 747 node, _ := ergo.StartNode("StageDispatcherBroadcast@localhost", "cookies", node.Options{}) 748 if node == nil { 749 t.Fatal("can't start node") 750 return 751 } 752 fmt.Println("OK") 753 754 subOpts := gen.StageSubscribeOptions{ 755 MinDemand: 4, 756 MaxDemand: 4, 757 } 758 759 producer := &StageProducerTest{ 760 value: make(chan interface{}, 2), 761 dispatcher: gen.CreateStageDispatcherBroadcast(), 762 } 763 consumer1 := &StageConsumerTest{ 764 value: make(chan interface{}, 2), 765 } 766 consumer2 := &StageConsumerTest{ 767 value: make(chan interface{}, 2), 768 } 769 consumer3 := &StageConsumerTest{ 770 value: make(chan interface{}, 2), 771 } 772 fmt.Printf("... starting Producer (StageDispatcherBroadcast): ") 773 producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 774 if errP != nil { 775 t.Fatal(errP) 776 } 777 fmt.Println("OK") 778 fmt.Printf("... starting Consumer1: ") 779 consumer1Process, errC1 := node.Spawn("stageConsumer1", gen.ProcessOptions{}, consumer1, nil) 780 if errC1 != nil { 781 t.Fatal(errC1) 782 } 783 fmt.Println("OK") 784 sub1, _ := consumer1.Subscribe(consumer1Process, "stageProducer", subOpts) 785 fmt.Printf("... Producer handled subscription request from Consumer1: ") 786 expected1 := etf.Tuple{sub1, uint(subOpts.MaxDemand)} 787 expected2 := sub1 788 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 789 790 fmt.Printf("... Consumer1 handled subscription confirmation from Producer: ") 791 waitForResultWithValue(t, consumer1.value, sub1) 792 793 fmt.Printf("... starting Consumer2: ") 794 consumer2Process, errC2 := node.Spawn("stageConsumer2", gen.ProcessOptions{}, consumer2, nil) 795 if errC2 != nil { 796 t.Fatal(errC2) 797 } 798 fmt.Println("OK") 799 sub2, _ := consumer2.Subscribe(consumer2Process, "stageProducer", subOpts) 800 fmt.Printf("... Producer handled subscription request from Consumer2: ") 801 expected1 = etf.Tuple{sub2, uint(subOpts.MaxDemand)} 802 expected2 = sub2 803 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 804 fmt.Printf("... Consumer2 handled subscription confirmation from Producer: ") 805 waitForResultWithValue(t, consumer2.value, sub2) 806 807 fmt.Printf("... starting Consumer3: ") 808 consumer3Process, errC3 := node.Spawn("stageConsumer3", gen.ProcessOptions{}, consumer3, nil) 809 if errC3 != nil { 810 t.Fatal(errC3) 811 } 812 fmt.Println("OK") 813 sub3, _ := consumer3.Subscribe(consumer3Process, "stageProducer", subOpts) 814 fmt.Printf("... Producer handled subscription request from Consumer3: ") 815 expected1 = etf.Tuple{sub3, uint(subOpts.MaxDemand)} 816 expected2 = sub3 817 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 818 fmt.Printf("... Consumer3 handled subscription confirmation from Producer: ") 819 waitForResultWithValue(t, consumer3.value, sub3) 820 821 fmt.Printf("... Producer sent 5 events: ") 822 events := etf.List{ 823 "a", "b", "c", "d", "e", 824 } 825 producer.SendEvents(producerProcess, events) 826 fmt.Println("OK") 827 exp1 := etf.Tuple{"events", sub1, events[0:4]} 828 fmt.Printf("... Consumer1 should receive 4: ") 829 waitForResultWithValue(t, consumer1.value, exp1) 830 exp2 := etf.Tuple{"events", sub2, events[0:4]} 831 fmt.Printf("... Consumer2 should receive 4: ") 832 waitForResultWithValue(t, consumer2.value, exp2) 833 exp3 := etf.Tuple{"events", sub3, events[0:4]} 834 fmt.Printf("... Consumer2 should receive 4: ") 835 waitForResultWithValue(t, consumer3.value, exp3) 836 837 node.Stop() 838 839 } 840 841 func TestStageDispatcherPartition(t *testing.T) { 842 fmt.Printf("\n=== Test StageDispatcherPartition\n") 843 fmt.Printf("Starting node: StageDispatcherPartition@localhost...") 844 node, _ := ergo.StartNode("StageDispatcherPartition@localhost", "cookies", node.Options{}) 845 if node == nil { 846 t.Fatal("can't start node") 847 return 848 } 849 fmt.Println("OK") 850 851 subOpts1 := gen.StageSubscribeOptions{ 852 MinDemand: 3, 853 MaxDemand: 4, 854 Partition: 0, 855 } 856 subOpts2 := gen.StageSubscribeOptions{ 857 MinDemand: 3, 858 MaxDemand: 4, 859 Partition: 1, 860 } 861 subOpts3 := gen.StageSubscribeOptions{ 862 MinDemand: 3, 863 MaxDemand: 4, 864 Partition: 2, 865 } 866 867 hash := func(t etf.Term) int { 868 i, ok := t.(int) 869 if !ok { 870 // filtering out 871 return -1 872 } 873 874 if i > 1000 { 875 return 2 876 } 877 if i > 100 { 878 return 1 879 } 880 return 0 881 } 882 883 producer := &StageProducerTest{ 884 value: make(chan interface{}, 2), 885 dispatcher: gen.CreateStageDispatcherPartition(3, hash), 886 } 887 consumer1 := &StageConsumerTest{ 888 value: make(chan interface{}, 2), 889 } 890 consumer2 := &StageConsumerTest{ 891 value: make(chan interface{}, 2), 892 } 893 consumer3 := &StageConsumerTest{ 894 value: make(chan interface{}, 2), 895 } 896 897 fmt.Printf("... starting Producer (StageDispatcherPartition): ") 898 producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil) 899 if errP != nil { 900 t.Fatal(errP) 901 } 902 fmt.Println("OK") 903 fmt.Printf("... starting Consumer1: ") 904 consumer1Process, errC1 := node.Spawn("stageConsumer1", gen.ProcessOptions{}, consumer1, nil) 905 if errC1 != nil { 906 t.Fatal(errC1) 907 } 908 fmt.Println("OK") 909 sub1, _ := consumer1.Subscribe(consumer1Process, "stageProducer", subOpts1) 910 fmt.Printf("... Producer handled subscription request from Consumer1: ") 911 expected1 := etf.Tuple{sub1, uint(subOpts1.MaxDemand)} 912 expected2 := sub1 913 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 914 915 fmt.Printf("... Consumer1 handled subscription confirmation from Producer: ") 916 waitForResultWithValue(t, consumer1.value, sub1) 917 918 fmt.Printf("... starting Consumer2: ") 919 consumer2Process, errC2 := node.Spawn("stageConsumer2", gen.ProcessOptions{}, consumer2, nil) 920 if errC2 != nil { 921 t.Fatal(errC2) 922 } 923 fmt.Println("OK") 924 sub2, _ := consumer2.Subscribe(consumer2Process, "stageProducer", subOpts2) 925 fmt.Printf("... Producer handled subscription request from Consumer2: ") 926 expected1 = etf.Tuple{sub2, uint(subOpts2.MaxDemand)} 927 expected2 = sub2 928 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 929 fmt.Printf("... Consumer2 handled subscription confirmation from Producer: ") 930 waitForResultWithValue(t, consumer2.value, sub2) 931 932 fmt.Printf("... starting Consumer3: ") 933 consumer3Process, errC3 := node.Spawn("stageConsumer3", gen.ProcessOptions{}, consumer3, nil) 934 if errC3 != nil { 935 t.Fatal(errC3) 936 } 937 fmt.Println("OK") 938 sub3, _ := consumer3.Subscribe(consumer3Process, "stageProducer", subOpts3) 939 fmt.Printf("... Producer handled subscription request from Consumer3: ") 940 expected1 = etf.Tuple{sub3, uint(subOpts3.MaxDemand)} 941 expected2 = sub3 942 waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2}) 943 fmt.Printf("... Consumer3 handled subscription confirmation from Producer: ") 944 waitForResultWithValue(t, consumer3.value, sub3) 945 946 fmt.Printf("... Producer sent 15 events. 3 of them should be discarded by 'hash' function: ") 947 events := etf.List{ 948 1, 2000, 200, "a", 90, "b", 80, 3000, 600, 9000, "c", 5, 1000, 100, 30, 949 } 950 producer.SendEvents(producerProcess, events) 951 fmt.Println("OK") 952 expEvents1 := etf.List{ 953 1, 90, 80, 5, // left in the queue: 100, 30, 954 } 955 exp1 := etf.Tuple{"events", sub1, expEvents1} 956 fmt.Printf("... Consumer1 should receive 4: ") 957 waitForResultWithValue(t, consumer1.value, exp1) 958 expEvents2 := etf.List{ 959 200, 600, 1000, 960 } 961 exp2 := etf.Tuple{"events", sub2, expEvents2} 962 fmt.Printf("... Consumer2 should receive 3: ") 963 waitForResultWithValue(t, consumer2.value, exp2) 964 expEvents3 := etf.List{ 965 2000, 3000, 9000, 966 } 967 exp3 := etf.Tuple{"events", sub3, expEvents3} 968 fmt.Printf("... Consumer2 should receive 3: ") 969 waitForResultWithValue(t, consumer3.value, exp3) 970 971 node.Stop() 972 973 }