aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Numerlor <[email protected]>2020-10-01 00:04:53 +0200
committerGravatar Numerlor <[email protected]>2020-10-10 15:03:38 +0200
commitc5aa0c0bd7e8933648fbedc92a7cd1f5ae199772 (patch)
treee99d41bdb8961f7a5cd473094757b881f95c54ee
parentRemove sphinx and requests from Pipfile (diff)
Reschedule failed inventory updates
-rw-r--r--bot/cogs/doc/cog.py39
1 files changed, 35 insertions, 4 deletions
diff --git a/bot/cogs/doc/cog.py b/bot/cogs/doc/cog.py
index 2cb296d53..41fca4584 100644
--- a/bot/cogs/doc/cog.py
+++ b/bot/cogs/doc/cog.py
@@ -19,6 +19,7 @@ from bot.converters import PackageName, ValidURL
from bot.decorators import with_role
from bot.pagination import LinePaginator
from bot.utils.messages import wait_for_deletion
+from bot.utils.scheduling import Scheduler
from .inventory_parser import FAILED_REQUEST_ATTEMPTS, fetch_inventory
from .parsing import get_symbol_markdown
@@ -189,6 +190,9 @@ class DocCog(commands.Cog):
self.item_fetcher = CachedParser()
self.renamed_symbols = set()
+ self.inventory_scheduler = Scheduler(self.__class__.__name__)
+ self.scheduled_inventories = set()
+
self.bot.loop.create_task(self.init_refresh_inventory())
async def init_refresh_inventory(self) -> None:
@@ -198,7 +202,7 @@ class DocCog(commands.Cog):
async def update_single(
self, api_package_name: str, base_url: str, inventory_url: str
- ) -> None:
+ ) -> bool:
"""
Rebuild the inventory for a single package.
@@ -207,12 +211,27 @@ class DocCog(commands.Cog):
* `base_url` is the root documentation URL for the specified package, used to build
absolute paths that link to specific symbols
* `inventory_url` is the absolute URL to the intersphinx inventory.
+
+ If the inventory file is currently unreachable,
+ the update is rescheduled to execute in 2 minutes on the first attempt, and 5 minutes on subsequent attempts.
+
+ Return True on success; False if fetching failed and was rescheduled.
"""
self.base_urls[api_package_name] = base_url
-
package = await fetch_inventory(self.bot.http_session, inventory_url)
+
if not package:
- return None
+ delay = 2*60 if inventory_url not in self.scheduled_inventories else 5*60
+ log.info(f"Failed to fetch inventory, attempting again in {delay//60} minutes.")
+ self.inventory_scheduler.schedule_later(
+ delay,
+ api_package_name,
+ fetch_inventory(self.bot.http_session, inventory_url)
+ )
+ self.scheduled_inventories.add(api_package_name)
+ return False
+ with suppress(KeyError):
+ self.scheduled_inventories.discard(api_package_name)
for group, items in package.items():
for symbol, relative_doc_url in items:
@@ -249,6 +268,7 @@ class DocCog(commands.Cog):
self.item_fetcher.add_item(symbol_item)
log.trace(f"Fetched inventory for {api_package_name}.")
+ return True
async def refresh_inventory(self) -> None:
"""Refresh internal documentation inventory."""
@@ -260,6 +280,7 @@ class DocCog(commands.Cog):
self.base_urls.clear()
self.doc_symbols.clear()
self.renamed_symbols.clear()
+ self.scheduled_inventories.clear()
await self.item_fetcher.clear()
# Run all coroutines concurrently - since each of them performs a HTTP
@@ -385,7 +406,11 @@ class DocCog(commands.Cog):
f"Inventory URL: {inventory_url}"
)
- await self.update_single(package_name, base_url, inventory_url)
+ if await self.update_single(package_name, base_url, inventory_url) is None:
+ await ctx.send(
+ f"Added package `{package_name}` to database but failed to fetch inventory; rescheduled in 2 minutes."
+ )
+ return
await ctx.send(f"Added package `{package_name}` to database and refreshed inventory.")
@docs_group.command(name='deletedoc', aliases=('removedoc', 'rm', 'd'))
@@ -399,6 +424,9 @@ class DocCog(commands.Cog):
"""
await self.bot.api_client.delete(f'bot/documentation-links/{package_name}')
+ if package_name in self.scheduled_inventories:
+ self.inventory_scheduler.cancel(package_name)
+
async with ctx.typing():
# Rebuild the inventory to ensure that everything
# that was from this package is properly deleted.
@@ -409,6 +437,9 @@ class DocCog(commands.Cog):
@with_role(*MODERATION_ROLES)
async def refresh_command(self, ctx: commands.Context) -> None:
"""Refresh inventories and send differences to channel."""
+ for inventory in self.scheduled_inventories:
+ self.inventory_scheduler.cancel(inventory)
+
old_inventories = set(self.base_urls)
with ctx.typing():
await self.refresh_inventory()