aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Numerlor <[email protected]>2021-01-09 21:48:51 +0100
committerGravatar Numerlor <[email protected]>2021-01-10 00:19:16 +0100
commit70609baca94dc7c7ad7598f707ac479efe348e88 (patch)
tree5267d456162fde262e70a703d900f5b9b238c91c
parentDo not add package name to the front of the symbol if it's already there (diff)
Periodically clear unnecessary futures from the _item_futures dict
The code has no way of reaching futures through new requests after their result has been set as that also includes setting its value in redis.
-rw-r--r--bot/exts/info/doc/_cog.py34
1 files changed, 33 insertions, 1 deletions
diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py
index feb08e1cb..364d99182 100644
--- a/bot/exts/info/doc/_cog.py
+++ b/bot/exts/info/doc/_cog.py
@@ -4,6 +4,7 @@ import asyncio
import logging
import re
import sys
+import time
from collections import defaultdict
from contextlib import suppress
from functools import partial
@@ -80,11 +81,25 @@ class QueueItem(NamedTuple):
class ParseResultFuture(asyncio.Future):
- """Future with the user_requested attribute to know which futures need to be waited for before clearing."""
+ """
+ Future with metadata for the parser class.
+
+ `user_requested` is set by the parser when a Future is requested by an user and moved to the front,
+ allowing the futures to only be waited for when clearing if they were user requested.
+
+ `result_set_time` provides the time at which the future's result has been set,
+ or -inf if the result hasn't been set yet
+ """
def __init__(self):
super().__init__()
self.user_requested = False
+ self.result_set_time = float("inf")
+
+ def set_result(self, result: str, /) -> None:
+ """Set `self.result_set_time` to current time when the result is set."""
+ self.result_set_time = time.time()
+ super().set_result(result)
class CachedParser:
@@ -102,6 +117,8 @@ class CachedParser:
self._item_futures: Dict[DocItem, ParseResultFuture] = {}
self._parse_task = None
+ self.cleanup_futures_task = bot_instance.loop.create_task(self._cleanup_futures())
+
async def get_markdown(self, doc_item: DocItem) -> str:
"""
Get the result Markdown of `doc_item`.
@@ -183,6 +200,21 @@ class CachedParser:
self._page_symbols.clear()
self._item_futures.clear()
+ async def _cleanup_futures(self) -> None:
+ """
+ Clear old futures from internal results.
+
+ After a future is set, we only need to wait for old requests to its associated DocItem to finish
+ as all new requests will get the value from the redis cache in the cog first.
+ Keeping them around for longer than a second is unnecessary and keeps the parsed Markdown strings alive.
+ """
+ while True:
+ current_time = time.time()
+ for key, future in self._item_futures.copy().items():
+ if current_time - future.result_set_time > 5:
+ del self._item_futures[key]
+ await asyncio.sleep(5)
+
class DocCog(commands.Cog):
"""A set of commands for querying & displaying documentation."""