| 
					
				 | 
			
			
				@@ -40,9 +40,10 @@ require_relative '../src/proto/grpc/testing/messages_pb' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 require_relative '../src/proto/grpc/testing/test_services_pb' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class RpcConfig 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  def init(rpcs_to_send, metadata_to_send) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def init(rpcs_to_send, metadata_to_send, timeout_sec = 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @rpcs_to_send = rpcs_to_send 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @metadata_to_send = metadata_to_send 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @timeout_sec = timeout_sec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   def rpcs_to_send 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @rpcs_to_send 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -50,6 +51,9 @@ class RpcConfig 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   def metadata_to_send 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @metadata_to_send 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def timeout_sec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @timeout_sec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # Some global constant mappings 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -71,6 +75,7 @@ $accumulated_stats_mu = Mutex.new 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 $num_rpcs_started_by_method = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 $num_rpcs_succeeded_by_method = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 $num_rpcs_failed_by_method = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+$accumulated_method_stats = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # RubyLogger defines a logger for gRPC based on the standard ruby logger. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 module RubyLogger 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -98,11 +103,30 @@ def create_stub(opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class StatsPerMethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def initialize() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @rpcs_started = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @result = Hash.new(0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def rpcs_started 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @rpcs_started 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def result 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @result 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def increment_rpcs_started() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @rpcs_started += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  def add_result(status_code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @result[status_code] += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class ConfigureTarget < Grpc::Testing::XdsUpdateClientConfigureService::Service 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   include Grpc::Testing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   def configure(req, _call) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    rpcs_to_send = req['types']; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    rpcs_to_send = req['types'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     metadata_to_send = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     req['metadata'].each do |m| 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       rpc = m.type 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -113,13 +137,10 @@ class ConfigureTarget < Grpc::Testing::XdsUpdateClientConfigureService::Service 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       metadata_value = m.value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       metadata_to_send[rpc][metadata_key] = metadata_value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    GRPC.logger.info("Configuring new rpcs_to_send and metadata_to_send...") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    GRPC.logger.info(rpcs_to_send) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    GRPC.logger.info(metadata_to_send) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     new_rpc_config = RpcConfig.new 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    new_rpc_config.init(rpcs_to_send, metadata_to_send) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new_rpc_config.init(rpcs_to_send, metadata_to_send, req['timeout_sec']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $rpc_config = new_rpc_config 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ClientConfigureResponse.new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ClientConfigureResponse.new() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -159,23 +180,37 @@ class TestTarget < Grpc::Testing::LoadBalancerStatsService::Service 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       rpcs_by_method: rpcs_by_method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       rpcs_by_peer: watcher['rpcs_by_peer'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       num_failures: watcher['no_remote_peer'] + watcher['rpcs_needed'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   def get_client_accumulated_stats(req, _call) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $accumulated_stats_mu.synchronize do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      all_stats_per_method = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      $accumulated_method_stats.each do |rpc, stats_per_method| 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        one_stats_per_method = LoadBalancerAccumulatedStatsResponse::MethodStats.new( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          rpcs_started: stats_per_method.rpcs_started, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          result: stats_per_method.result 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        all_stats_per_method[rpc] = one_stats_per_method 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       LoadBalancerAccumulatedStatsResponse.new( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         num_rpcs_started_by_method: $num_rpcs_started_by_method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         num_rpcs_succeeded_by_method: $num_rpcs_succeeded_by_method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        num_rpcs_failed_by_method: $num_rpcs_failed_by_method 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        num_rpcs_failed_by_method: $num_rpcs_failed_by_method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stats_per_method: all_stats_per_method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def add_stats_per_method(rpc_stats_key, status_code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  $accumulated_method_stats[rpc_stats_key].add_result(status_code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # execute 1 RPC and return remote hostname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 def execute_rpc(op, fail_on_failed_rpcs, rpc_stats_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   remote_peer = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  status_code = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   begin 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     op.execute 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if op.metadata.key?('hostname') 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -185,8 +220,10 @@ def execute_rpc(op, fail_on_failed_rpcs, rpc_stats_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if fail_on_failed_rpcs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       raise e 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    status_code = e.code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   $accumulated_stats_mu.synchronize do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    add_stats_per_method(rpc_stats_key, status_code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if remote_peer.empty? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       $num_rpcs_failed_by_method[rpc_stats_key] += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     else 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -207,6 +244,7 @@ def execute_rpc_in_thread(op, rpc_stats_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       # Doing this for consistency 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       $accumulated_stats_mu.synchronize do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         $num_rpcs_succeeded_by_method[rpc_stats_key] += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        add_stats_per_method(rpc_stats_key, 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     rescue GRPC::BadStatus => e 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       # Normal execution arrives here, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -214,6 +252,7 @@ def execute_rpc_in_thread(op, rpc_stats_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       # balancing policy" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       $accumulated_stats_mu.synchronize do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         $num_rpcs_failed_by_method[rpc_stats_key] += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        add_stats_per_method(rpc_stats_key, e.code) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -238,7 +277,8 @@ def run_test_loop(stub, target_seconds_between_rpcs, fail_on_failed_rpcs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       target_next_start += target_seconds_between_rpcs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       sleep(sleep_seconds) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    deadline = GRPC::Core::TimeConsts::from_relative_time(30) # 30 seconds 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    deadline_sec = $rpc_config.timeout_sec > 0 ? $rpc_config.timeout_sec : 30 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    deadline = GRPC::Core::TimeConsts::from_relative_time(deadline_sec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     results = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $rpc_config.rpcs_to_send.each do |rpc| 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       # rpc is in the form of :UNARY_CALL or :EMPTY_CALL here 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -246,10 +286,7 @@ def run_test_loop(stub, target_seconds_between_rpcs, fail_on_failed_rpcs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                    $rpc_config.metadata_to_send[rpc] : {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       $accumulated_stats_mu.synchronize do 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         $num_rpcs_started_by_method[rpc.to_s] += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        num_started = $num_rpcs_started_by_method[rpc.to_s] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if num_started % 100 == 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          GRPC.logger.info("Started #{num_started} of #{rpc}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        $accumulated_method_stats[rpc.to_s].increment_rpcs_started() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       if rpc == :UNARY_CALL 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         op = stub.unary_call(simple_req, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -266,11 +303,8 @@ def run_test_loop(stub, target_seconds_between_rpcs, fail_on_failed_rpcs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       rpc_stats_key = rpc.to_s 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       if metadata.key?('rpc-behavior') and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (metadata['rpc-behavior'] == 'keep-open') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        num_open_threads = keep_open_threads.size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if num_open_threads % 50 == 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          GRPC.logger.info("number of keep_open_threads = #{num_open_threads}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ((metadata['rpc-behavior'] == 'keep-open') or 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         (metadata['rpc-behavior'].start_with?('sleep'))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         keep_open_threads << execute_rpc_in_thread(op, rpc_stats_key) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         results[rpc] = execute_rpc(op, fail_on_failed_rpcs, rpc_stats_key) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -364,6 +398,7 @@ def main 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $num_rpcs_started_by_method[rpc.to_s] = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $num_rpcs_succeeded_by_method[rpc.to_s] = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $num_rpcs_failed_by_method[rpc.to_s] = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    $accumulated_method_stats[rpc.to_s] = StatsPerMethod.new 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   end 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   # The client just sends rpcs continuously in a regular interval 
			 |