@@ -1268,23 +1268,27 @@ <h1 class="title">Module <code>slack_bolt.app.async_app</code></h1>
12681268 def _init_context(self, req: AsyncBoltRequest):
12691269 req.context["logger"] = get_bolt_app_logger(app_name=self.name, base_logger=self._base_logger)
12701270 req.context["token"] = self._token
1271- if self._token is not None:
1272- # This AsyncWebClient instance can be safely singleton
1273- req.context["client"] = self._async_client
1274- else:
1275- # Set a new dedicated instance for this request
1276- client_per_request: AsyncWebClient = AsyncWebClient(
1277- token=None, # the token will be set later
1278- base_url=self._async_client.base_url,
1279- timeout=self._async_client.timeout,
1280- ssl=self._async_client.ssl,
1281- proxy=self._async_client.proxy,
1282- session=self._async_client.session,
1283- trust_env_in_session=self._async_client.trust_env_in_session,
1284- headers=self._async_client.headers,
1285- team_id=req.context.team_id,
1286- )
1287- req.context["client"] = client_per_request
1271+ # Prior to version 1.15, when the token is static, self._client was passed to `req.context`.
1272+ # The intention was to avoid creating a new instance per request
1273+ # in the interest of runtime performance/memory footprint optimization.
1274+ # However, developers may want to replace the token held by req.context.client in some situations.
1275+ # In this case, this behavior can result in thread-unsafe data modification on `self._client`.
1276+ # (`self._client` a.k.a. `app.client` is a singleton object per an App instance)
1277+ # Thus, we've changed the behavior to create a new instance per request regardless of token argument
1278+ # in the App initialization starting v1.15.
1279+ # The overhead brought by this change is slight so that we believe that it is ignorable in any cases.
1280+ client_per_request: AsyncWebClient = AsyncWebClient(
1281+ token=self._token, # this can be None, and it can be set later on
1282+ base_url=self._async_client.base_url,
1283+ timeout=self._async_client.timeout,
1284+ ssl=self._async_client.ssl,
1285+ proxy=self._async_client.proxy,
1286+ session=self._async_client.session,
1287+ trust_env_in_session=self._async_client.trust_env_in_session,
1288+ headers=self._async_client.headers,
1289+ team_id=req.context.team_id,
1290+ )
1291+ req.context["client"] = client_per_request
12881292
12891293 @staticmethod
12901294 def _to_listener_functions(
@@ -2583,23 +2587,27 @@ <h2 id="args">Args</h2>
25832587 def _init_context(self, req: AsyncBoltRequest):
25842588 req.context["logger"] = get_bolt_app_logger(app_name=self.name, base_logger=self._base_logger)
25852589 req.context["token"] = self._token
2586- if self._token is not None:
2587- # This AsyncWebClient instance can be safely singleton
2588- req.context["client"] = self._async_client
2589- else:
2590- # Set a new dedicated instance for this request
2591- client_per_request: AsyncWebClient = AsyncWebClient(
2592- token=None, # the token will be set later
2593- base_url=self._async_client.base_url,
2594- timeout=self._async_client.timeout,
2595- ssl=self._async_client.ssl,
2596- proxy=self._async_client.proxy,
2597- session=self._async_client.session,
2598- trust_env_in_session=self._async_client.trust_env_in_session,
2599- headers=self._async_client.headers,
2600- team_id=req.context.team_id,
2601- )
2602- req.context["client"] = client_per_request
2590+ # Prior to version 1.15, when the token is static, self._client was passed to `req.context`.
2591+ # The intention was to avoid creating a new instance per request
2592+ # in the interest of runtime performance/memory footprint optimization.
2593+ # However, developers may want to replace the token held by req.context.client in some situations.
2594+ # In this case, this behavior can result in thread-unsafe data modification on `self._client`.
2595+ # (`self._client` a.k.a. `app.client` is a singleton object per an App instance)
2596+ # Thus, we've changed the behavior to create a new instance per request regardless of token argument
2597+ # in the App initialization starting v1.15.
2598+ # The overhead brought by this change is slight so that we believe that it is ignorable in any cases.
2599+ client_per_request: AsyncWebClient = AsyncWebClient(
2600+ token=self._token, # this can be None, and it can be set later on
2601+ base_url=self._async_client.base_url,
2602+ timeout=self._async_client.timeout,
2603+ ssl=self._async_client.ssl,
2604+ proxy=self._async_client.proxy,
2605+ session=self._async_client.session,
2606+ trust_env_in_session=self._async_client.trust_env_in_session,
2607+ headers=self._async_client.headers,
2608+ team_id=req.context.team_id,
2609+ )
2610+ req.context["client"] = client_per_request
26032611
26042612 @staticmethod
26052613 def _to_listener_functions(
0 commit comments