Files
scraper/.hermes/plans/scraper-mytheclipse-round2-spec.md
asepharyana 966d86ff67
Deploy Scraper / build-and-deploy (push) Canceled after 0s
refactor: migrate thread/async/queue/scheduler infra to mytheclipse
- bootstrap: TracingLayer from mytheclipse-tracing (composed with scraper
  env filter), RuntimeConfig::auto() thread logging, init job queue + cron.
- proxy_fetch: leader task via ::mytheclipse::spawn_io, gzip decompression
  offloaded to ::mytheclipse::compute (sized rayon pool, panic-isolated),
  bounded by new async fetch limiter (tokio Semaphore bridge).
- queue: new infrastructure/queue module over mytheclipse-queue
  (InMemoryQueue + WorkerPool + BackpressureEnforcer) for repair jobs.
- scheduler: new infrastructure/scheduler.rs using mytheclipse::cron for
  the daily 02:00 UTC image-cache cleanup.
- deps: add mytheclipse-queue + mytheclipse-tracing path deps; mytheclipse
  -> full feature; rayon 1.12; keep path deps for unpublished crates.
- tests: infra_round2 runtime smoke tests (compute panic isolation,
  spawn_io, backpressure admission, cron parse, queue roundtrip).
- Fix: local cache::mytheclipse bridge module shadowed the mytheclipse
  crate name; use leading :: at spawn_io/compute call sites.
2026-08-30 19:40:01 +07:00

6.5 KiB

Spec Round 2 — Scraper → mytheclipse Deep Infra Migration (thread/async/queue)

Date: 2026-08-30 Repo: /home/code/scraper (scraper-service) Library: /home/code/mytheclipse (mytheclipse crates v1.20+ local path)

Scope

Round 1 (committed c7ffd29) migrated retry, redis cache, event bus, ratelimit, config. Round 2 targets the remaining runtime/concurrency/queue/observability infrastructure — the parts the user explicitly asked to push "sampai tingkat thread, async, queue dan lainnya".

1. Runtime thread sizing — mytheclipse::runtime_auto::RuntimeConfig

Current: bootstrap logs std::thread::available_parallelism() manually; tokio runtime is #[tokio::main(flavor = "multi_thread")] with default worker counts.

Change:

  • Add mytheclipse = { features = ["lifecycle", ...] } (add lifecycle feature).
  • In bootstrap/mod.rs, compute RuntimeConfig::auto() once at build, log worker/blocking/compute/io counts, and keep the tokio runtime default (already multi_thread). Optionally pass worker_threads via a #[tokio::main] alternative — but since main uses the macro, we keep the macro form and simply surface the auto-derived counts in logs + store them in AppState for future pool sizing.
  • Replace the manual available_parallelism() log with RuntimeConfig::auto().

2. Async I/O + background task spawns — mytheclipse::spawn_io / spawn_bg

Current:

  • proxy_fetch.rs:114 uses tokio::spawn(...) for the request-coalescing leader
  • 30+ tokio::task::spawn_blocking(...) sites across otakudesu.rs, anime2 use_cases, komik use_cases, proxy_fetch (gzip decompress)
  • No bounded background task pool

Change:

  • proxy_fetch.rs leader task → mytheclipse::spawn_io(...) (same semantics, adds tracing span instrumentation). No behavior change.
  • Add mytheclipse = { features = ["io", "bg"] }.
  • The spawn_blocking sites stay as-is this round (they're CPU-bound parser calls and spawn_blocking is the correct tokio primitive; mytheclipse compute() would replace them but that's a 2k+ LOC parser sweep — deferred as before). Document this in the spec's residual section.
  • Add bg feature and expose a bounded background executor via mytheclipse::spawn_bg for the cache-write / relay-fallback background tasks that currently fire-and-forget (if any are found in use_cases).

3. Worker queue — mytheclipse-queue InMemoryQueue + WorkerPool

Current: no queue abstraction; the only "background" work is the cache batch writes (cache_image_urls_batch_lazy in the proxy image-cache path — but proxy routes were removed, so that path may be dead). Event bus has no worker pool.

