|
27 | 27 | using namespace datastax::internal; |
28 | 28 | using namespace datastax::internal::core; |
29 | 29 |
|
| 30 | +class InorderLoadBalancingPolicy : public LoadBalancingPolicy { |
| 31 | +public: |
| 32 | + typedef SharedRefPtr<LoadBalancingPolicy> Ptr; |
| 33 | + typedef Vector<Ptr> Vec; |
| 34 | + |
| 35 | + InorderLoadBalancingPolicy() |
| 36 | + : LoadBalancingPolicy() |
| 37 | + , hosts_(new HostVec()) {} |
| 38 | + |
| 39 | + virtual void init(const Host::Ptr& connected_host, const HostMap& hosts, Random* random, |
| 40 | + const String& local_dc) { |
| 41 | + hosts_->reserve(hosts.size()); |
| 42 | + std::transform(hosts.begin(), hosts.end(), std::back_inserter(*hosts_), GetHost()); |
| 43 | + } |
| 44 | + |
| 45 | + virtual CassHostDistance distance(const Host::Ptr& host) const { |
| 46 | + return CASS_HOST_DISTANCE_LOCAL; |
| 47 | + } |
| 48 | + |
| 49 | + virtual bool is_host_up(const Address& address) const { |
| 50 | + return std::find_if(hosts_->begin(), hosts_->end(), FindAddress(address)) != hosts_->end(); |
| 51 | + } |
| 52 | + |
| 53 | + virtual void on_host_added(const Host::Ptr& host) { add_host(hosts_, host); } |
| 54 | + |
| 55 | + virtual void on_host_removed(const Host::Ptr& host) { remove_host(hosts_, host); } |
| 56 | + |
| 57 | + virtual void on_host_up(const Host::Ptr& host) { add_host(hosts_, host); } |
| 58 | + |
| 59 | + virtual void on_host_down(const Address& address) { remove_host(hosts_, address); } |
| 60 | + |
| 61 | + virtual QueryPlan* new_query_plan(const String& keyspace, RequestHandler* request_handler, |
| 62 | + const TokenMap* token_map) { |
| 63 | + return new InternalQueryPlan(hosts_); |
| 64 | + } |
| 65 | + |
| 66 | + virtual LoadBalancingPolicy* new_instance() { return new InorderLoadBalancingPolicy(); } |
| 67 | + |
| 68 | +private: |
| 69 | + struct FindAddress { |
| 70 | + |
| 71 | + FindAddress(const Address& address) |
| 72 | + : address(address) {} |
| 73 | + |
| 74 | + bool operator()(const Host::Ptr& host) const { return host->address() == address; } |
| 75 | + |
| 76 | + Address address; |
| 77 | + }; |
| 78 | + |
| 79 | + class InternalQueryPlan : public datastax::internal::core::QueryPlan { |
| 80 | + public: |
| 81 | + InternalQueryPlan(const CopyOnWriteHostVec& hosts) |
| 82 | + : index_(0) |
| 83 | + , hosts_(hosts) {} |
| 84 | + |
| 85 | + virtual Host::Ptr compute_next() { |
| 86 | + if (index_ < hosts_->size()) { |
| 87 | + return (*hosts_)[index_++]; |
| 88 | + } |
| 89 | + return Host::Ptr(); |
| 90 | + } |
| 91 | + |
| 92 | + private: |
| 93 | + size_t index_; |
| 94 | + CopyOnWriteHostVec hosts_; |
| 95 | + }; |
| 96 | + |
| 97 | +private: |
| 98 | + CopyOnWriteHostVec hosts_; |
| 99 | +}; |
| 100 | + |
30 | 101 | class RequestProcessorUnitTest : public EventLoopTest { |
31 | 102 | public: |
32 | 103 | RequestProcessorUnitTest() |
33 | 104 | : EventLoopTest("RequestProcessorUnitTest") {} |
34 | 105 |
|
35 | | - HostMap generate_hosts() { |
| 106 | + HostMap generate_hosts(size_t num_hosts = 3) { |
36 | 107 | HostMap hosts; |
37 | | - Host::Ptr host1(new Host(Address("127.0.0.1", PORT))); |
38 | | - Host::Ptr host2(new Host(Address("127.0.0.2", PORT))); |
39 | | - Host::Ptr host3(new Host(Address("127.0.0.3", PORT))); |
40 | | - hosts[host1->address()] = host1; |
41 | | - hosts[host2->address()] = host2; |
42 | | - hosts[host3->address()] = host3; |
| 108 | + num_hosts = std::min(num_hosts, static_cast<size_t>(255)); |
| 109 | + for (size_t i = 1; i <= num_hosts; ++i) { |
| 110 | + char buf[64]; |
| 111 | + sprintf(buf, "127.0.0.%d", static_cast<int>(i)); |
| 112 | + Host::Ptr host(new Host(Address(buf, PORT))); |
| 113 | + hosts[host->address()] = host; |
| 114 | + } |
43 | 115 | return hosts; |
44 | 116 | } |
45 | 117 |
|
@@ -619,3 +691,73 @@ TEST_F(RequestProcessorUnitTest, RequestTimeout) { |
619 | 691 | processor->close(); |
620 | 692 | ASSERT_TRUE(close_future->wait_for(WAIT_FOR_TIME)); |
621 | 693 | } |
| 694 | + |
| 695 | +TEST_F(RequestProcessorUnitTest, LowNumberOfStreams) { |
| 696 | + mockssandra::SimpleRequestHandlerBuilder builder; |
| 697 | + builder.on(mockssandra::OPCODE_QUERY) |
| 698 | + .wait(1000) // Give time for the streams to run out |
| 699 | + .system_local() |
| 700 | + .system_peers() |
| 701 | + .empty_rows_result(1); |
| 702 | + mockssandra::SimpleCluster cluster(builder.build(), 2); // Two node cluster |
| 703 | + ASSERT_EQ(cluster.start_all(), 0); |
| 704 | + |
| 705 | + Future::Ptr close_future(new Future()); |
| 706 | + CloseListener::Ptr listener(new CloseListener(close_future)); |
| 707 | + |
| 708 | + HostMap hosts(generate_hosts(2)); |
| 709 | + Future::Ptr connect_future(new Future()); |
| 710 | + |
| 711 | + ExecutionProfile profile; |
| 712 | + profile.set_load_balancing_policy(new InorderLoadBalancingPolicy()); |
| 713 | + profile.set_speculative_execution_policy(new NoSpeculativeExecutionPolicy()); |
| 714 | + profile.set_retry_policy(new DefaultRetryPolicy()); |
| 715 | + |
| 716 | + RequestProcessorSettings settings; |
| 717 | + settings.default_profile = profile; |
| 718 | + settings.request_queue_size = 2 * CASS_MAX_STREAMS + 1; // Create a request queue with enough room |
| 719 | + |
| 720 | + RequestProcessorInitializer::Ptr initializer(new RequestProcessorInitializer( |
| 721 | + hosts.begin()->second, PROTOCOL_VERSION, hosts, TokenMap::Ptr(), "", |
| 722 | + bind_callback(on_connected, connect_future.get()))); |
| 723 | + initializer->with_settings(settings)->with_listener(listener.get())->initialize(event_loop()); |
| 724 | + |
| 725 | + ASSERT_TRUE(connect_future->wait_for(WAIT_FOR_TIME)); |
| 726 | + EXPECT_FALSE(connect_future->error()); |
| 727 | + RequestProcessor::Ptr processor(connect_future->processor()); |
| 728 | + |
| 729 | + // Saturate the hosts connections, but leave one stream. |
| 730 | + for (int i = 0; i < 2 * CASS_MAX_STREAMS - 1; ++i) { |
| 731 | + ResponseFuture::Ptr response_future(new ResponseFuture()); |
| 732 | + Statement::Ptr request(new QueryRequest("SELECT * FROM table")); |
| 733 | + RequestHandler::Ptr request_handler(new RequestHandler(request, response_future)); |
| 734 | + processor->process_request(request_handler); |
| 735 | + } |
| 736 | + |
| 737 | + { // Try two more requests. One should succeed on "127.0.0.2" and the other should fail (out of |
| 738 | + // streams). |
| 739 | + ResponseFuture::Ptr response_future(new ResponseFuture()); |
| 740 | + |
| 741 | + Statement::Ptr request(new QueryRequest("SELECT * FROM table")); |
| 742 | + request->set_record_attempted_addresses(true); |
| 743 | + RequestHandler::Ptr request_handler(new RequestHandler(request, response_future)); |
| 744 | + processor->process_request(request_handler); |
| 745 | + |
| 746 | + ResponseFuture::Ptr response_future_fail(new ResponseFuture()); |
| 747 | + RequestHandler::Ptr request_handler_fail(new RequestHandler( |
| 748 | + Statement::Ptr(new QueryRequest("SELECT * FROM table")), response_future_fail)); |
| 749 | + processor->process_request(request_handler_fail); |
| 750 | + ASSERT_TRUE(response_future_fail->wait_for(WAIT_FOR_TIME)); |
| 751 | + ASSERT_TRUE(response_future_fail->error()); |
| 752 | + EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, response_future_fail->error()->code); |
| 753 | + |
| 754 | + ASSERT_TRUE(response_future->wait_for(WAIT_FOR_TIME)); |
| 755 | + EXPECT_FALSE(response_future->error()); |
| 756 | + AddressVec attempted = response_future->attempted_addresses(); |
| 757 | + ASSERT_GE(attempted.size(), 1u); |
| 758 | + EXPECT_EQ(attempted[0], Address("127.0.0.2", PORT)); |
| 759 | + } |
| 760 | + |
| 761 | + processor->close(); |
| 762 | + ASSERT_TRUE(close_future->wait_for(WAIT_FOR_TIME)); |
| 763 | +} |
0 commit comments