Improve sitemaps 20260611#7440
Conversation
- Remove unrelated proposal files (generate_proposal_html.py, integration_proposal.html) - Delete now-dead DisclosureSitemap and PersonSitemap classes (no longer registered) - Remove broken large-sitemap-<section>.xml URL patterns from sitemaps_infinite/urls.py; the generator now caches under the 'sitemaps' URL so those routes would have 500'd on cache miss via InfinitePaginatorSitemap.get_urls() raising NotImplementedError - Remove the now-empty sitemaps_infinite URL include from cl/urls.py - Simplify OpinionSitemap.priority() by removing unreachable AttributeError guards since .only() guarantees citation_count and precedential_status are fetched - Fix trailing blank lines in audio/sitemap.py and opinion_page/sitemap.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
CourtListener Sitemap Architecture: Analysis & SEO RecommendationsThis document analyzes the current architecture used by CourtListener (Free Law Project) to build and cache its sitemaps. Given that the platform houses tens of millions of court documents, efficient sitemap design is critical to ensuring robust indexation and high search engine placement on Google. 1. Current Architecture OverviewCourtListener divides its sitemap structure into two separate indexing pipelines in urls.py: A. Standard Sitemaps (
|
Walkthrough - Sitemap Architecture Optimization & ExclusionsWe have optimized CourtListener's sitemap architecture to scale for millions of records, resolved query filters on opinions to allow indexing of all unblocked opinions, implemented rates bypass for verified web crawlers, and completely excluded Changes Made1. Sitemap Class Migrations to Pregenerated Infinite Framework
2. Sitemap Namespace Unification & Exclusions
3. Pregeneration Bug Fixes
4. Celery Task Integration
5. Crawler Rate Limit Adjustments
6. Test suite Migrations
Verification & Validation ResultsAutomated TestsWe ran Django's test suite for all sitemap models sequentially to avoid Redis cross-talk. All 5 tests passed successfully: docker exec cl-django python manage.py test cl.opinion_page.tests.OpinionSitemapTest cl.opinion_page.tests.DocketSitemapTest cl.opinion_page.tests.DocketEmptySitemapTest --parallel 1Output: |
|
Three quick things here, before running to yoga:
|
|
This was Claude's assessment after the work by AG: Sitemap Changes Review What's good Resumption bug fix in sitemap_generator.py — The previous code had a real bug where Rate limiter swap (@ratelimit_deny_list) — The old @ratelimiter_all_2_per_m (2 req/min for Redis None-value sanitization — Prevents potential hset errors from None values. Celery task wrapper — Clean and correct; enables scheduled/queued generation. Relaxed OpinionSitemap filter with priority gradient — Expanding from "precedential + get_latest_lastmod() — Adds proper Last-Modified header support for conditional GET Issues to fix before committing
generate_proposal_html.py and integration_proposal.html are about a U.S. Code integration
The large-sitemap- now caches content under keys built from the sitemaps URL (/sitemap- request to /large-sitemap-recap Jump to bottom (ctrl+End) ↓ fall through to InfinitePaginatorSitemap.get_urls() → raise NotImplementedError → 500 error. The tests confirm the intent is to consolidate onto the sitemaps URL name, but
Also, the sitemap index in sitemaps_infinite/urls.py uses sitemap_url_name:
Removing them from sitemaps in cl/urls.py means /sitemap-people.xml and
Since items() calls .only(..., "citation_count", "precedential_status"), both fields are These are unnecessary — .only() guarantees these fieldstry:
Summary The core strategy is sound and will meaningfully improve Google's coverage of the site — |
- Delete Celery task wrapper; K8s CronJob calls management command directly - Replace blanket rate limit with targeted limit on cache misses only, so cached responses are unrestricted while the expensive DB path stays protected at 2/m for non-allowlisted clients - Switch sitemap view and generator to S3 cache via get_s3_cache(), gated behind enable-s3-cache waffle switch for easy rollback - Fix double blank line in cl/sitemap.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
CourtListener Sitemap Refinement Plan (S3 Cache, k8s Cron, & Smart Rate Limiting)This implementation plan refines the sitemaps optimization in response to maintainer feedback. It shifts cache operations to S3, leverages Kubernetes cronjobs (removing Celery tasks), and implements a smart rate-limiting model to protect the database while allowing unhindered search indexation. User Review RequiredImportant Use of S3 Cache (S3 Express Cache Backend): Kubernetes Cronjob Execution: Open QuestionsNote None. The maintainer feedback is clear on S3 caching, rate limits, and scheduling. Proposed ChangesComponent 1: Caching Layer Migration (S3 Cache)We will use the existing helper function [MODIFY] cl/sitemaps_infinite/sitemap_generator.py
[MODIFY] cl/sitemap.py
Component 2: Smart Rate Limiting (Cache-Miss Only)To protect the Django application servers and databases while allowing search crawlers to scan pregenerated sitemaps unhindered, we will check sitemap cache status before applying rate limits. [MODIFY] cl/sitemap.py
Component 3: Cleanup of Unused Files[DELETE] cl/sitemaps_infinite/tasks.py
Verification PlanAutomated Tests
Manual Verification
Walkthrough - Sitemap Refinement (S3 Caching & Rate Limit Updates)We have refined CourtListener's sitemap architecture according to the maintainer's feedback: migrating caching operations to AWS S3, removing the Celery tasks file in favor of Kubernetes cronjobs, and implementing a smart rate-limiting system that applies only to cache misses. Refinement Changes Made1. Caching Layer Migration to AWS S3
2. Smart Cache-Miss Rate Limiting
3. Cleanup of Celery Tasks
Verification & Validation Results1. Automated TestsAll 5 django unit tests pass successfully in 2. Manual Rate Limiting VerificationWe tested both cache-miss throttling and cache-hit bypasses inside the django shell:
I asked about crawling slowing the server: In short, these changes protect the application server and database completely, resulting in almost zero database drag even if Googlebot indexes the sitemaps rapidly. Here is the breakdown of why this is the case and how the server is protected: 1. Zero Database Drag for Sitemap Requests (Cache Hits)When Googlebot crawls the sitemap files (e.g.,
2. Strict Database Protection (Cache Misses)If a bot or malicious scraper attempts to bypass the cache by requesting pages that don't exist (e.g., requesting
3. The Real Drag: Crawling the Actual Opinion PagesIt is important to distinguish between sitemap crawling and content page crawling:
If the maintainer wants to regulate how fast Googlebot requests the actual content pages, the standard way to control this is:
SummaryBy pregenerating the sitemaps and serving them via the S3 cache, we have solved the sitemap-induced server load problem. Googlebot can crawl the sitemap files as fast as it wants without impacting database performance, while dynamic database queries are protected by cache-miss rate limiting.Changes by AG2, reviewed by CC |
AudioSitemap now uses cursor-based pre generation, so its test needs setUpSiteDomain(), reset_sitemaps_cursor(), and generate_urls_chunk() before accessing the sitemap -- matching the pattern used by OpnionSitemapTest and DocketSitemapTest. Co-Authored-By: Claude Sonnett 4.6
for more information, see https://pre-commit.ci
…c/courtlistener into improve-sitemaps-20260611
|
I asked AI Studio to analyze this pull request regarding whether generating the sitemaps in this new manner would itself cause database load that could be a drag on the site. It believes quite the opposite to be true. It wrote: Based on the technical details of Pull Request #7440, the proposed method for generating sitemaps is specifically engineered to prevent internal database thrashing and ensure that live traffic is not impacted. The strategy shifts from traditional, on-the-fly sitemap generation (which often causes the issues you’re worried about) to a pre-generated, cursor-based architecture. Here is how it addresses your specific concerns: 1. Cursor Pagination vs. SQL OffsetTraditional sitemap generation uses
2. Elimination of Dynamic Database LookupsIn the proposed way, sitemaps for the largest models (
3. Avoiding "Count" QueriesOne of the most common ways to crash a database with millions of rows is running
4. Efficient Data Transfer (
|
|
Sounds promising and right to me. I've assigned to Eduardo for review. |
Fixes
No issue — proactive improvement. First Antigravity 2.0 was used to identify bugs and inefficiencies in the existing sitemaps strategy. One bug prevented Googlebot from crawling effectively. In reviewing its proposal I chose to drop Judges and Financial Disclosures from the sitemaps and set Antigravity loose to make its proposed improvements. Then, Claude Code was asked to analyze the changes and make any further improvements. It noted some now dead code that could be dropped and made a few other improvements. Then Antigravity 2.0 was asked to review the changes Claude had made. Antigravity agreed that the changes were good and needed and that all tests passed.
Summary
OpinionSitemapcoverage: relaxed filter from "precedential, filed <75 years ago, cited ≥1 time or filed <10 years ago" to all unblocked opinions. Adds a priority gradient (0.4–0.8) so Google still knows which opinions matter most.OpinionSitemapandAudioSitemapto cursor-based pre-generation: both now extendInfinitePaginatorSitemap, matching the existingDocketSitemappattern, so they handle millions of records without timeouts.generate_urls_chunk()wasn't resettingcurrent_page = 1for sections that start fresh after a resume, potentially continuing from a stale page number.sitemaps-pregeneratedURL patterns: the generator was caching under thesitemapsURL name but the oldlarge-sitemap-*.xmlroutes usedsitemaps-pregenerated, causing guaranteed cache misses →NotImplementedError→ 500s. Retired those routes.@ratelimiter_all_2_per_m→@ratelimit_deny_list(250/h with allowlist bypass), so Googlebot isn't throttled crawling sitemaps.generate_urls_chunk()for scheduled execution.PersonSitemap,DisclosureSitemap(removed from URL config), and thesitemaps-pregeneratedURL patterns and imports.None-value sanitization to preventhseterrors.Deployment
This PR should:
skip-deployskip-web-deployskip-celery-deployskip-cronjob-deployskip-daemon-deploygenerate_sitemapsmanagement command (or the new Celery task) to warm the cache for the newly added opinion and audio sitemap sections.