github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/archive/archive_test.go (about) 1 // Copyright (C) 2021-2022 Talos, Inc. 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 archive 16 17 import ( 18 "flag" 19 "log" 20 "math/rand" 21 "os" 22 "sync" 23 "testing" 24 "time" 25 26 "github.com/lirm/aeron-go/aeron" 27 "github.com/lirm/aeron-go/aeron/idlestrategy" 28 "github.com/lirm/aeron-go/aeron/logging" 29 "github.com/lirm/aeron-go/archive/codecs" 30 ) 31 32 // Rather than mock or spawn an archive-media-driver we're just seeing 33 // if we can connect to one and if we can we'll run some tests. If the 34 // init fails to connect then we'll skip the tests 35 // FIXME:BiggerPicture this plan fails as aeron-go calls log.Fatalf() if the media driver is not running! 36 var archive *Archive 37 var haveArchive = false 38 var DEBUG = false 39 40 type TestCases struct { 41 sampleStream int32 42 sampleChannel string 43 replayStream int32 44 replayChannel string 45 } 46 47 var testCases = []TestCases{ 48 {int32(*TestConfig.SampleStream), *TestConfig.SampleChannel, int32(*TestConfig.ReplayStream), *TestConfig.ReplayChannel}, 49 } 50 51 // For testing async events 52 type TestCounters struct { 53 recordingSignalCount int 54 recordingEventStartedCount int 55 recordingEventProgressCount int 56 recordingEventStoppedCount int 57 } 58 59 var testCounters TestCounters 60 61 func RecordingSignalListener(rse *codecs.RecordingSignalEvent) { 62 testCounters.recordingSignalCount++ 63 } 64 65 func RecordingEventStartedListener(rs *codecs.RecordingStarted) { 66 testCounters.recordingEventStartedCount++ 67 } 68 69 func RecordingEventProgressListener(rp *codecs.RecordingProgress) { 70 testCounters.recordingEventProgressCount++ 71 } 72 73 func RecordingEventStoppedListener(rs *codecs.RecordingStopped) { 74 testCounters.recordingEventStoppedCount++ 75 } 76 77 func TestMain(m *testing.M) { 78 flag.Parse() 79 80 var err error 81 context := aeron.NewContext() 82 context.AeronDir(*TestConfig.AeronPrefix) 83 options := DefaultOptions() 84 85 // Cleaning up after test runs can take a little time so we 86 // randomize the streams in use to make that less likely 87 r := rand.New(rand.NewSource(time.Now().UnixNano())) 88 testCases[0].sampleStream += int32(r.Intn(1000)) 89 testCases[0].replayStream += int32(r.Intn(1000)) 90 if testCases[0].sampleStream == testCases[0].replayStream { 91 testCases[0].replayStream++ 92 } 93 94 if *TestConfig.Debug { 95 log.Printf("Setting verbose logging") 96 log.Printf("Using %s/%d and %s/%d", testCases[0].sampleChannel, testCases[0].sampleStream, testCases[0].replayChannel, testCases[0].replayStream) 97 options.ArchiveLoglevel = logging.DEBUG 98 DEBUG = true 99 } 100 101 archive, err = NewArchive(options, context) 102 if err != nil || archive == nil { 103 log.Printf("archive-media-driver connection failed, skipping all archive_tests:%s", err.Error()) 104 return 105 } 106 haveArchive = true 107 108 result := m.Run() 109 if result != 0 { 110 archive.Close() 111 os.Exit(result) 112 } 113 114 // FIXME disable auth testing 115 archive.Close() 116 os.Exit(result) 117 118 // Test auth 119 options.AuthEnabled = true 120 options.AuthCredentials = []uint8(*TestConfig.AuthCredentials) 121 options.AuthChallenge = []uint8(*TestConfig.AuthChallenge) 122 options.AuthResponse = []uint8(*TestConfig.AuthResponse) 123 124 testCases[0].sampleStream += int32(r.Intn(1000)) 125 testCases[0].replayStream += int32(r.Intn(1000)) 126 if testCases[0].sampleStream == testCases[0].replayStream { 127 testCases[0].replayStream++ 128 } 129 130 archive, err = NewArchive(options, context) 131 if err != nil || archive == nil { 132 log.Printf("secure-archive-media-driver connection failed, skipping allsecure archive_tests:%s", err.Error()) 133 haveArchive = false 134 return 135 } 136 137 haveArchive = true 138 result = m.Run() 139 140 archive.Close() 141 os.Exit(result) 142 } 143 144 // This should always pass 145 func TestConnection(t *testing.T) { 146 if !haveArchive { 147 return 148 } 149 150 // PollForErrorEvents should be safe 151 for i := 0; i < 10; i++ { 152 count, err := archive.PollForErrorResponse() 153 if err != nil { 154 t.Logf("PollforErrorRespose() recieved %d responses, err is %s", count, err) 155 t.FailNow() 156 } 157 idler := idlestrategy.Sleeping{SleepFor: time.Millisecond * 100} 158 idler.Idle(0) 159 } 160 161 } 162 163 // Test KeepAlive 164 func TestKeepAlive(t *testing.T) { 165 if !haveArchive { 166 return 167 } 168 169 if testing.Verbose() && DEBUG { 170 logging.SetLevel(logging.DEBUG, "archive") 171 } 172 173 if err := archive.KeepAlive(); err != nil { 174 t.Log(err) 175 t.FailNow() 176 } 177 } 178 179 // Helper to check values of counters 180 func CounterValuesMatch(c TestCounters, signals int, started int, progress int, stopped int, t *testing.T) bool { 181 if testCounters.recordingSignalCount != signals { 182 t.Logf("testCounters.recordingSignalCount[%d] != signals[%d]", testCounters.recordingSignalCount, signals) 183 return false 184 } 185 if testCounters.recordingEventStartedCount != started { 186 t.Logf("testCounters.recordingEventStartedCount[%d] != started[%d]", testCounters.recordingEventStartedCount, started) 187 return false 188 } 189 if testCounters.recordingEventProgressCount != progress { 190 t.Logf("testCounters.recordingEventProgressCount[%d] != progress[%d]", testCounters.recordingEventProgressCount, progress) 191 return false 192 } 193 if testCounters.recordingEventStoppedCount != stopped { 194 t.Logf("testCounters.recordingEventStoppedCount[%d] != stopped[%d]", testCounters.recordingEventStoppedCount, stopped) 195 return false 196 } 197 return true 198 } 199 200 // Test that Archive RPCs will fail correctly 201 func TestRPCFailure(t *testing.T) { 202 if !haveArchive { 203 return 204 } 205 206 if testing.Verbose() && DEBUG { 207 logging.SetLevel(logging.DEBUG, "archive") 208 } 209 210 // Ask to stop a bogus recording 211 res, err := archive.StopRecordingByIdentity(0xdeadbeef) 212 if err == nil || res { 213 t.Logf("RPC succeeded and should have failed") 214 t.FailNow() 215 } 216 217 } 218 219 // Test the recording event signals appear 220 func TestAsyncEvents(t *testing.T) { 221 if !haveArchive { 222 return 223 } 224 225 if testing.Verbose() && DEBUG { 226 logging.SetLevel(logging.DEBUG, "archive") 227 } 228 229 archive.Listeners.RecordingSignalListener = RecordingSignalListener 230 archive.Listeners.RecordingEventStartedListener = RecordingEventStartedListener 231 archive.Listeners.RecordingEventProgressListener = RecordingEventProgressListener 232 archive.Listeners.RecordingEventStoppedListener = RecordingEventStoppedListener 233 234 testCounters = TestCounters{0, 0, 0, 0} 235 if !CounterValuesMatch(testCounters, 0, 0, 0, 0, t) { 236 t.Log("Async event counters mismatch") 237 t.FailNow() 238 } 239 240 archive.EnableRecordingEvents() 241 archive.RecordingEventsPoll() 242 243 if !CounterValuesMatch(testCounters, 0, 0, 0, 0, t) { 244 t.Log("Async event counters mismatch") 245 t.FailNow() 246 } 247 248 publication, err := archive.AddRecordedPublication(testCases[0].sampleChannel, testCases[0].sampleStream) 249 if err != nil { 250 t.Log(err) 251 t.FailNow() 252 } 253 254 // Delay a little to get the publication is established 255 idler := idlestrategy.Sleeping{SleepFor: time.Millisecond * 100} 256 idler.Idle(0) 257 258 archive.RecordingEventsPoll() 259 if !CounterValuesMatch(testCounters, 1, 1, 0, 0, t) { 260 t.Log("Async event counters mismatch") 261 t.FailNow() 262 } 263 264 if err := archive.StopRecordingByPublication(*publication); err != nil { 265 t.Log(err) 266 t.FailNow() 267 } 268 269 if !CounterValuesMatch(testCounters, 2, 1, 0, 0, t) { 270 t.Log("Async event counters mismatch") 271 t.FailNow() 272 } 273 274 archive.RecordingEventsPoll() 275 if !CounterValuesMatch(testCounters, 2, 1, 0, 1, t) { 276 t.Log("Async event counters mismatch") 277 t.FailNow() 278 } 279 280 // Cleanup 281 archive.DisableRecordingEvents() 282 archive.Listeners.RecordingSignalListener = nil 283 archive.Listeners.RecordingEventStartedListener = nil 284 archive.Listeners.RecordingEventProgressListener = nil 285 archive.Listeners.RecordingEventStoppedListener = nil 286 testCounters = TestCounters{0, 0, 0, 0} 287 if !CounterValuesMatch(testCounters, 0, 0, 0, 0, t) { 288 t.Log("Async event counters mismatch") 289 t.FailNow() 290 } 291 292 publication.Close() 293 } 294 295 // Test PollForErrorEvents 296 func TestPollForErrorEvents(t *testing.T) { 297 if !haveArchive { 298 return 299 } 300 301 if testing.Verbose() && DEBUG { 302 logging.SetLevel(logging.DEBUG, "archive") 303 } 304 305 archive.Listeners.RecordingSignalListener = RecordingSignalListener 306 307 testCounters = TestCounters{0, 0, 0, 0} 308 if !CounterValuesMatch(testCounters, 0, 0, 0, 0, t) { 309 t.Log("Async event counters mismatch") 310 t.FailNow() 311 } 312 313 publication, err := archive.AddRecordedPublication(testCases[0].sampleChannel, testCases[0].sampleStream) 314 if err != nil { 315 t.Log(err) 316 t.FailNow() 317 } 318 319 // PollForErrorEvents should simply return successfully with the recording signal event having arrived 320 _, err = archive.PollForErrorResponse() 321 if err != nil { 322 t.Log(err) 323 t.FailNow() 324 } 325 if !CounterValuesMatch(testCounters, 1, 0, 0, 0, t) { 326 t.Log("Async event counters mismatch") 327 t.FailNow() 328 } 329 330 // Delay a little to get the publication established 331 idler := idlestrategy.Sleeping{SleepFor: time.Millisecond * 500} 332 idler.Idle(0) 333 334 if err := archive.StopRecordingByPublication(*publication); err != nil { 335 t.Log(err) 336 t.FailNow() 337 } 338 publication.Close() 339 340 // Now we'll reach inside the archive a little to leave an outstanding request in the queue 341 // We know a StopRecording of a non-existent recording should fail but this call will succeed 342 // as it's only the request half 343 err = archive.Proxy.StopRecordingSubscriptionRequest(12345, 54321) 344 if err != nil { 345 t.Log(err) 346 t.FailNow() 347 } 348 349 // So now PollForErrorResponse should get the reply to that 350 // and fail because overlapping I/O is very bad. Note that if 351 // normal archive calls are made then we have locking to 352 // prevent this 353 idler.Idle(0) 354 count, err := archive.Control.PollForErrorResponse() 355 if err == nil { 356 t.Logf("PollForErrorResponse succeeded and should have failed: count is %d", count) 357 t.FailNow() 358 } 359 if count != 1 { 360 t.Logf("PollForErrorResponse failed correctly but count is %d and should have been 1", count) 361 t.FailNow() 362 } 363 364 // Then PollForErrorResponse should see no further messages 365 idler.Idle(0) 366 count, err = archive.Control.PollForErrorResponse() 367 if err != nil { 368 t.Logf("PollForErrorResponse failed") 369 t.FailNow() 370 } 371 if count != 0 { 372 t.Logf("PollForErrorResponse succeeded but count is %d and should have been 0", count) 373 t.FailNow() 374 } 375 } 376 377 // Test adding a recording and then removing it - by Publication (session specific) 378 func TestStartStopRecordingByPublication(t *testing.T) { 379 if !haveArchive { 380 return 381 } 382 383 if testing.Verbose() && DEBUG { 384 logging.SetLevel(logging.DEBUG, "archive") 385 } 386 387 publication, err := archive.AddRecordedPublication(testCases[0].sampleChannel, testCases[0].sampleStream) 388 if err != nil { 389 t.Log(err) 390 t.FailNow() 391 } 392 393 // Delay a little to get the publication is established 394 idler := idlestrategy.Sleeping{SleepFor: time.Millisecond * 100} 395 idler.Idle(0) 396 397 if err := archive.StopRecordingByPublication(*publication); err != nil { 398 t.Log(err) 399 t.FailNow() 400 } 401 publication.Close() 402 403 } 404 405 // Test adding a recording and then removing it - by Subscription 406 func TestStartStopRecordingBySubscription(t *testing.T) { 407 if !haveArchive { 408 return 409 } 410 411 if testing.Verbose() && DEBUG { 412 logging.SetLevel(logging.DEBUG, "archive") 413 } 414 415 // Start snd stop by subscription 416 subscriptionID, err := archive.StartRecording(testCases[0].sampleChannel, testCases[0].sampleStream, true, true) 417 if err != nil { 418 t.Log(err) 419 t.FailNow() 420 } 421 err = archive.StopRecordingBySubscriptionId(subscriptionID) 422 if err != nil { 423 t.Log(err) 424 t.FailNow() 425 } 426 } 427 428 // Test adding a recording and then removing it - by Channel and Stream 429 func TestStartStopRecordingByChannelAndStream(t *testing.T) { 430 if !haveArchive { 431 return 432 } 433 434 if testing.Verbose() && DEBUG { 435 logging.SetLevel(logging.DEBUG, "archive") 436 } 437 438 // Start snd stop by channel&stream 439 _, err := archive.StartRecording(testCases[0].sampleChannel, testCases[0].sampleStream, true, true) 440 if err != nil { 441 t.Log(err) 442 t.FailNow() 443 } 444 err = archive.StopRecording(testCases[0].sampleChannel, testCases[0].sampleStream) 445 if err != nil { 446 t.Log(err) 447 t.FailNow() 448 } 449 450 // Start snd stop by identity is done in other tests 451 } 452 453 // Test adding a recording and then removing it, checking the listing counts between times 454 func TestListRecordings(t *testing.T) { 455 if !haveArchive { 456 return 457 } 458 459 if testing.Verbose() && DEBUG { 460 logging.SetLevel(logging.DEBUG, "archive") 461 } 462 463 recordings, err := archive.ListRecordingsForUri(0, 100, testCases[0].sampleChannel, testCases[0].sampleStream) 464 if err != nil { 465 t.Log(err) 466 t.FailNow() 467 } 468 initial := len(recordings) 469 t.Logf("Initial count is %d", initial) 470 471 // Add a recording 472 subscriptionID, err := archive.StartRecording(testCases[0].sampleChannel, testCases[0].sampleStream, true, true) 473 if err != nil { 474 t.Log(err) 475 t.FailNow() 476 } 477 t.Logf("SubscriptionID is %d", subscriptionID) 478 479 // Add a publication on that 480 publication, err := archive.AddPublication(testCases[0].sampleChannel, testCases[0].sampleStream) 481 if err != nil { 482 t.Log(err) 483 t.FailNow() 484 } 485 if testing.Verbose() && DEBUG { 486 t.Logf("Publication is %#v", publication) 487 } 488 489 recordings, err = archive.ListRecordingsForUri(0, 100, testCases[0].sampleChannel, testCases[0].sampleStream) 490 if err != nil { 491 t.Log(err) 492 t.FailNow() 493 } 494 if len(recordings) == 0 { 495 t.Log("No Recordings!") 496 t.FailNow() 497 } 498 499 // Grab the recordingID 500 recordingID := recordings[len(recordings)-1].RecordingId 501 t.Logf("Working count is %d, recordingID is %d", len(recordings), recordingID) 502 503 // Cleanup 504 res, err := archive.StopRecordingByIdentity(recordingID) 505 if err != nil { 506 t.Logf("StopRecordingByIdentity(%d) failed: %s", recordingID, err.Error()) 507 } else if !res { 508 t.Logf("StopRecordingByIdentity(%d) failed", recordingID) 509 } 510 if err := archive.PurgeRecording(recordingID); err != nil { 511 t.Logf("PurgeRecording(%d) failed: %s", recordingID, err.Error()) 512 } 513 publication.Close() 514 515 recordings, err = archive.ListRecordingsForUri(0, 100, testCases[0].sampleChannel, testCases[0].sampleStream) 516 if err != nil { 517 t.Log(err) 518 t.FailNow() 519 } 520 final := len(recordings) 521 t.Logf("Final count is %d", final) 522 523 if initial != final { 524 t.Logf("Number of recordings changed from %d to %d", initial, final) 525 t.Fail() 526 } 527 } 528 529 // Test starting a replay 530 func TestStartStopReplay(t *testing.T) { 531 if !haveArchive { 532 return 533 } 534 535 // Add a recording to make sure there is one 536 subscriptionID, err := archive.StartRecording(testCases[0].sampleChannel, testCases[0].sampleStream, true, true) 537 if err != nil { 538 t.Log(err) 539 t.FailNow() 540 } 541 t.Logf("SubscriptionID is %d", subscriptionID) 542 543 // Add a publication on that 544 publication, err := archive.AddPublication(testCases[0].sampleChannel, testCases[0].sampleStream) 545 if err != nil { 546 t.Log(err) 547 t.FailNow() 548 } 549 t.Logf("Publication found %v", publication) 550 551 recordings, err := archive.ListRecordingsForUri(0, 100, "aeron", testCases[0].sampleStream) 552 if err != nil { 553 t.Log(err) 554 t.FailNow() 555 } 556 if len(recordings) == 0 { 557 t.Log("No recordings!") 558 t.FailNow() 559 } 560 561 // That should give us a recordingID 562 recordingID := recordings[len(recordings)-1].RecordingId 563 564 replayID, err := archive.StartReplay(recordingID, 0, RecordingLengthNull, testCases[0].replayChannel, testCases[0].replayStream) 565 if err != nil { 566 t.Logf("StartReplay failed: %d, %s", replayID, err.Error()) 567 t.FailNow() 568 } 569 if err := archive.StopReplay(replayID); err != nil { 570 t.Logf("StopReplay(%d) failed: %s", replayID, err.Error()) 571 } 572 573 // So ListRecordingsForUri should find something 574 recordings, err = archive.ListRecordingsForUri(0, 100, testCases[0].sampleChannel, testCases[0].sampleStream) 575 if err != nil { 576 t.Log(err) 577 t.FailNow() 578 } 579 t.Logf("Working count is %d, recordingID is %d", len(recordings), recordingID) 580 581 // And ListRecordings should also find something 582 recordings, err = archive.ListRecordings(0, 10) 583 if err != nil { 584 t.Log(err) 585 t.FailNow() 586 } 587 if len(recordings) == 0 { 588 t.Log("No recordings!") 589 t.FailNow() 590 } 591 recordingID = recordings[len(recordings)-1].RecordingId 592 t.Logf("Working count is %d, recordingID is %d", len(recordings), recordingID) 593 594 // ListRecording should find one by the above Id 595 recording, err := archive.ListRecording(recordingID) 596 if err != nil { 597 t.Log(err) 598 t.FailNow() 599 } 600 if recordingID != recording.RecordingId { 601 t.Log("ListRecording did not return the correct record descriptor") 602 t.FailNow() 603 } 604 t.Logf("ListRecording(%d) returned %#v", recordingID, *recording) 605 606 // ListRecording should not find one with a bad ID 607 badID := int64(-127) 608 recording, err = archive.ListRecording(badID) 609 if err != nil { 610 t.Log(err) 611 t.FailNow() 612 } 613 if recording != nil { 614 t.Log("ListRecording returned a record descriptor and should not have") 615 t.FailNow() 616 } 617 t.Logf("ListRecording(%d) correctly returned nil", badID) 618 619 // While we're here, check ListRecordingSubscription is working 620 descriptors, err := archive.ListRecordingSubscriptions(0, 10, false, 0, "aeron") 621 if err != nil { 622 t.Logf("ListRecordingSubscriptions() returned error:%s", err.Error()) 623 t.FailNow() 624 } 625 if descriptors == nil { 626 t.Logf("ListRecordingSubscriptions() returned no descriptors") 627 t.FailNow() 628 } 629 t.Logf("ListRecordingSubscriptions() returned %d descriptor(s)", len(descriptors)) 630 631 // Cleanup 632 res, err := archive.StopRecordingByIdentity(recordingID) 633 if err != nil { 634 t.Logf("StopRecordingByIdentity(%d) failed: %s", recordingID, err.Error()) 635 t.FailNow() 636 } 637 if !res { 638 t.Logf("StopRecordingByIdentity(%d) failed", recordingID) 639 } 640 } 641 642 // Test starting a replay 643 // FIXME:BiggerPicture Disabled as aeron calls log.Fatalf() 644 func DisabledTestAddRecordedPublicationFailure(t *testing.T) { 645 if !haveArchive { 646 return 647 } 648 649 pub, err := archive.AddRecordedPublication("bogus", 99) 650 if err != nil || pub != nil { 651 t.Logf("Add recorded publication succeeded and should have failed. error:%s, pub%#v", err, pub) 652 t.FailNow() 653 } 654 } 655 656 // Test concurrency 657 func DisabledTestConcurrentConnections(t *testing.T) { 658 var wg sync.WaitGroup 659 660 for i := 0; i < 10; i++ { 661 wg.Add(1) 662 go ConcurrentSimple(&wg, i, t) 663 } 664 wg.Wait() 665 666 } 667 668 func ConcurrentSimple(wg *sync.WaitGroup, n int, t *testing.T) { 669 670 var err error 671 context := aeron.NewContext() 672 context.AeronDir(*TestConfig.AeronPrefix) 673 options := DefaultOptions() 674 // options.ArchiveLoglevel = logging.DEBUG 675 676 defer wg.Done() 677 t.Logf("Worker %d starting", n) 678 679 // Randomize our stream 680 r := rand.New(rand.NewSource(time.Now().UnixNano())) 681 testCases[0].sampleStream += int32(r.Intn(10000)) 682 683 archive, err = NewArchive(options, context) 684 if err != nil || archive == nil { 685 t.Logf("archive-media-driver connection failed, skipping all archive_tests:%s", err.Error()) 686 return 687 } 688 689 // Thump out some Start and Stop RecordingRequests. If we do too many we'tll timeout, or need to backoff 690 if false { 691 for i := 0; i < 5; i++ { 692 _, err := archive.StartRecording(testCases[0].sampleChannel, int32(20000+n*100+i), true, true) 693 if err != nil { 694 t.Logf("StartRecording failed for worker %d, attempt %d: %s", n, i, err.Error()) 695 return 696 } 697 } 698 } 699 700 archive.Close() 701 t.Logf("Worker %d exiting", n) 702 }