Change:

  • Add mytheclipse-queue = { path = ..., features = ["in-memory"] }.
  • Wire a small application-level JobQueue in src/infrastructure/queue/:
    • InMemoryQueue + WorkerPool consuming a scrape:repair topic for background image-cache repair/re-sync jobs.
    • Graceful shutdown: on app shutdown, stop workers.
    • This is additive infra (no existing behavior replaced), so it's low-risk and demonstrates the queue crate in the scraper.
  • Actually use it: the image_cache repair path (cache cleanup at 2 AM) is currently absent (scheduler dir missing). Introduce a small src/infrastructure/scheduler.rs using mytheclipse::cron (lifecycle feature) OR tokio::time::interval for the daily 02:00 cleanup that enqueues repair jobs onto the queue. Given the original CLAUDE.md mentions "scheduler — Cron jobs (daily cache cleanup at 2 AM)", recreating it on mytheclipse-queue is a faithful, additive migration.

4. Backpressure / concurrency — mytheclipse::ConcurrencyLimiter / BackpressureQueue

Current: proxy_fetch has request coalescing via DashMap + broadcast (kept — it's application logic, not infra). No app-level backpressure.

Change:

  • Add mytheclipse = { features = ["traffic"] } (already enabled for RateLimiter in round 1).
  • Wrap the fetch-with-proxy path with a ConcurrencyLimiter (bound concurrent outbound scrapes, default e.g. 16) so burst traffic can't saturate the upstream. This is a real, central hot path (every scrape goes through it).
  • Keep the RateLimiter middleware (round 1) for inbound.

5. Observability — mytheclipse-tracing TracingLayer (+ keep OTel metrics)

Current: tracing_subscriber::fmt().with_env_filter(...) in bootstrap; opentelemetry metrics in observability/metrics.rs (kept — mytheclipse-tracing has no metrics exporter, round-1 decision).

Change:

  • Add mytheclipse-tracing = { path = ..., features = ["env"] }.
  • Replace tracing_subscriber::fmt() in bootstrap with mytheclipse_tracing::TracingLayer::install() (same RUST_LOG env-filter semantics, adds tracing span layer compatibility).
  • Keep init_otel_metrics() untouched (OTel exporter stays).

6. HTTP client — mytheclipse-http HttpClient (optional, re-evaluate)

Round 1 deferred this because mytheclipse-http's client "loses header/UA/status control". Re-check: mytheclipse-http::client::HttpClient — if it exposes with_timeout, get_text, get_json, post_json, and header injection, use it for the non-critical relay fetches (fetch_via_relays). If not header-capable, skip (kept as residual).

Files touched

  • Cargo.toml (features: add lifecycle, io, bg; add mytheclipse-queue, mytheclipse-tracing)
  • src/bootstrap/mod.rs (RuntimeConfig log + TracingLayer + scheduler start)
  • src/infrastructure/scraping/proxy_fetch.rs (spawn_io + ConcurrencyLimiter)
  • src/infrastructure/queue/mod.rs (NEW — InMemoryQueue + WorkerPool + repair topic)
  • src/infrastructure/scheduler.rs (NEW — daily 02:00 cleanup → queue enqueue)
  • src/infrastructure/cache/mytheclipse.rs (maybe wire limiter through cache path)
  • src/presentation/state.rs (add queue handle / limiter if needed)

Verification

  1. cargo check — green after each batch
  2. cargo test — all pass
  3. cargo build --release — green
  4. cargo fmt --check on touched files
  5. Runtime smoke: boot server, hit /health, confirm logs show RuntimeConfig and scheduler started; queue jobs process (test harness)

Residual (deliberately deferred)

  • spawn_blocking → mytheclipse::compute() sweep (2k+ LOC parser files)
  • OTel metrics → mytheclipse-tracing (no metrics exporter in library yet)
  • rayon in komik_parser
  • mytheclipse-http client if header control is insufficient

Commit

Single commit at end: refactor: migrate runtime/queue/concurrency infra to mytheclipse (round 2)