|
21 | 21 | #include <pulsar/Client.h> |
22 | 22 |
|
23 | 23 | #include <algorithm> |
| 24 | +#include <atomic> |
24 | 25 | #include <boost/exception/all.hpp> |
25 | 26 | #include <chrono> |
26 | 27 | #include <future> |
|
36 | 37 | #include "lib/Future.h" |
37 | 38 | #include "lib/HTTPLookupService.h" |
38 | 39 | #include "lib/LogUtils.h" |
| 40 | +#include "lib/LookupDataResult.h" |
39 | 41 | #include "lib/RetryableLookupService.h" |
40 | 42 | #include "lib/TimeUtils.h" |
41 | 43 | #include "lib/Utils.h" |
| 44 | +#include "pulsar/Result.h" |
42 | 45 |
|
43 | 46 | DECLARE_LOG_OBJECT() |
44 | 47 |
|
@@ -500,3 +503,62 @@ TEST(LookupServiceTest, testRedirectionLimit) { |
500 | 503 | } |
501 | 504 | } |
502 | 505 | } |
| 506 | + |
| 507 | +static std::atomic_bool firstTime{true}; |
| 508 | + |
| 509 | +class MockLookupService : public BinaryProtoLookupService { |
| 510 | + public: |
| 511 | + using BinaryProtoLookupService::BinaryProtoLookupService; |
| 512 | + |
| 513 | + Future<Result, LookupDataResultPtr> getPartitionMetadataAsync(const TopicNamePtr& topicName) override { |
| 514 | + bool expected = true; |
| 515 | + if (firstTime.compare_exchange_strong(expected, false)) { |
| 516 | + // Trigger the retry |
| 517 | + LOG_INFO("Fail the lookup for " << topicName->toString() << " intentionally"); |
| 518 | + Promise<Result, LookupDataResultPtr> promise; |
| 519 | + promise.setFailed(ResultRetryable); |
| 520 | + return promise.getFuture(); |
| 521 | + } |
| 522 | + return BinaryProtoLookupService::getPartitionMetadataAsync(topicName); |
| 523 | + } |
| 524 | +}; |
| 525 | + |
| 526 | +TEST(LookupServiceTest, testAfterClientShutdown) { |
| 527 | + auto client = std::make_shared<ClientImpl>("pulsar://localhost:6650", ClientConfiguration{}, |
| 528 | + [](const std::string& serviceUrl, const ClientConfiguration&, |
| 529 | + ConnectionPool& pool, const AuthenticationPtr&) { |
| 530 | + return std::make_shared<MockLookupService>( |
| 531 | + serviceUrl, pool, ClientConfiguration{}); |
| 532 | + }); |
| 533 | + std::promise<Result> promise; |
| 534 | + client->subscribeAsync("lookup-service-test-after-client-shutdown", "sub", ConsumerConfiguration{}, |
| 535 | + [&promise](Result result, const Consumer&) { promise.set_value(result); }); |
| 536 | + client->shutdown(); |
| 537 | + EXPECT_EQ(ResultDisconnected, promise.get_future().get()); |
| 538 | + |
| 539 | + firstTime = true; |
| 540 | + std::promise<Result> promise2; |
| 541 | + client->subscribeAsync("lookup-service-test-retry-after-destroyed", "sub", ConsumerConfiguration{}, |
| 542 | + [&promise2](Result result, const Consumer&) { promise2.set_value(result); }); |
| 543 | + EXPECT_EQ(ResultAlreadyClosed, promise2.get_future().get()); |
| 544 | +} |
| 545 | + |
| 546 | +TEST(LookupServiceTest, testRetryAfterDestroyed) { |
| 547 | + auto executorProvider = std::make_shared<ExecutorServiceProvider>(1); |
| 548 | + ConnectionPool pool({}, executorProvider, AuthFactory::Disabled(), ""); |
| 549 | + |
| 550 | + auto internalLookupService = |
| 551 | + std::make_shared<MockLookupService>("pulsar://localhost:6650", pool, ClientConfiguration{}); |
| 552 | + auto lookupService = |
| 553 | + RetryableLookupService::create(internalLookupService, std::chrono::seconds(30), executorProvider); |
| 554 | + |
| 555 | + // Simulate the race condition that `getPartitionMetadataAsync` is called after `close` is called on the |
| 556 | + // lookup service. |
| 557 | + lookupService->close(); |
| 558 | + std::atomic<Result> result{ResultUnknownError}; |
| 559 | + lookupService->getPartitionMetadataAsync(TopicName::get("lookup-service-test-retry-after-destroyed")) |
| 560 | + .addListener([&result](Result innerResult, const LookupDataResultPtr&) { result = innerResult; }); |
| 561 | + EXPECT_EQ(ResultAlreadyClosed, result.load()); |
| 562 | + pool.close(); |
| 563 | + executorProvider->close(); |
| 564 | +} |
0 commit comments