fix: cap ring related products to 45 and add 502 retry in watch mode

Previously every watch JSON request and product page load called
get_ring_related_products with forward=len(ring), loading ALL products
in the shop from the DB. For large rings this caused worker memory to
spike, triggering uwsgi reload-on-rss kills and producing 502s.

Cap related items to 42 forward + 3 backward (matching the sidebar
display limit). The watch JSON endpoint now accepts a dir query param
so the client can signal travel direction — when going backward the
allocation flips to 3 forward + 42 backward.

Also adds retry with backoff (up to 2 retries, 1s/2s delay) in
fetchWatchData() for transient 502/503/504 responses during worker
recycling.
This commit is contained in:
russell@unturf.com 2026-02-24 12:13:45 -05:00
parent 7145f2b313
commit 40828ca31b
3 changed files with 23 additions and 5 deletions

View file

@ -413,11 +413,20 @@
});
}
// --- Fetch watch JSON ---
function fetchWatchData(productId) {
return fetch('/watch/' + productId + '/json', {
// --- Fetch watch JSON (with retry for transient 502s) ---
function fetchWatchData(productId, attempt) {
attempt = attempt || 0;
var url = '/watch/' + productId + '/json?dir=' + ringDirection;
return fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'}
}).then(function(r) {
if (r.status >= 502 && r.status <= 504 && attempt < 2) {
return new Promise(function(resolve) {
setTimeout(resolve, (attempt + 1) * 1000);
}).then(function() {
return fetchWatchData(productId, attempt + 1);
});
}
if (!r.ok) throw new Error('Watch fetch failed');
return r.json();
});

View file

@ -98,7 +98,7 @@ def product(request):
from ..models.product import get_ring_related_products, get_related_products
ring = product.shop.discovery_ring
if ring:
related_products = get_ring_related_products(product, ring, forward=len(ring))
related_products = get_ring_related_products(product, ring, forward=42)
else:
related_products = get_related_products(product)

View file

@ -106,7 +106,16 @@ def watch_json(request):
# Use existing discovery ring — never recompute during a request
ring = request.shop.discovery_ring
if ring:
related = get_ring_related_products(product, ring, forward=len(ring))
# Client sends dir=-1 when traveling backward through the ring.
# Flip forward/backward so the sidebar shows items in the travel direction.
try:
direction = int(request.params.get("dir", "1"))
except (ValueError, TypeError):
direction = 1
if direction == -1:
related = get_ring_related_products(product, ring, forward=3, backward=42)
else:
related = get_ring_related_products(product, ring, forward=42, backward=3)
else:
related = get_related_products(product)