From 337f89f25a1e8433d973e025225e7ee77f5e68c6 Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Thu, 28 Dec 2023 13:44:21 +0530 Subject: [PATCH] feat: use grpc within locust --- bench/adaptor.py | 64 ++ bench/locustfile.py | 184 ++++-- .../json/exceptions_1703750739.8121767.csv | 1 + .../json/failures_1703750736.2690804.csv | 1 + .../json/report_1703750744.1268215.html | 592 ++++++++++++++++++ .../json/requests_1703750722.6138687.csv | 3 + test.py | 3 +- 7 files changed, 784 insertions(+), 64 deletions(-) create mode 100644 bench/adaptor.py create mode 100644 bench/results/v2/grpc-no-conn-pool-single/json/exceptions_1703750739.8121767.csv create mode 100644 bench/results/v2/grpc-no-conn-pool-single/json/failures_1703750736.2690804.csv create mode 100644 bench/results/v2/grpc-no-conn-pool-single/json/report_1703750744.1268215.html create mode 100644 bench/results/v2/grpc-no-conn-pool-single/json/requests_1703750722.6138687.csv diff --git a/bench/adaptor.py b/bench/adaptor.py new file mode 100644 index 0000000..78b4b5c --- /dev/null +++ b/bench/adaptor.py @@ -0,0 +1,64 @@ +import time +from typing import Any, Callable +import grpc +import grpc.experimental.gevent as grpc_gevent +from grpc_interceptor import ClientInterceptor +from locust import User +from locust.exception import LocustError + +# patch grpc so that it uses gevent instead of asyncio +grpc_gevent.init_gevent() + + +class LocustInterceptor(ClientInterceptor): + def __init__(self, environment, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.env = environment + + def intercept( + self, + method: Callable, + request_or_iterator: Any, + call_details: grpc.ClientCallDetails, + ): + response = None + exception = None + start_perf_counter = time.perf_counter() + response_length = 0 + try: + response = method(request_or_iterator, call_details) + response_length = response.result().ByteSize() + except grpc.RpcError as e: + exception = e + + self.env.events.request.fire( + request_type="grpc", + name=call_details.method, + response_time=(time.perf_counter() - start_perf_counter) * 1000, + response_length=response_length, + response=response, + context=None, + exception=exception, + ) + return response + + +class GrpcUser(User): + abstract = True + stub_class = None + + def __init__(self, environment): + super().__init__(environment) + for attr_value, attr_name in ( + (self.host, "host"), + (self.stub_class, "stub_class"), + ): + if attr_value is None: + raise LocustError(f"You must specify the {attr_name}.") + + self._channel = grpc.insecure_channel(self.host) + interceptor = LocustInterceptor(environment=environment) + self._channel = grpc.intercept_channel(self._channel, interceptor) + + self.stub = self.stub_class(self._channel) diff --git a/bench/locustfile.py b/bench/locustfile.py index 1645bb0..b12524d 100644 --- a/bench/locustfile.py +++ b/bench/locustfile.py @@ -1,66 +1,125 @@ +import json from pprint import pprint from locust import FastHttpUser, between, task -password = "fooobarasdfasdf" -username = "realaravinth" +from dcache_py import dcache_pb2 as dcache +from dcache_py.dcache_pb2 import RaftRequest +from dcache_py.dcache_pb2_grpc import DcacheServiceStub - -class Unprotected(FastHttpUser): - # wait_time = between(5, 15) - peers = [ - "http://localhost:9001", - "http://localhost:9002", - "http://localhost:9003", - ] - leader = "http://localhost:9001" - host = leader - captcha_id="locust" - - pipeline_vote = [] - for _ in range(0,1000): - pipeline_vote.append({"AddVisitor": captcha_id}) - -# def on_start(self): -# resp = self.client.get(f"{self.leader}/metrics") -# data = resp.json() -# leader = data["Ok"]["membership_config"]["log_id"]["leader_id"]["node_id"] -# self.leader = self.peers[leader - 1] -# self.host = self.leader -# print(f"Leader: {self.host}") -# self.add_captcha(captcha_id="locust") - - - - def write(self, data): - resp = self.client.post(f"{self.host}/write", json=data) - print(f"RPC Status: {resp.status_code}") - resp = resp.json() - if "Err" in resp: - leader = resp["Err"]["APIError"]["ForwardToLeader"]["leader_node"]["addr"] - print(f"Forwarding write to leader {leader}") - return write(leader, data) - return resp["Ok"]["data"] - - def pipeline_write(self, data): - resp = self.client.post(f"{self.host}/pipeline/write", json=data) +# password = "fooobarasdfasdf" +# username = "realaravinth" +# +# +# class Unprotected(FastHttpUser): +# # wait_time = between(5, 15) +# peers = [ +# "http://localhost:9001", +# "http://localhost:9002", +# "http://localhost:9003", +# ] +# leader = "http://localhost:9001" +# host = leader +# captcha_id="locust" +# +# pipeline_vote = [] +# for _ in range(0,1000): +# pipeline_vote.append({"AddVisitor": captcha_id}) +# +## def on_start(self): +## resp = self.client.get(f"{self.leader}/metrics") +## data = resp.json() +## leader = data["Ok"]["membership_config"]["log_id"]["leader_id"]["node_id"] +## self.leader = self.peers[leader - 1] +## self.host = self.leader +## print(f"Leader: {self.host}") +## self.add_captcha(captcha_id="locust") +# +# +# +# def write(self, data): +# resp = self.client.post(f"{self.host}/write", json=data) # print(f"RPC Status: {resp.status_code}") - resp = resp.json() - if "Err" in resp: - leader = resp["Err"]["APIError"]["ForwardToLeader"]["leader_node"]["addr"] - print(f"Forwarding write to leader {leader}") - return write(leader, data) - return resp +# resp = resp.json() +# if "Err" in resp: +# leader = resp["Err"]["APIError"]["ForwardToLeader"]["leader_node"]["addr"] +# print(f"Forwarding write to leader {leader}") +# return write(leader, data) +# return resp["Ok"]["data"] +# +# def pipeline_write(self, data): +# resp = self.client.post(f"{self.host}/pipeline/write", json=data) +## print(f"RPC Status: {resp.status_code}") +# resp = resp.json() +# if "Err" in resp: +# leader = resp["Err"]["APIError"]["ForwardToLeader"]["leader_node"]["addr"] +# print(f"Forwarding write to leader {leader}") +# return write(leader, data) +# return resp +# +# +# +# +# def add_vote(self, captcha_id: str): +# resp = self.write(data={"AddVisitor": captcha_id}) +# pprint(resp) +# +# def add_vote_pipeline(self, captcha_id: str): +# resp = self.pipeline_write(data=self.pipeline_vote) +## pprint(resp) +# +# def add_captcha(self, captcha_id: str): +# params = { +# "AddCaptcha": { +# "id": captcha_id, +# "mcaptcha": { +# "visitor_threshold": 0, +# "defense": { +# "levels": [ +# {"visitor_threshold": 50, "difficulty_factor": 500}, +# {"visitor_threshold": 5000, "difficulty_factor": 50000}, +# ], +# "current_visitor_threshold": 0, +# }, +# "duration": 30, +# }, +# } +# } +# resp = self.write(data=params) +# pprint(f"Captcha added {captcha_id}: {resp}") +# +# +# @task +# def unprotected(self): +# self.add_vote_pipeline(captcha_id=self.captcha_id) +# ##self.add_vote(captcha_id="locust") +## data = { +## "username": username, +## "password": username, +## "confirm_password": username, +## } +## self.client.post("/unprotected", data=data) +# +import gevent +import adaptor +from locust import events, task +class HelloGrpcUser(adaptor.GrpcUser): + stub_class = DcacheServiceStub + leader = "localhost:9001" + host = leader + captcha_id = "locust" + msg = RaftRequest(data=json.dumps({"AddVisitor": captcha_id})) + def on_start(self): + self.add_captcha(captcha_id=self.captcha_id) def add_vote(self, captcha_id: str): - resp = self.write(data={"AddVisitor": captcha_id}) - pprint(resp) + resp = self.stub.Write(self.msg) - def add_vote_pipeline(self, captcha_id: str): - resp = self.pipeline_write(data=self.pipeline_vote) -# pprint(resp) + # def add_vote_pipeline(self, captcha_id: str): + # resp = self.pipeline_write(data=self.pipeline_vote) + # pprint(resp) def add_captcha(self, captcha_id: str): params = { @@ -79,17 +138,16 @@ class Unprotected(FastHttpUser): }, } } - resp = self.write(data=params) + + msg = RaftRequest(data=json.dumps(params)) + resp = self.stub.Write(msg) pprint(f"Captcha added {captcha_id}: {resp}") - @task - def unprotected(self): - self.add_vote_pipeline(captcha_id=self.captcha_id) - ##self.add_vote(captcha_id="locust") -# data = { -# "username": username, -# "password": username, -# "confirm_password": username, -# } -# self.client.post("/unprotected", data=data) + def addVote(self): + self.add_vote(self.captcha_id) + + +# msg = dcache.CaptchaID(id=captcha_id) +# resp = self.stub.AddVisitor(msg) +# pprint(resp) diff --git a/bench/results/v2/grpc-no-conn-pool-single/json/exceptions_1703750739.8121767.csv b/bench/results/v2/grpc-no-conn-pool-single/json/exceptions_1703750739.8121767.csv new file mode 100644 index 0000000..5e0e870 --- /dev/null +++ b/bench/results/v2/grpc-no-conn-pool-single/json/exceptions_1703750739.8121767.csv @@ -0,0 +1 @@ +Count,Message,Traceback,Nodes diff --git a/bench/results/v2/grpc-no-conn-pool-single/json/failures_1703750736.2690804.csv b/bench/results/v2/grpc-no-conn-pool-single/json/failures_1703750736.2690804.csv new file mode 100644 index 0000000..f87ff75 --- /dev/null +++ b/bench/results/v2/grpc-no-conn-pool-single/json/failures_1703750736.2690804.csv @@ -0,0 +1 @@ +Method,Name,Error,Occurrences diff --git a/bench/results/v2/grpc-no-conn-pool-single/json/report_1703750744.1268215.html b/bench/results/v2/grpc-no-conn-pool-single/json/report_1703750744.1268215.html new file mode 100644 index 0000000..bddb3a0 --- /dev/null +++ b/bench/results/v2/grpc-no-conn-pool-single/json/report_1703750744.1268215.html @@ -0,0 +1,592 @@ + + + + Test Report for locustfile.py + + + + +
+

