Skip to content

Cache sitemap discovery results via discovery.cache_ttl_hours - #91

Merged
brylie merged 4 commits into
mainfrom
feature/cache-sitemap-discovery
Aug 7, 2026
Merged

Cache sitemap discovery results via discovery.cache_ttl_hours#91
brylie merged 4 commits into
mainfrom
feature/cache-sitemap-discovery

Conversation

@brylie

@brylie brylie commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds discovery.cache_ttl_hours (default 24h) to DiscoveryConfig, matching the value already documented in the crawler-improvements spec's example config.
  • Adds a discovery_run SQLite table to ManifestStore (record_discovery_run / get_last_discovery_run) tracking each site's last completed discovery run — just a completed_at timestamp and complete flag, not a full run-history log (that's a larger change, deferred per Persist robots.txt and sitemap URLs as crawl-run metadata #77's discussion).
  • DiscoveryRunner.run() checks this before fetching: if a sitemap-source site's last discovery run completed within cache_ttl_hours, it skips the robots.txt and sitemap fetch entirely and rebuilds the run summary (discovered/eligible/excluded_by_reason) from the manifest's existing records instead. DiscoveryRunSummary gains a cached: bool field so callers/logs can tell.
  • Every sitemap-source run (cached or not) records its completion, so a run that failed (e.g. robots.txt unreachable) is stored as incomplete and never served from cache.
  • cache_ttl_hours: 0 disables caching outright — always re-fetches, matching the issue's stated escape hatch.
  • discover's CLI output notes (cache hit; no HTTP requests made) when the summary came from cache.
  • Gap-crawl discovery is untouched — caching only applies to discovery.source == "sitemap".

Closes #76

Test plan

  • uv run pytest — full crawler suite passes (120 tests)
  • New tests cover: cache hit skips HTTP calls and rebuilds discovered/eligible/excluded_by_reason from the manifest; cache miss after TTL elapse re-fetches; cache_ttl_hours=0 always re-fetches; a successful run records discovery_run; an incomplete run (robots unreachable) is recorded incomplete and not served from cache; ManifestStore.record_discovery_run/get_last_discovery_run round-trip, overwrite-on-rerun, and per-site isolation; CLI prints the cache-hit note
  • uv run ruff check / ruff format --check — clean
  • uv run mypy tapio_crawler / uv run pyrefly check tapio_crawler — clean
  • pre-commit hooks passed on the commit

Summary by CodeRabbit

  • New Features

    • Added caching for completed sitemap discovery runs to reduce repeat network requests.
    • Added a configurable cache duration, defaulting to 24 hours; setting it to zero disables caching.
    • Discovery status now indicates when results were served from cache.
    • Cached results are refreshed when sitemap URLs, scope settings, or the base URL changes.
  • Bug Fixes

    • Incomplete, failed, or inaccessible-robots runs are no longer reused as cached results.
    • Cached result counts now accurately reflect stored discovery records.

Within the TTL, discover reuses the manifest's existing scope
decisions instead of re-fetching robots.txt and the sitemap, cutting
avoidable HTTP requests to sources on repeated runs. A dedicated
discovery_run table (per-site last completed_at + complete flag)
tracks freshness. TTL 0 disables caching and always re-fetches.

Closes #76

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 05557190-0a03-4137-8575-ea384ccf7618

📥 Commits

Reviewing files that changed from the base of the PR and between 14d395b and e222961.

📒 Files selected for processing (3)
  • crawler/tapio_crawler/discovery/runner.py
  • crawler/tapio_crawler/manifest/store.py
  • crawler/tests/discovery/test_discovery_runner.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • crawler/tapio_crawler/manifest/store.py
  • crawler/tapio_crawler/discovery/runner.py

📝 Walkthrough

Walkthrough

Sitemap discovery now supports TTL-based caching with configuration fingerprints. Fresh complete runs rebuild summaries from manifest records without HTTP requests. Incomplete or changed runs are not reused, and the CLI reports cache hits.

Changes

Sitemap discovery cache

Layer / File(s) Summary
Cache configuration and run state
crawler/tapio_crawler/config/config_models.py, crawler/tapio_crawler/manifest/store.py, crawler/tests/manifest/test_store.py
DiscoveryConfig adds a non-negative cache_ttl_hours setting. ManifestStore persists discovery status, completion timestamps, and configuration fingerprints per site, with migration support.
Cache evaluation and discovery lifecycle
crawler/tapio_crawler/discovery/runner.py, crawler/tests/discovery/test_discovery_runner.py
DiscoveryRunner validates TTL and configuration fingerprints, rebuilds fresh summaries from manifest records, records complete or incomplete runs, and refetches when the cache is stale, disabled, incomplete, or mismatched.
Cache-hit CLI reporting
crawler/tapio_crawler/cli.py, crawler/tests/test_cli.py
The CLI appends a cache-hit note when the discovery summary came from cached data.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DiscoveryRunner
  participant ManifestStore
  participant RobotsAndSitemapHTTP
  DiscoveryRunner->>ManifestStore: Get latest discovery run
  alt Fresh complete matching run
    ManifestStore-->>DiscoveryRunner: Return run metadata
    DiscoveryRunner->>ManifestStore: Read manifest records
    ManifestStore-->>DiscoveryRunner: Return manifest records
    DiscoveryRunner-->>DiscoveryRunner: Build cached summary
  else Cache unavailable or expired
    DiscoveryRunner->>RobotsAndSitemapHTTP: Fetch robots.txt and sitemaps
    RobotsAndSitemapHTTP-->>DiscoveryRunner: Return discovery data
    DiscoveryRunner->>ManifestStore: Record run status and fingerprint
  end
Loading

Possibly related PRs

  • Finntegrate/tapio#73: Implements related sitemap discovery and run-state persistence.
  • Finntegrate/tapio#74: Adds the discovery configuration, runner, manifest store, and CLI flow extended by this change.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: caching sitemap discovery results using discovery.cache_ttl_hours.
Linked Issues check ✅ Passed The implementation satisfies issue [#76] by caching sitemap runs within the TTL and refetching after expiry, with coverage for cache behavior and incomplete runs.
Out of Scope Changes check ✅ Passed The changes support the caching objective through configuration, persistence, runner logic, CLI reporting, and focused tests; no unrelated code changes are evident.
Docstring Coverage ✅ Passed Docstring coverage is 96.55% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/cache-sitemap-discovery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…-discovery

# Conflicts:
#	crawler/tapio_crawler/cli.py
#	crawler/tapio_crawler/discovery/runner.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crawler/tapio_crawler/discovery/runner.py (1)

108-149: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Invalidate cached state for every failed sitemap run.

Only the unreachable-required-robots path records complete=False. If fetch_robots_rules(), _discover_urls(), or _persist_discovered_urls() raises, the prior complete record remains active.

If caching was disabled for that failed run and is later enabled, _try_cached_summary() can reuse the earlier record. Record an incomplete state before network work starts, then replace it with the final status only after successful completion. Add exception-path coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crawler/tapio_crawler/discovery/runner.py` around lines 108 - 149, Update the
sitemap flow in the surrounding discovery runner to record an incomplete
discovery run before fetch_robots_rules() begins, so failures in
fetch_robots_rules(), _discover_urls(), or _persist_discovered_urls() invalidate
prior cached state. On successful completion, replace that record with
summary.complete; preserve the existing non-sitemap behavior and add
exception-path coverage.
🧹 Nitpick comments (1)
crawler/tapio_crawler/manifest/store.py (1)

62-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Describe incomplete discovery runs correctly.

get_last_discovery_run() returns records with complete=False. Do not describe those records as completed runs. State that the method returns the most recently recorded run and returns None only when no run was recorded.

Also applies to: 143-151

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crawler/tapio_crawler/manifest/store.py` around lines 62 - 67, Update the
LastDiscoveryRun documentation and the get_last_discovery_run() documentation to
describe the returned value as the most recently recorded discovery run,
including incomplete runs, and state that None is returned only when no run has
been recorded. Remove wording that implies every returned run completed
successfully.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crawler/tapio_crawler/manifest/store.py`:
- Around line 53-58: Persist a versioned fingerprint of cache-relevant discovery
and scope configuration in the discovery_run schema and store accessors; in
crawler/tapio_crawler/discovery/runner.py lines 168-175, compare the current
fingerprint before returning cached manifest records and rerun discovery when it
differs; in crawler/tests/discovery/test_discovery_runner.py lines 211-266, add
coverage changing sitemap URLs and scope configuration within the TTL and assert
sitemap refetching and recalculated results.

---

Outside diff comments:
In `@crawler/tapio_crawler/discovery/runner.py`:
- Around line 108-149: Update the sitemap flow in the surrounding discovery
runner to record an incomplete discovery run before fetch_robots_rules() begins,
so failures in fetch_robots_rules(), _discover_urls(), or
_persist_discovered_urls() invalidate prior cached state. On successful
completion, replace that record with summary.complete; preserve the existing
non-sitemap behavior and add exception-path coverage.

---

Nitpick comments:
In `@crawler/tapio_crawler/manifest/store.py`:
- Around line 62-67: Update the LastDiscoveryRun documentation and the
get_last_discovery_run() documentation to describe the returned value as the
most recently recorded discovery run, including incomplete runs, and state that
None is returned only when no run has been recorded. Remove wording that implies
every returned run completed successfully.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a30a0e22-e6c9-4a33-9d9d-5177dfc0693b

📥 Commits

Reviewing files that changed from the base of the PR and between 1474a6c and e2bc7e4.

📒 Files selected for processing (7)
  • crawler/tapio_crawler/cli.py
  • crawler/tapio_crawler/config/config_models.py
  • crawler/tapio_crawler/discovery/runner.py
  • crawler/tapio_crawler/manifest/store.py
  • crawler/tests/discovery/test_discovery_runner.py
  • crawler/tests/manifest/test_store.py
  • crawler/tests/test_cli.py

Comment thread crawler/tapio_crawler/manifest/store.py
Cache-relevant discovery/scope config now fingerprints into the recorded
discovery_run row, so a later run treats a cache hit as stale when
sitemap_urls or scope rules change, even within cache_ttl_hours. A
discovery run is also now recorded incomplete before robots/sitemap
fetching begins, so a crash partway through leaves the site's last
recorded run marked incomplete instead of leaving a stale "complete" run
in place that a later call could still serve from cache.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crawler/tapio_crawler/manifest/store.py (1)

63-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Complete the changed Google-style docstrings.

  • crawler/tapio_crawler/manifest/store.py#L63-L73: Add an Attributes section for completed_at, complete, and config_fingerprint.
  • crawler/tapio_crawler/discovery/runner.py#L43-L58: Add Args and Returns sections for _config_fingerprint.
  • crawler/tests/discovery/test_discovery_runner.py#L317-L322: Add an Args section for store.
  • crawler/tests/discovery/test_discovery_runner.py#L363-L369: Add an Args section for store.
  • crawler/tests/discovery/test_discovery_runner.py#L496-L503: Add an Args section for store.

As per coding guidelines, use Google-style docstrings for all Python functions and classes, documenting summaries, parameters, return types, exceptions, examples, notes, TODOs, deprecations, references, and warnings where applicable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crawler/tapio_crawler/manifest/store.py` around lines 63 - 73, Complete the
Google-style docstrings at all five sites: in
crawler/tapio_crawler/manifest/store.py:63-73, add an Attributes section
documenting completed_at, complete, and config_fingerprint; in
crawler/tapio_crawler/discovery/runner.py:43-58, add Args and Returns sections
for _config_fingerprint; and in
crawler/tests/discovery/test_discovery_runner.py:317-322, 363-369, and 496-503,
add an Args section documenting store for each affected test function.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crawler/tapio_crawler/discovery/runner.py`:
- Around line 43-58: The cache fingerprint omits the site origin, allowing
within-TTL reuse across base URL changes. In
crawler/tapio_crawler/discovery/runner.py:43-58, add a normalized site base_url
to _config_fingerprint; update crawler/tapio_crawler/discovery/runner.py:127-132
and 218-219 to pass and compare the same base-URL-aware fingerprint. In
crawler/tests/discovery/test_discovery_runner.py:317-360, add a within-TTL test
changing only SiteConfig.base_url and assert a cache miss with robots.txt and
sitemap requests.

---

Nitpick comments:
In `@crawler/tapio_crawler/manifest/store.py`:
- Around line 63-73: Complete the Google-style docstrings at all five sites: in
crawler/tapio_crawler/manifest/store.py:63-73, add an Attributes section
documenting completed_at, complete, and config_fingerprint; in
crawler/tapio_crawler/discovery/runner.py:43-58, add Args and Returns sections
for _config_fingerprint; and in
crawler/tests/discovery/test_discovery_runner.py:317-322, 363-369, and 496-503,
add an Args section documenting store for each affected test function.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a0f99d7-5488-46ac-8f8f-4c06af9f34f6

📥 Commits

Reviewing files that changed from the base of the PR and between e2bc7e4 and 14d395b.

📒 Files selected for processing (3)
  • crawler/tapio_crawler/discovery/runner.py
  • crawler/tapio_crawler/manifest/store.py
  • crawler/tests/discovery/test_discovery_runner.py

Comment thread crawler/tapio_crawler/discovery/runner.py Outdated
Discovery-run caching now includes the site's base_url in the config
fingerprint so a domain change invalidates a cached run within
cache_ttl_hours instead of serving manifest data discovered under the
old origin. Also fills in missing docstring Args/Attributes sections
flagged as nitpicks.
@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
8.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

CI Results

Service Check Result
crawler ruff (lint) ✅ Passed
crawler mypy ✅ Passed
crawler pyrefly ✅ Passed
crawler pytest + coverage ✅ Passed
ingest ruff (lint) ✅ Passed
ingest mypy ✅ Passed
ingest pyrefly ✅ Passed
ingest pytest + coverage ✅ Passed
backend ruff (lint) ✅ Passed
backend mypy ✅ Passed
backend pyrefly ✅ Passed
backend pytest + coverage ✅ Passed
app lint (eslint + prettier) ✅ Passed

All checks passed. 🎉

@brylie
brylie merged commit 7be0d50 into main Aug 7, 2026
6 of 7 checks passed
@brylie
brylie deleted the feature/cache-sitemap-discovery branch August 7, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cache sitemap discovery results to avoid re-fetching on every run

1 participant