aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Numerlor <[email protected]>2020-11-10 01:14:31 +0100
committerGravatar Numerlor <[email protected]>2020-11-10 01:14:31 +0100
commitc9fe7b1d6b98334c29f516b682b93b4c1c3946a1 (patch)
treee2f211a17d250477080f3cbd27de18cad6aac357
parentHandle escaped backslashes in strings (diff)
Cache user fetched symbols through redis.
-rw-r--r--bot/exts/info/doc/_cog.py22
-rw-r--r--bot/exts/info/doc/_redis_cache.py23
2 files changed, 43 insertions, 2 deletions
diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py
index 257435e95..ab3ad159a 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 urllib.parse
from collections import defaultdict
from contextlib import suppress
from typing import Dict, List, NamedTuple, Optional, Union
@@ -21,6 +22,7 @@ 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
+from ._redis_cache import DocRedisCache
log = logging.getLogger(__name__)
@@ -182,6 +184,8 @@ class InventoryURL(commands.Converter):
class DocCog(commands.Cog):
"""A set of commands for querying & displaying documentation."""
+ doc_cache = DocRedisCache()
+
def __init__(self, bot: Bot):
self.base_urls = {}
self.bot = bot
@@ -296,16 +300,30 @@ class DocCog(commands.Cog):
Attempt to scrape and fetch the data for the given `symbol`, and build an embed from its contents.
If the symbol is known, an Embed with documentation about it is returned.
+
+ First check the DocRedisCache before querying the cog's `CachedParser`,
+ if not present also create a redis entry for the symbol.
"""
+ log.trace(f"Building embed for symbol `{symbol}`")
symbol_info = self.doc_symbols.get(symbol)
if symbol_info is None:
+ log.debug("Symbol does not exist.")
return None
self.bot.stats.incr(f"doc_fetches.{symbol_info.package.lower()}")
+ item_url = f"{symbol_info.url}#{symbol_info.symbol_id}"
+ redis_key = "".join(urllib.parse.urlparse(item_url)[1:]) # url without scheme
+
+ markdown = await self.doc_cache.get(redis_key)
+ if markdown is None:
+ log.debug(f"Redis cache miss for symbol `{symbol}`.")
+ markdown = await self.item_fetcher.get_markdown(self.bot.http_session, symbol_info)
+ await self.doc_cache.set(redis_key, markdown)
+
embed = discord.Embed(
title=discord.utils.escape_markdown(symbol),
- url=f"{symbol_info.url}#{symbol_info.symbol_id}",
- description=await self.item_fetcher.get_markdown(self.bot.http_session, symbol_info)
+ url=item_url,
+ description=markdown
)
# Show all symbols with the same name that were renamed in the footer.
embed.set_footer(
diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py
new file mode 100644
index 000000000..147394ba6
--- /dev/null
+++ b/bot/exts/info/doc/_redis_cache.py
@@ -0,0 +1,23 @@
+from typing import Optional
+
+from async_rediscache.types.base import RedisObject, namespace_lock
+
+
+class DocRedisCache(RedisObject):
+ """Interface for redis functionality needed by the Doc cog."""
+
+ @namespace_lock
+ async def set(self, key: str, value: str) -> None:
+ """
+ Set markdown `value` for `key`.
+
+ Keys expire after a week to keep data up to date.
+ """
+ with await self._get_pool_connection() as connection:
+ await connection.setex(f"{self.namespace}:{key}", 7*24*60*60, value)
+
+ @namespace_lock
+ async def get(self, key: str) -> Optional[str]:
+ """Get markdown contents for `key`."""
+ with await self._get_pool_connection() as connection:
+ return await connection.get(f"{self.namespace}:{key}", encoding="utf8")