11"""
22Pressure Test for the OpenAI TTS API Server
33
4- This script sends concurrent requests to the API server to test its performance under load .
4+ This script continuously sends requests to the API server until it encounters a failure .
55"""
66
77import asyncio
@@ -112,8 +112,16 @@ async def send_request(session, url, voice, text_length, request_num, save_dir=N
112112 "response_size" : 0
113113 }
114114
115- async def run_pressure_test (num_requests , concurrency , server_url , text_length = "medium" , save_audio = False ):
116- """Run the pressure test with the specified parameters."""
115+ async def run_test (server_url , text_length = "medium" , save_audio = False , num_requests = None , concurrency = 1 ):
116+ """Run the test with specified parameters.
117+
118+ Args:
119+ server_url: URL of the TTS server
120+ text_length: Length of test text (short, medium, long)
121+ save_audio: Whether to save audio files
122+ num_requests: Number of requests to send (None for continuous until failure)
123+ concurrency: Number of concurrent requests to send
124+ """
117125
118126 if not server_url .endswith ('/v1/audio/speech' ):
119127 server_url = f"{ server_url .rstrip ('/' )} /v1/audio/speech"
@@ -123,10 +131,15 @@ async def run_pressure_test(num_requests, concurrency, server_url, text_length="
123131 if save_audio :
124132 save_dir = Path ('test_output' )
125133 save_dir .mkdir (exist_ok = True )
126-
127- print (f"Starting pressure test with { num_requests } total requests, { concurrency } concurrent connections" )
134+
135+ mode = "fixed" if num_requests else "continuous"
136+
137+ print (f"Starting { mode } test with { concurrency } concurrent requests" )
128138 print (f"Server URL: { server_url } " )
129139 print (f"Text length: { text_length } " )
140+ if num_requests :
141+ print (f"Number of requests: { num_requests } " )
142+ print (f"Concurrency level: { concurrency } " )
130143 if save_audio :
131144 print (f"Saving audio files to: { save_dir } " )
132145 print ("-" * 60 )
@@ -135,40 +148,62 @@ async def run_pressure_test(num_requests, concurrency, server_url, text_length="
135148 start_time = time .time ()
136149
137150 async with aiohttp .ClientSession () as session :
138- tasks = []
139- for i in range (num_requests ):
140- # Cycle through different voices
141- voice = VOICES [i % len (VOICES )]
142-
143- # Add a small delay between creating tasks to avoid overwhelming the server at startup
144- await asyncio .sleep (0.05 )
151+ if num_requests :
152+ # Fixed number of requests mode
153+ request_nums = list (range (1 , num_requests + 1 ))
145154
146- task = asyncio .create_task (send_request (session , server_url , voice , text_length , i + 1 , save_dir ))
147- tasks .append (task )
155+ # Process requests in batches according to concurrency
156+ for i in range (0 , len (request_nums ), concurrency ):
157+ batch = request_nums [i :i + concurrency ]
158+ tasks = []
159+
160+ for req_num in batch :
161+ voice = VOICES [(req_num - 1 ) % len (VOICES )]
162+ tasks .append (send_request (session , server_url , voice , text_length , req_num , save_dir ))
163+
164+ batch_results = await asyncio .gather (* tasks )
165+ results .extend (batch_results )
166+
167+ # Check if any request in the batch failed
168+ if any (not r ["success" ] for r in batch_results ):
169+ print ("Stopping test due to request failure" )
170+ break
171+ else :
172+ # Continuous mode (until failure)
173+ request_num = 1
174+ failed = False
148175
149- # If we've reached the concurrency limit, wait for some tasks to complete
150- if len (tasks ) >= concurrency :
151- done , pending = await asyncio .wait (tasks , return_when = asyncio .FIRST_COMPLETED )
152- results .extend ([task .result () for task in done ])
153- tasks = list (pending ) # Convert the pending set back to a list
154-
155- # Wait for any remaining tasks
156- if tasks :
157- done , _ = await asyncio .wait (tasks )
158- results .extend ([task .result () for task in done ])
176+ while not failed :
177+ tasks = []
178+
179+ for i in range (concurrency ):
180+ curr_req_num = request_num + i
181+ voice = VOICES [(curr_req_num - 1 ) % len (VOICES )]
182+ tasks .append (send_request (session , server_url , voice , text_length , curr_req_num , save_dir ))
183+
184+ batch_results = await asyncio .gather (* tasks )
185+ results .extend (batch_results )
186+
187+ # Check if any request in the batch failed
188+ if any (not r ["success" ] for r in batch_results ):
189+ failed = True
190+ print ("Stopping test due to request failure" )
191+ break
192+
193+ request_num += concurrency
159194
160195 end_time = time .time ()
161196 total_duration = end_time - start_time
162197
163198 print ("\n " + "=" * 60 )
164- print (f"Pressure Test Results ({ text_length } text)" )
199+ print (f"Test Results ({ text_length } text, { 'continuous' if not num_requests else num_requests } requests, { concurrency } concurrency )" )
165200 print ("=" * 60 )
166201
167202 # Calculate and display statistics
168203 successful_reqs = [r for r in results if r ["success" ]]
169204 failed_reqs = [r for r in results if not r ["success" ]]
170205
171- success_rate = len (successful_reqs ) / num_requests * 100 if num_requests > 0 else 0
206+ success_rate = len (successful_reqs ) / len ( results ) * 100 if results else 0
172207
173208 # Calculate response time statistics
174209 if successful_reqs :
@@ -183,46 +218,51 @@ async def run_pressure_test(num_requests, concurrency, server_url, text_length="
183218 avg_size = statistics .mean (sizes ) / 1024 # KB
184219 total_size = sum (sizes ) / (1024 * 1024 ) # MB
185220
186- print (f"Success Rate: { success_rate :.1f} % ({ len (successful_reqs )} /{ num_requests } )" )
221+ print (f"Total Successful Requests: { len (successful_reqs )} " )
222+ print (f"Success Rate: { success_rate :.1f} %" )
187223 print (f"Total Test Duration: { total_duration :.2f} seconds" )
188224 print (f"Average Response Time: { avg_duration :.3f} seconds" )
189225 print (f"Median Response Time: { median_duration :.3f} seconds" )
190226 print (f"Min Response Time: { min_duration :.3f} seconds" )
191227 print (f"Max Response Time: { max_duration :.3f} seconds" )
192228 print (f"Average Response Size: { avg_size :.1f} KB" )
193229 print (f"Total Data Transferred: { total_size :.2f} MB" )
194- print (f"Requests per Second: { num_requests / total_duration :.2f} " )
230+ print (f"Requests per Second: { len ( results ) / total_duration :.2f} " )
195231 print (f"Throughput: { (total_size * 8 ) / total_duration :.2f} Mbps" )
196232 else :
197- print (f"Success Rate: 0% (0/ { num_requests } ) " )
233+ print (f"Success Rate: 0%" )
198234 print (f"Total Test Duration: { total_duration :.2f} seconds" )
199235
200- # Show error breakdown if there are failures
236+ # Show error details for the failures
201237 if failed_reqs :
202- print ("\n Error Breakdown:" )
203- error_counts = {}
204- for req in failed_reqs :
205- error = req .get ("error" , f"HTTP { req ['status' ]} " )
206- error_counts [error ] = error_counts .get (error , 0 ) + 1
238+ print ("\n Failure Details:" )
239+ for i , failed_req in enumerate (failed_reqs [:5 ]): # Show up to 5 failures
240+ error = failed_req .get ("error" , f"HTTP { failed_req ['status' ]} " )
241+ print (f" Request Number: { failed_req ['request_num' ]} " )
242+ print (f" Voice: { failed_req ['voice' ]} " )
243+ print (f" Error: { error } " )
244+ print (f" Duration: { failed_req ['duration' ]:.2f} seconds" )
245+ if i < len (failed_reqs ) - 1 and i < 4 : # Add separator except after the last one
246+ print (" ---" )
207247
208- for error , count in error_counts . items () :
209- print (f" { error } : { count } occurrences " )
248+ if len ( failed_reqs ) > 5 :
249+ print (f" ... and { len ( failed_reqs ) - 5 } more failures " )
210250
211251if __name__ == "__main__" :
212252 parser = argparse .ArgumentParser (description = "Pressure test for OpenAI TTS API Server" )
213- parser .add_argument ("-n" , "--num-requests" , type = int , default = 10 , help = "Total number of requests to send" )
214- parser .add_argument ("-c" , "--concurrency" , type = int , default = 2 , help = "Number of concurrent connections" )
215253 parser .add_argument ("-u" , "--url" , type = str , default = "http://localhost:7000" , help = "Server URL" )
216254 parser .add_argument ("-t" , "--text-length" , type = str , choices = ["short" , "medium" , "long" ], default = "medium" ,
217255 help = "Length of text to use for testing" )
218256 parser .add_argument ("-s" , "--save-audio" , action = "store_true" , help = "Save audio files to test_output directory" )
257+ parser .add_argument ("-n" , "--num-requests" , type = int , help = "Number of requests to send (default: continuous until failure)" )
258+ parser .add_argument ("-c" , "--concurrency" , type = int , default = 1 , help = "Number of concurrent requests (default: 1)" )
219259
220260 args = parser .parse_args ()
221261
222- asyncio .run (run_pressure_test (
223- args .num_requests ,
224- args .concurrency ,
262+ asyncio .run (run_test (
225263 args .url ,
226264 args .text_length ,
227- args .save_audio
265+ args .save_audio ,
266+ args .num_requests ,
267+ args .concurrency
228268 ))
0 commit comments