Skip to content

refactor(validator): share one MirrorClient across the OSS scoring round#1562

Closed
ebios-star wants to merge 1 commit into
entrius:testfrom
ebios-star:refactor/share-mirror-client-across-scoring-round
Closed

refactor(validator): share one MirrorClient across the OSS scoring round#1562
ebios-star wants to merge 1 commit into
entrius:testfrom
ebios-star:refactor/share-mirror-client-across-scoring-round

Conversation

@ebios-star

Copy link
Copy Markdown
Contributor

Summary

get_rewards opened a fresh MirrorClient — and therefore a brand-new requests.Session — for every UID inside the per-miner loop:

# evaluate_miners_pull_requests, called once per uid in get_rewards
with MirrorClient() as mirror_client:
    await asyncio.to_thread(load_miner_prs, ..., client=mirror_client)
    await score_miner_prs(..., client=mirror_client)

So a full scoring round builds and tears down one HTTPS session per miner against mirror.gittensor.io, discarding the connection pool between miners and re-doing the TLS handshake on every iteration. Connection reuse also matters under the mirror's Cloudflare rate limit (50 req / 10s / IP).

This PR hoists a single MirrorClient to the round level in get_rewards and threads it through evaluate_miners_pull_requests. This mirrors the pattern the issue-discovery path already uses — it builds one client and passes it through every miner (issue_discovery/scan.py:156). The OSS-scoring path is now consistent with it.

evaluate_miners_pull_requests gains an optional mirror_client parameter and only owns/closes a client when it created one itself; a caller-supplied client stays open for the next miner. Standalone and test calls (which pass no client) keep their previous create-and-close behavior, so there is no new session leak on that path.

Why it's behavior-preserving

MirrorClient holds no per-miner or per-instance request state — only the pooled requests.Session (utils/mirror/client.py). Sharing one instance across miners changes nothing about request content, retries, rate-limit handling, or scoring; it only reuses the connection pool instead of recreating it ~N times per round.

Related Issues

None — this is a latent inefficiency found by inspection, not a tracked issue.

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other (describe below)

Testing

  • Tests added/updated
  • Manually tested

Added two tests in tests/validator/oss_contributions/mirror/test_routing.py pinning the client-ownership contract:

  • test_injected_client_is_reused_not_reconstructed — an injected client is threaded into load_miner_prs/score_miner_prs, no new MirrorClient is constructed, and it is not closed by the callee.
  • test_standalone_call_owns_and_closes_its_client — with no injected client, exactly one is constructed and closed (no leak).

Full suite: 958 passed. ruff check, ruff format --check, and pyright all clean.

No CLI output is affected by this change.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Changes are documented (if applicable)

get_rewards opened a fresh MirrorClient (hence a new requests.Session) for
every UID inside the per-miner loop, throwing away the mirror.gittensor.io
connection pool between miners and re-handshaking TLS on each iteration.

Hoist a single MirrorClient to the round level and thread it through
evaluate_miners_pull_requests, matching how issue discovery already passes
one client through every miner (scan.py). evaluate_miners_pull_requests now
takes an optional mirror_client and only owns/closes a client when it
created one itself, so standalone/test calls keep their previous behavior.

Behavior-preserving: MirrorClient holds no per-miner state, so sharing the
instance only reuses the connection pool. Adds two tests pinning the client
ownership contract (injected client reused, standalone client closed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ebios-star

Copy link
Copy Markdown
Contributor Author

@anderdc @LandynDev whenever you have a moment — small, behavior-preserving refactor. get_rewards was constructing a new MirrorClient/requests.Session per UID inside the per-miner loop; this hoists a single client to the round level and threads it through evaluate_miners_pull_requests, the same way issue_discovery/scan.py already passes one client through every miner. The callee only closes a client it created itself, so standalone/test calls are unchanged. No scoring math or CLI output touched. Full suite green (958 passed); ruff + pyright clean. Happy to adjust naming/scope.

@xiao-xiao-mao xiao-xiao-mao Bot added the refactor Code restructuring without behavior change label Jun 29, 2026
@anderdc anderdc closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code restructuring without behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants