更新
This commit is contained in:
@@ -396,6 +396,15 @@ class TrafficController:
|
||||
self._background = asyncio.Semaphore(
|
||||
_env_int("KEFU_BACKGROUND_NETWORK_CONCURRENCY", 2)
|
||||
)
|
||||
# Recurring polls can create hundreds of waiters when many accounts
|
||||
# are online. Admit at most one normal waiter to the semaphore at a
|
||||
# time so startup validation can join near the front instead of being
|
||||
# buried behind the entire polling backlog. The shared semaphore is
|
||||
# still the single bandwidth cap; startup work does not add extra
|
||||
# network concurrency.
|
||||
self._background_normal_admission = asyncio.Lock()
|
||||
self._background_startup_clear = asyncio.Event()
|
||||
self._background_startup_clear.set()
|
||||
self._browser = asyncio.Semaphore(
|
||||
_env_int("KEFU_BROWSER_START_CONCURRENCY", 1)
|
||||
)
|
||||
@@ -410,12 +419,20 @@ class TrafficController:
|
||||
)
|
||||
self.background_waiting = 0
|
||||
self.background_active = 0
|
||||
self.background_startup_waiting = 0
|
||||
self.background_startup_active = 0
|
||||
self.browser_waiting = 0
|
||||
self.browser_active = 0
|
||||
self.media_proxy_active = 0
|
||||
|
||||
@asynccontextmanager
|
||||
async def background_slot(self, account_id: int = 0, description: str = "request"):
|
||||
async def background_slot(
|
||||
self,
|
||||
account_id: int = 0,
|
||||
description: str = "request",
|
||||
*,
|
||||
startup: bool = False,
|
||||
):
|
||||
current_task = asyncio.current_task()
|
||||
owner_task, depth = self._background_owner.get()
|
||||
if owner_task is current_task and depth > 0:
|
||||
@@ -429,12 +446,29 @@ class TrafficController:
|
||||
started = asyncio.get_running_loop().time()
|
||||
self.background_waiting += 1
|
||||
try:
|
||||
await self._background.acquire()
|
||||
if startup:
|
||||
self.background_startup_waiting += 1
|
||||
self._background_startup_clear.clear()
|
||||
try:
|
||||
await self._background.acquire()
|
||||
finally:
|
||||
self.background_startup_waiting -= 1
|
||||
if self.background_startup_waiting == 0:
|
||||
self._background_startup_clear.set()
|
||||
else:
|
||||
# Only one recurring/background request may wait directly on
|
||||
# the shared semaphore. A later startup request therefore
|
||||
# has at most one normal request ahead of it, not hundreds.
|
||||
async with self._background_normal_admission:
|
||||
await self._background_startup_clear.wait()
|
||||
await self._background.acquire()
|
||||
except BaseException:
|
||||
self.background_waiting -= 1
|
||||
raise
|
||||
self.background_waiting -= 1
|
||||
self.background_active += 1
|
||||
if startup:
|
||||
self.background_startup_active += 1
|
||||
token = self._background_owner.set((current_task, 1))
|
||||
waited = asyncio.get_running_loop().time() - started
|
||||
if waited >= 1.0:
|
||||
@@ -448,6 +482,8 @@ class TrafficController:
|
||||
yield
|
||||
finally:
|
||||
self._background_owner.reset(token)
|
||||
if startup:
|
||||
self.background_startup_active -= 1
|
||||
self.background_active -= 1
|
||||
self._background.release()
|
||||
|
||||
@@ -492,6 +528,8 @@ class TrafficController:
|
||||
"background": {
|
||||
"active": self.background_active,
|
||||
"waiting": self.background_waiting,
|
||||
"startup_active": self.background_startup_active,
|
||||
"startup_waiting": self.background_startup_waiting,
|
||||
},
|
||||
"browser": {
|
||||
"active": self.browser_active,
|
||||
|
||||
Reference in New Issue
Block a user