Repository navigation
Expand file tree
/
Copy pathstress_greeter.py
More file actions
158 lines (125 loc) · 5.13 KB
/
Copy pathstress_greeter.py
File metadata and controls
158 lines (125 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Tencent is pleased to support the open source community by making tRPC-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Python is licensed under Apache-2.0.
"""Stream handlers for the hellostream stress client.
Selected by ``HELLOSTREAM_STRESS=1`` in ``trpc_main``. Replies carry no
per-frame log line. Each reply is exactly ``pid=<pid> tid=<tid> n=<n>`` so
the client can check order and which process and thread ran the handler.
``n`` is the request index echoed from ``SayHelloAll`` and ``SayHelloRsp``,
and the number of data frames received for ``SayHelloReq``.
"""
from __future__ import annotations
import os
import re
import sys
import threading
from trpc_sdk.abc import TrpcContext
from trpc_sdk.log import logger
_STUB = os.path.join(os.path.dirname(__file__), "stub")
if _STUB not in sys.path:
sys.path.insert(0, _STUB)
from trpc_py_stream_helloworld import pb # noqa: E402 # pyright: ignore[reportMissingImports]
from trpc_py_stream_helloworld import rpc # noqa: E402 # pyright: ignore[reportMissingImports]
from trpc_py_stream_helloworld import rpc_stream # noqa: E402 # pyright: ignore[reportMissingImports]
# Leading ``n=<int>`` on a bidi request. The rest of the name is padding.
_N_RE = re.compile(r"^n=(\d+)")
# Leading ``count=<int>`` on a server-stream request.
_COUNT_RE = re.compile(r"^count=(\d+)")
def _stamp(n: int) -> str:
"""Return the reply body for one frame.
Args:
n: Sequence index, or a frame count for the client-stream close.
Returns:
``pid=<pid> tid=<tid> n=<n>`` for the calling thread.
"""
return f"pid={os.getpid()} tid={threading.get_ident()} n={n}"
def _request_n(name: str) -> int:
"""Read the ``n=<int>`` prefix of a request name.
Args:
name: ``HelloRequest.name`` sent by the stress client.
Returns:
The parsed index, or ``-1`` when the prefix is missing.
"""
match = _N_RE.match(name)
if match is None:
return -1
return int(match.group(1))
def _reply_count(name: str) -> int:
"""Read how many server-stream replies to send.
Args:
name: ``HelloRequest.name``, expected to start with ``count=<int>``.
Returns:
The parsed count, or ``0`` when the prefix is missing.
"""
match = _COUNT_RE.match(name)
if match is None:
return 0
return int(match.group(1))
class StressGreeter(rpc.GreeterServicer):
"""Echo stream handlers with no sleep and no per-frame logging."""
# pylint: disable=invalid-name
async def SayHello(self, ctx: TrpcContext, request: pb.HelloRequest) -> pb.HelloReply:
"""Echo ``request.name`` for a unary call on the stress process.
A stress process still registers this method, so a stray unary call
does not fall through to ``NotImplementedError``.
Args:
ctx: Server context. The reply does not read it.
request: Unary request.
Returns:
A reply whose ``message`` is ``request.name``.
"""
del ctx
reply = pb.HelloReply()
reply.message = request.name
return reply
# pylint: disable=invalid-name
async def SayHelloReq(self, stream: rpc_stream.GreeterServerStream.SayHelloReq) -> None:
"""Count data frames and close with that count.
Args:
stream: Client-streaming call. ``recv`` raises ``EOFError`` when
the client has closed its sends.
"""
count = 0
while True:
try:
await stream.recv()
except EOFError:
reply = pb.HelloReply()
reply.message = _stamp(count)
await stream.send_and_close(reply)
return
except Exception as err: # pylint: disable=broad-except
logger.error("stress SayHelloReq failed: %s", err)
return
count += 1
# pylint: disable=invalid-name
async def SayHelloRsp(self, request: pb.HelloRequest, stream: rpc_stream.GreeterServerStream.SayHelloRsp) -> None:
"""Send ``count=<n>`` replies immediately.
Args:
request: Opening request. ``name`` starts with ``count=<n>``.
stream: Server-streaming call.
"""
for index in range(_reply_count(request.name)):
reply = pb.HelloReply()
reply.message = _stamp(index)
await stream.send(reply)
# pylint: disable=invalid-name
async def SayHelloAll(self, stream: rpc_stream.GreeterServerStream.SayHelloAll) -> None:
"""Echo each data frame with this process and thread stamped on it.
Args:
stream: Bidi call. ``recv`` raises ``EOFError`` when the client
has closed its sends.
"""
while True:
try:
request = await stream.recv()
except EOFError:
return
except Exception as err: # pylint: disable=broad-except
logger.error("stress SayHelloAll failed: %s", err)
return
reply = pb.HelloReply()
reply.message = _stamp(_request_n(request.name))
await stream.send(reply)