16
16
import threading
17
17
from asyncio import BaseEventLoop
18
18
from logging import LogRecord
19
- from typing import Optional
19
+ from typing import Optional , List
20
20
21
21
import grpc
22
22
@@ -79,8 +79,8 @@ def __init__(self, loop: BaseEventLoop, host: str, port: int,
79
79
# PYTHON_THREADPOOL_THREAD_COUNT app setting. The default value is 1.
80
80
self ._sync_tp_max_workers : int = self ._get_sync_tp_max_workers ()
81
81
self ._sync_call_tp : concurrent .futures .Executor = (
82
- concurrent . futures . ThreadPoolExecutor (
83
- max_workers = self . _sync_tp_max_workers ) )
82
+ self . _create_sync_call_tp ( self . _sync_tp_max_workers )
83
+ )
84
84
85
85
self ._grpc_connect_timeout : float = grpc_connect_timeout
86
86
# This is set to -1 by default to remove the limitation on msg size
@@ -97,9 +97,7 @@ async def connect(cls, host: str, port: int, worker_id: str,
97
97
disp = cls (loop , host , port , worker_id , request_id , connect_timeout )
98
98
disp ._grpc_thread .start ()
99
99
await disp ._grpc_connected_fut
100
- logger .info ('Successfully opened gRPC channel to %s:%s '
101
- 'with sync threadpool max workers set to %s' ,
102
- host , port , disp ._sync_tp_max_workers )
100
+ logger .info ('Successfully opened gRPC channel to %s:%s ' , host , port )
103
101
return disp
104
102
105
103
async def dispatch_forever (self ):
@@ -161,9 +159,7 @@ def stop(self) -> None:
161
159
self ._grpc_thread .join ()
162
160
self ._grpc_thread = None
163
161
164
- if self ._sync_call_tp is not None :
165
- self ._sync_call_tp .shutdown ()
166
- self ._sync_call_tp = None
162
+ self ._stop_sync_call_tp ()
167
163
168
164
def on_logging (self , record : logging .LogRecord , formatted_msg : str ) -> None :
169
165
if record .levelno >= logging .CRITICAL :
@@ -318,11 +314,19 @@ async def _handle__invocation_request(self, req):
318
314
fi : functions .FunctionInfo = self ._functions .get_function (
319
315
function_id )
320
316
321
- logger .info (f'Received FunctionInvocationRequest, '
322
- f'request ID: { self .request_id } , '
323
- f'function ID: { function_id } , '
324
- f'invocation ID: { invocation_id } , '
325
- f'function type: { "async" if fi .is_async else "sync" } ' )
317
+ function_invocation_logs : List [str ] = [
318
+ 'Received FunctionInvocationRequest' ,
319
+ f'request ID: { self .request_id } ' ,
320
+ f'function ID: { function_id } ' ,
321
+ f'invocation ID: { invocation_id } ' ,
322
+ f'function type: { "async" if fi .is_async else "sync" } '
323
+ ]
324
+ if not fi .is_async :
325
+ function_invocation_logs .append (
326
+ f'sync threadpool max workers: { self ._sync_tp_max_workers } '
327
+ )
328
+ logger .info (', ' .join (function_invocation_logs ))
329
+
326
330
args = {}
327
331
for pb in invoc_request .input_data :
328
332
pb_type_info = fi .input_types [pb .name ]
@@ -426,6 +430,13 @@ async def _handle__function_environment_reload_request(self, req):
426
430
for var in env_vars :
427
431
os .environ [var ] = env_vars [var ]
428
432
433
+ # Apply PYTHON_THREADPOOL_THREAD_COUNT
434
+ self ._stop_sync_call_tp ()
435
+ self ._sync_tp_max_workers = self ._get_sync_tp_max_workers ()
436
+ self ._sync_call_tp = (
437
+ self ._create_sync_call_tp (self ._sync_tp_max_workers )
438
+ )
439
+
429
440
# Reload package namespaces for customer's libraries
430
441
packages_to_reload = ['azure' , 'google' ]
431
442
for p in packages_to_reload :
@@ -479,6 +490,15 @@ def _change_cwd(self, new_cwd: str):
479
490
else :
480
491
logger .warning ('Directory %s is not found when reloading' , new_cwd )
481
492
493
+ def _stop_sync_call_tp (self ):
494
+ """Deallocate the current synchronous thread pool and assign
495
+ self._sync_call_tp to None. If the thread pool does not exist,
496
+ this will be a no op.
497
+ """
498
+ if getattr (self , '_sync_call_tp' , None ):
499
+ self ._sync_call_tp .shutdown ()
500
+ self ._sync_call_tp = None
501
+
482
502
def _get_sync_tp_max_workers (self ) -> int :
483
503
def tp_max_workers_validator (value : str ) -> bool :
484
504
try :
@@ -501,6 +521,17 @@ def tp_max_workers_validator(value: str) -> bool:
501
521
default_value = f'{ PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT } ' ,
502
522
validator = tp_max_workers_validator ))
503
523
524
+ def _create_sync_call_tp (
525
+ self , max_worker : int ) -> concurrent .futures .Executor :
526
+ """Create a thread pool executor with max_worker. This is a wrapper
527
+ over ThreadPoolExecutor constructor. Consider calling this method after
528
+ _stop_sync_call_tp() to ensure only 1 synchronous thread pool is
529
+ running.
530
+ """
531
+ return concurrent .futures .ThreadPoolExecutor (
532
+ max_workers = max_worker
533
+ )
534
+
504
535
def __run_sync_func (self , invocation_id , func , params ):
505
536
# This helper exists because we need to access the current
506
537
# invocation_id from ThreadPoolExecutor's threads.
0 commit comments