fix: reduce chess-results.com request volume by ~20-50x
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 5s

- check_all_subscriptions: quick pre-check (1 req, art=4 only) before
  full fetch; full fetch only when results_count or current_round changed.
  Interval 60s → 300s. Saves ~10,000 req/day per active subscription.
- rescan_existing_tournaments: filter to tournaments ended within last 30
  days (or no end_date). Drops from ~2000 cached TNRs to ~30-100 active
  ones per run, saving ~22,000 req/day.
- warmup: sleep 1s per TNR instead of 20ms per 500 — spreads initial
  full-scan over hours rather than minutes.
- parser: extract _normalize_url helper, add fetch_tournament_quick().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
vrubel 2026-06-21 07:47:38 +00:00
parent 66b10d9425
commit 8058080c3a
3 changed files with 65 additions and 12 deletions

View file

@ -768,21 +768,31 @@ def _warmup_cache_sync():
if tnr % 100 == 0:
set_tnr_state('warmup_tnr', str(tnr))
if tnr % 500 == 0:
elapsed = (start_tnr - tnr) // 500 * 500 # rough
print(f'warmup: progress TNR {tnr} (started at {start_tnr})', file=sys.stderr)
time.sleep(0.02)
# Rate-limit: ~1 req/sec to stay within chess-results.com daily limit.
# Cached TNRs return instantly (no HTTP), so this only applies to misses.
time.sleep(1.0)
# Mark warmup as complete — next run will see this and skip
set_tnr_state('warmup_tnr', 'done')
async def rescan_existing_tournaments(context):
"""Every 2h: refetch art=0 for all cached TNRs, detect new players."""
"""Every 2h: refetch art=0 for recent/active cached TNRs, detect new players.
Only rescans tournaments that ended within the last 30 days (or have no
end_date). Old completed tournaments never get new players, so skipping
them keeps daily request volume within chess-results.com rate limits.
"""
import time
from datetime import timedelta
from bots.client_bot import _md_escape as _md_esc
cutoff = (date.today() - timedelta(days=30)).isoformat()
conn = _get_conn()
cached_tnrs = [r[0] for r in conn.execute('SELECT tnr FROM tnr_cache ORDER BY tnr DESC').fetchall()]
cached_tnrs = [r[0] for r in conn.execute(
'SELECT tnr FROM tnr_cache WHERE end_date = "" OR end_date >= ? ORDER BY tnr DESC',
(cutoff,)).fetchall()]
conn.close()
if not cached_tnrs:
return
@ -840,7 +850,7 @@ async def rescan_existing_tournaments(context):
async def check_all_subscriptions(context):
from swiss_calc.parser import fetch_tournament
from swiss_calc.parser import fetch_tournament, fetch_tournament_quick
from swiss_calc.swiss import calculate_next_round
from bots.client_bot import format_pairings, _render_chunks
from telegram.constants import ParseMode
@ -849,10 +859,22 @@ async def check_all_subscriptions(context):
if not subs:
return
today_str = date.today().isoformat()
for sub in subs:
if sub['tournament_url'] == 'fide_only':
continue
# Quick check: 1 request — skip full fetch if nothing changed
try:
quick = fetch_tournament_quick(sub['tournament_url'])
except Exception:
continue
quick_player = find_player(quick, sub['player_name'], sub['player_sno'])
quick_results = len(quick_player.get('results', [])) if quick_player else 0
quick_rd = quick.get('current_round', 0)
if quick_results <= sub['last_results_count'] and quick_rd <= sub['last_round_done']:
continue
# Something changed — do full fetch for accurate data and pairing calc
try:
tournament = fetch_tournament(sub['tournament_url'])
except Exception: