All skills
simota avatar

/trawl

@95ba0d9
by shingo imotasimota/agent-skills85 stars
15

Architecting crawl and scraping systems: distributed crawler topology, URL frontier, politeness, compliance. Architecture-only. Not for single-page scraping (Vector) or ETL pipelines (Stream).

Use this Skill: https://skilld.dev/gh/simota/agent-skills/trawl

This session only. Nothing lands on disk.

referenceobservability.md

≈2.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Crawl Observability Design

Overview

Crawl observability ensures the system is operating correctly, efficiently, and within compliance bounds. It covers metrics collection, dashboards, alerting, cost modeling, and graceful shutdown monitoring.

Prometheus Metrics

Core Metrics

Metric Type Labels Unit Description
crawler_urls_fetched_total Counter domain, status_code, worker_id count Total URLs fetched
crawler_urls_discovered_total Counter source (link/sitemap/seed) count Total URLs added to frontier
crawler_frontier_depth Gauge priority_level count Current pending URLs in frontier
crawler_frontier_breadth Gauge — count Number of distinct domains in frontier
crawler_fetch_duration_seconds Histogram domain, content_type seconds Fetch latency distribution
crawler_fetch_bytes_total Counter domain, content_type bytes Total bytes downloaded
crawler_error_total Counter domain, error_category count Fetch errors by category
crawler_extraction_duration_seconds Histogram parser_type seconds Extraction pipeline latency
crawler_dedup_hit_total Counter — count URLs rejected by dedup (seen-set hit)
crawler_compliance_block_total Counter signal_type (robots / x-robots-tag / tdmrep / ai.txt / c2pa / tos / pay-per-crawl-402) count URLs blocked by compliance
crawler_pay_per_crawl_402_total Counter domain, outcome (accepted/declined) count Cloudflare 402 Pay-Per-Crawl events
crawler_pay_per_crawl_charged_cents Counter domain cents Cumulative paid-crawl spend
crawler_worker_active Gauge worker_id count Currently active workers
crawler_checkpoint_age_seconds Gauge — seconds Time since last frontier checkpoint

Derived Metrics (Recording Rules)

# Crawl throughput (URLs/sec, 5-minute average)
- record: crawler:throughput_5m
  expr: rate(crawler_urls_fetched_total[5m])

# Error rate by category (percentage)
- record: crawler:error_rate_4xx
  expr: rate(crawler_error_total{error_category="4xx"}[5m]) / rate(crawler_urls_fetched_total[5m])

- record: crawler:error_rate_5xx
  expr: rate(crawler_error_total{error_category="5xx"}[5m]) / rate(crawler_urls_fetched_total[5m])

# Cost per URL (derived from egress and compute)
- record: crawler:cost_per_url
  expr: (crawler_fetch_bytes_total * 0.00000001 + crawler_fetch_duration_seconds * 0.00005) / crawler_urls_fetched_total

# Frontier drain rate (URLs consumed per second)
- record: crawler:frontier_drain_rate
  expr: -deriv(crawler_frontier_depth[10m])

Error Category Labels

error_category HTTP Status / Condition Action
4xx 400-499 (client error) Skip URL, do not retry (except 429)
429 Too Many Requests Back off, reduce domain rate
5xx 500-599 (server error) Retry up to 3x with exponential backoff
network DNS failure, timeout, connection reset Retry up to 3x, then mark domain degraded
compliance robots.txt block, opt-out signal Skip URL, log compliance decision
redirect_loop > 5 redirects in chain Skip URL, log redirect chain
content_error Parse failure, encoding error Log for manual review

Grafana Dashboard Design

Dashboard Layout (4 rows)

Row 1: Overview
┌──────────────┬──────────────┬──────────────┬──────────────┐
│ URLs/sec     │ Total Fetched│ Frontier     │ Active       │
│ (gauge)      │ (counter)    │ Depth (gauge)│ Workers      │
└──────────────┴──────────────┴──────────────┴──────────────┘

Row 2: Performance
┌──────────────────────────┬──────────────────────────────┐
│ Fetch Latency (p50/p95)  │ Throughput by Domain (top 10)│
│ (heatmap)                │ (time series)                │
└──────────────────────────┴──────────────────────────────┘

Row 3: Errors & Compliance
┌──────────────────────────┬──────────────────────────────┐
│ Error Rate by Category   │ Compliance Blocks            │
│ (stacked area)           │ (by signal type, pie)        │
└──────────────────────────┴──────────────────────────────┘

Row 4: Cost & Health
┌──────────────────────────┬──────────────────────────────┐
│ Cost per URL (time series)│ Checkpoint Age (gauge)      │
│ (line, with budget line) │ (single stat, red > 5 min)  │
└──────────────────────────┴──────────────────────────────┘

Alert Thresholds

Critical Alerts (Page Immediately)

Alert Condition Duration Action
Frontier exhaustion crawler_frontier_depth < 100 5 min Crawl is about to stop — investigate seed injection or scope limits
Checkpoint stale crawler_checkpoint_age_seconds > 600 1 min Frontier state may be lost on crash — investigate persistence layer
All workers down crawler_worker_active == 0 2 min Complete crawl failure — check coordinator and infrastructure
Error rate spike crawler:error_rate_5xx > 0.10 5 min 10%+ of fetches failing — check target site health or rate limiting

Warning Alerts (Notify)

Alert Condition Duration Action
Throughput drop crawler:throughput_5m < 0.5 * avg_over_time(crawler:throughput_5m[1h]) 15 min Throughput dropped 50%+ — check politeness limits or proxy health
Frontier backlog crawler_frontier_depth > 10000000 30 min 10M+ pending URLs — may need more workers or scope tightening
High 4xx rate crawler:error_rate_4xx > 0.05 10 min 5%+ client errors — check URL canonicalization or stale URLs
Cost overshoot crawler:cost_per_url > budget_per_url * 1.5 1 hour Cost 50%+ over budget — review proxy costs or egress optimization
Rate limit hits rate(crawler_error_total{error_category="429"}[5m]) > 0.5 5 min Being rate-limited — reduce per-domain crawl rate

Cost-per-URL Modeling

Component Breakdown

Component Cost Model Typical Range
Compute vCPU-seconds × $/vCPU-second $0.00001-0.0001 per URL
Network egress bytes × $/GB (cloud egress pricing) $0.00005-0.001 per URL
Proxy requests × $/request (or GB × $/GB) $0.0001-0.01 per URL
Storage bytes × $/GB-month (output storage) $0.000001-0.00001 per URL
Queue/Redis operations × $/operation $0.000001-0.0001 per URL

Estimation Formula

cost_per_url = compute_cost + egress_cost + proxy_cost + storage_cost + queue_cost

Where:
  compute_cost = avg_fetch_duration_sec × vcpu_cost_per_sec
  egress_cost  = avg_page_size_bytes × egress_cost_per_byte
  proxy_cost   = proxy_cost_per_request (if using proxy)
  storage_cost = avg_output_size_bytes × storage_cost_per_byte_month × retention_months
  queue_cost   = 3 × queue_operation_cost (enqueue + dequeue + dedup check)

Example (Medium tier, cloud-hosted, residential proxy):
  compute:  0.5s × $0.00004/vCPU-s  = $0.00002
  egress:   200KB × $0.09/GB         = $0.000018
  proxy:    $0.005/request            = $0.005
  storage:  50KB × $0.023/GB-mo × 3  = $0.0000035
  queue:    3 × $0.0000004            = $0.0000012
  ────────────────────────────────────────────
  Total:                              ≈ $0.0051/URL

Without proxy:                        ≈ $0.000042/URL

Budget Planning Table

Scale Tier URLs/day Cost/URL (no proxy) Cost/URL (proxy) Daily Cost Range
Small 10K $0.00004 $0.005 $0.40 - $50
Medium 500K $0.00003 $0.003 $15 - $1,500
Large 10M $0.00002 $0.002 $200 - $20,000
Web-scale 100M $0.00001 N/A (own infra) $1,000+

Graceful Shutdown Observability

Shutdown Metrics

Shutdown sequence monitoring:
1. SIGTERM received → log timestamp
2. crawler_shutdown_initiated (gauge = 1)
3. crawler_inflight_requests (gauge, draining toward 0)
4. crawler_frontier_flush_status (0=pending, 1=complete)
5. crawler_kafka_offsets_committed (0=pending, 1=complete)
6. crawler_shutdown_complete (gauge = 1)
7. Process exit

Monitor: time between step 1 and step 6 should be < 30 seconds.
Alert if: crawler_inflight_requests > 0 after 30 seconds of shutdown.

Resume Verification

After restart, verify:
1. crawler_frontier_depth matches pre-shutdown value (±1%)
2. No duplicate fetches in first 5 minutes (check dedup hit rate)
3. Throughput reaches 80% of pre-shutdown level within 10 minutes
4. All workers reconnect within 2 minutes

SLO/SLI Alignment with Beacon

Trawl's observability design produces the inputs that Beacon uses for alerting. The mapping:

Trawl Metric Beacon SLI SLO Target
crawler:throughput_5m Crawl throughput ≥ configured URLs/sec
1 - crawler:error_rate_5xx Fetch success rate ≥ 99%
max(content_age) Content freshness < freshness SLO
crawler_frontier_depth Frontier health 100 < depth < 10M
crawler:cost_per_url Cost efficiency < budget per URL
crawler_compliance_block_total{signal_type="pay-per-crawl-402"} Pay-Per-Crawl budget guard within monthly cap
concurrent_per_target (cross-IP) DDoS-equivalent prevention ≤ 10 (post-Trilegangers default)

Instrument with OpenTelemetry SDK (Traces / Metrics / Logs all Stable in early 2026; Profiling signal at RC). Use OTLP exporter to Tempo/Jaeger (traces), Prometheus/Mimir (metrics), Loki/SigNoz (logs).

Source: SKILL.md on GitHub

2 warnings1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    The 'trawl' skill is a comprehensive architecture tool for designing web crawling and scraping systems. It provides deep technical guidance on crawler topology, URL frontier management, and legal compliance without generating any executable code. No security risks or malicious patterns were identified.

  • Socket1mo

    1 alert: gptSecurity

  • Snyk1mo

    Risk: MEDIUM · 1 issue

Signed by skilld at 95ba0d9. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated last month

README badge

README badge for simota/agent-skills/trawl