#Is caching simple reverse() / {% url %} or {% static %} calls safe?

1 messages · Page 1 of 1 (latest)

cosmic granite
#

I tried looking around but I couldn't find any answers so I wanted to ask: is caching the results of reverse("url_name_here") (or equivalent {% url "url_name_here" %}) safe / won't cause any issues? Here's some example code

from django.urls import reverse

PATH_CACHE = {}

def cached_url(value):
    cached_path = PATH_CACHE.get(value, None)
    if cached_path is None:
        cached_path = reverse(value)
        PATH_CACHE[value] = cached_path
    return cached_path

With this example code, I'd replace simple reverse() calls (ones without arguments) with cached_url(). Would also like to know the same for static('path/to/file') and {% static 'path/to/file' %}, especially with the ManifestStaticFilesStorage storage backend.

As I intend to run my application with Gunicorn's Gevent workers, it'd be also great to know if this would cause any issues with greenlets / async. While I'm not particularly experienced with async, if it's OK running synchronously it doesn't seem like it'd be an issue with async as if 2 separate greenlet(s? thread-likes? Not exactly sure how to call them) reverse the same URL name and set it on the dictionary, the only thing lost would be some performance as (according to https://stackoverflow.com/a/6955678) Python's dictionaries are thread-safe for single operations.

P.S. I initially posted this as a regular message but realized a bit too late that perhaps the question is best framed as a thread.

spring urchin
#

I see no problems with doing it, the URL patterns don't change during runtime (unless you're doing something very odd?). Your cache function doesn't support URLs with path parameters though.
I guess I would question what's the point, though? Have you profiled your code and found reversing URLs and generating static URLs to be an unacceptable performance problem?

cosmic granite
#

Thanks for the info. Yeah, I understand it's a micro optimization but it just felt so obvious and simple that I thought I'd use it as all it requires is replacing a bunch of function calls and tags with the cached version. Were it something that required more than 5 mins of effort, I probably would've skipped it and focused on much more important optimizations. The lack of path parameters is intentional - caching URLs with those would require more work (maintaining max cache size, clearing old entries, etc.) and feels like the type of micro optimization that's just not worth it (alongside the much bigger memory trade off).

spring urchin
#

It's a neat idea, though—you could perhaps raise it on the forum or via the New Features GitHub as something to add to Django by default?

cosmic granite
#

From what I've seen, Django generally seems to take the "good enough" approach to performance (which is fair, since Python isn't exactly the language you go for if you want to squeeze out every tiny bit of performance) and considering how simple the idea is, it doesn't seem like this kind of micro optimization would interest them.

spring urchin
#

You'll never know unless you ask 🤷‍♂️

low elm
#

Opinionated answer: django's url resolver is very fast. I haven't profiled it, but I'd be surprised if you will even detect a performance improvement. Premature optimization is harmful. Even when it only takes 5 minutes, optimization should follow performance analysis.
That in no way excuses sloppy, inefficient code, it just means don't add extra complexity until you know you have a problem.

gusty frost
#

Technically this is also not safe - you can specify a different url conf on the request object which I've seen in real projects done via middleware. Django also allows swapping out a different root urlconf with override_settings in tests

Additionally, rolling your own cache like that is a bit naive and a theoretical potential memory leak. I'd rather leverage @functools.lru_cache with an appropriate max_size argument