diff options
| author | 2021-10-17 11:00:47 +0100 | |
|---|---|---|
| committer | 2021-10-17 11:00:47 +0100 | |
| commit | 5f2eebf6d6a03b7760c07d71cd20df93bfed2bdc (patch) | |
| tree | 4982fff5bd644215f454f416b049674ab4695571 | |
| parent | Fixed delayed logs and made some other minor refactors (#1863) (diff) | |
| parent | Merge branch 'main' into doc-repetitive-outdated-warn (diff) | |
Merge pull request #1795 from Numerlor/doc-repetitive-outdated-warn
Prevent repetitive stale inventory warnings from erroneously generated inventories
| -rw-r--r-- | bot/exts/info/doc/_batch_parser.py | 22 | ||||
| -rw-r--r-- | bot/exts/info/doc/_cog.py | 1 | ||||
| -rw-r--r-- | bot/exts/info/doc/_redis_cache.py | 43 | 
3 files changed, 47 insertions, 19 deletions
| diff --git a/bot/exts/info/doc/_batch_parser.py b/bot/exts/info/doc/_batch_parser.py index 92f814c9d..c27f28eac 100644 --- a/bot/exts/info/doc/_batch_parser.py +++ b/bot/exts/info/doc/_batch_parser.py @@ -17,6 +17,7 @@ from bot.utils import scheduling  from . import _cog, doc_cache  from ._parsing import get_symbol_markdown +from ._redis_cache import StaleItemCounter  log = get_logger(__name__) @@ -24,6 +25,8 @@ log = get_logger(__name__)  class StaleInventoryNotifier:      """Handle sending notifications about stale inventories through `DocItem`s to dev log.""" +    symbol_counter = StaleItemCounter() +      def __init__(self):          self._init_task = scheduling.create_task(              self._init_channel(), @@ -40,13 +43,16 @@ class StaleInventoryNotifier:      async def send_warning(self, doc_item: _cog.DocItem) -> None:          """Send a warning to dev log if one wasn't already sent for `item`'s url."""          if doc_item.url not in self._warned_urls: -            self._warned_urls.add(doc_item.url) -            await self._init_task -            embed = discord.Embed( -                description=f"Doc item `{doc_item.symbol_id=}` present in loaded documentation inventories " -                            f"not found on [site]({doc_item.url}), inventories may need to be refreshed." -            ) -            await self._dev_log.send(embed=embed) +            # Only warn if the item got less than 3 warnings +            # or if it has been more than 3 weeks since the last warning +            if await self.symbol_counter.increment_for(doc_item) < 3: +                self._warned_urls.add(doc_item.url) +                await self._init_task +                embed = discord.Embed( +                    description=f"Doc item `{doc_item.symbol_id=}` present in loaded documentation inventories " +                                f"not found on [site]({doc_item.url}), inventories may need to be refreshed." +                ) +                await self._dev_log.send(embed=embed)  class QueueItem(NamedTuple): @@ -103,7 +109,7 @@ class BatchParser:          if doc_item not in self._item_futures and doc_item not in self._queue:              self._item_futures[doc_item].user_requested = True -            async with bot.instance.http_session.get(doc_item.url) as response: +            async with bot.instance.http_session.get(doc_item.url, raise_for_status=True) as response:                  soup = await bot.instance.loop.run_in_executor(                      None,                      BeautifulSoup, diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py index fbbcd4a10..ebf5f5932 100644 --- a/bot/exts/info/doc/_cog.py +++ b/bot/exts/info/doc/_cog.py @@ -464,6 +464,7 @@ class DocCog(commands.Cog):      ) -> None:          """Clear the persistent redis cache for `package`."""          if await doc_cache.delete(package_name): +            await self.item_fetcher.stale_inventory_notifier.symbol_counter.delete()              await ctx.send(f"Successfully cleared the cache for `{package_name}`.")          else:              await ctx.send("No keys matching the package found.") diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py index 79648893a..107f2344f 100644 --- a/bot/exts/info/doc/_redis_cache.py +++ b/bot/exts/info/doc/_redis_cache.py @@ -25,8 +25,7 @@ class DocRedisCache(RedisObject):          All keys from a single page are stored together, expiring a week after the first set.          """ -        url_key = remove_suffix(item.relative_url_path, ".html") -        redis_key = f"{self.namespace}:{item.package}:{url_key}" +        redis_key = f"{self.namespace}:{item_key(item)}"          needs_expire = False          with await self._get_pool_connection() as connection: @@ -44,10 +43,36 @@ class DocRedisCache(RedisObject):      @namespace_lock      async def get(self, item: DocItem) -> Optional[str]:          """Return the Markdown content of the symbol `item` if it exists.""" -        url_key = remove_suffix(item.relative_url_path, ".html") +        with await self._get_pool_connection() as connection: +            return await connection.hget(f"{self.namespace}:{item_key(item)}", item.symbol_id, encoding="utf8") +    @namespace_lock +    async def delete(self, package: str) -> bool: +        """Remove all values for `package`; return True if at least one key was deleted, False otherwise.""" +        with await self._get_pool_connection() as connection: +            package_keys = [ +                package_key async for package_key in connection.iscan(match=f"{self.namespace}:{package}:*") +            ] +            if package_keys: +                await connection.delete(*package_keys) +                return True +            return False + + +class StaleItemCounter(RedisObject): +    """Manage increment counters for stale `DocItem`s.""" + +    @namespace_lock +    async def increment_for(self, item: DocItem) -> int: +        """ +        Increment the counter for `item` by 1, set it to expire in 3 weeks and return the new value. + +        If the counter didn't exist, initialize it with 1. +        """ +        key = f"{self.namespace}:{item_key(item)}:{item.symbol_id}"          with await self._get_pool_connection() as connection: -            return await connection.hget(f"{self.namespace}:{item.package}:{url_key}", item.symbol_id, encoding="utf8") +            await connection.expire(key, WEEK_SECONDS * 3) +            return int(await connection.incr(key))      @namespace_lock      async def delete(self, package: str) -> bool: @@ -62,10 +87,6 @@ class DocRedisCache(RedisObject):              return False -def remove_suffix(string: str, suffix: str) -> str: -    """Remove `suffix` from end of `string`.""" -    # TODO replace usages with str.removesuffix on 3.9 -    if string.endswith(suffix): -        return string[:-len(suffix)] -    else: -        return string +def item_key(item: DocItem) -> str: +    """Get the redis redis key string from `item`.""" +    return f"{item.package}:{item.relative_url_path.removesuffix('.html')}" | 