Locust Test Report

+ +
+ +

During: 2023-12-28 08:02:42 - 2023-12-28 08:04:54

+

Target Host: localhost:9001

+

Script: locustfile.py

+
+ +
+

Request Statistics

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodName# Requests# FailsAverage (ms)Min (ms)Max (ms)Average size (bytes)RPSFailures/s
grpc/dcache.DcacheService/Write96465053032860130732.30.0
Aggregated96465053032860130732.30.0
+
+ +
+

Response Time Statistics

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodName50%ile (ms)60%ile (ms)70%ile (ms)80%ile (ms)90%ile (ms)95%ile (ms)99%ile (ms)100%ile (ms)
grpc/dcache.DcacheService/Write6006707708801100120015002900
Aggregated6006707708801100120015002900
+
+ + + + + + +
+

Charts

+
+ + +
+

Final ratio

+
+
+ +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/bench/results/v2/grpc-no-conn-pool-single/json/requests_1703750722.6138687.csv b/bench/results/v2/grpc-no-conn-pool-single/json/requests_1703750722.6138687.csv new file mode 100644 index 0000000..85ebcbd --- /dev/null +++ b/bench/results/v2/grpc-no-conn-pool-single/json/requests_1703750722.6138687.csv @@ -0,0 +1,3 @@ +Type,Name,Request Count,Failure Count,Median Response Time,Average Response Time,Min Response Time,Max Response Time,Average Content Size,Requests/s,Failures/s,50%,66%,75%,80%,90%,95%,98%,99%,99.9%,99.99%,100% +grpc,/dcache.DcacheService/Write,96465,0,600.0,530.5241670541676,3.931416000114041,2860.153126999876,130.11822940963043,732.274667601832,0.0,600,720,830,880,1100,1200,1300,1500,2300,2900,2900 +,Aggregated,96465,0,600.0,530.5241670541676,3.931416000114041,2860.153126999876,130.11822940963043,732.274667601832,0.0,600,720,830,880,1100,1200,1300,1500,2300,2900,2900 diff --git a/test.py b/test.py index be23e9d..3888220 100755 --- a/test.py +++ b/test.py @@ -135,7 +135,8 @@ def grpc_add_captcha(stub: DcacheServiceStub, captcha_id: str): def grpc_run(): with grpc.insecure_channel(host) as channel: stub = DcacheServiceStub(channel) - grpc_add_captcha(stub, captcha_id) +# grpc_add_captcha(stub, captcha_id) + grpc_add_vote(stub, captcha_id) if __name__ == "__main__": grpc_run()