Skip to content

perf(cli): read a single on-chain issue by ID instead of scanning all#1390

Open
kinjitakabe wants to merge 1 commit into
entrius:testfrom
kinjitakabe:perf/cli-single-issue-contract-read
Open

perf(cli): read a single on-chain issue by ID instead of scanning all#1390
kinjitakabe wants to merge 1 commit into
entrius:testfrom
kinjitakabe:perf/cli-single-issue-contract-read

Conversation

@kinjitakabe
Copy link
Copy Markdown

Why this is necessary: fetch_issue_from_contract is used to act on one issue by ID (e.g. gitt issues submissions --id <N>), but it currently reads every issue registered on the contract to find it:

issues = read_issues_from_contract(...)          # one childstate RPC per issue, 1..next_issue_id
issue = next((i for i in issues if i.get('id') == issue_id), None)

That's O(N) substrate RPC round-trips to fetch O(1) data. next_issue_id only ever grows — completed and cancelled issues are never removed from storage — so every single-issue command gets progressively slower and pounds the node with N round-trips for data whose storage key it can compute directly. The Ink! 5 lazy mapping key for a specific issue_id is already computable inline, so a single read was always possible; the code just wasn't using it.

The fix: add read_issue_from_contract, which computes the one issue's lazy key and does a single childstate_getStorage call, and route fetch_issue_from_contract through it. O(N) → O(1).

The per-issue decode is factored into a shared _read_one_issue_from_child_storage helper, so the full-scan path (read_issues_from_contract, used by issues list) and the new single-read path share one decode implementation instead of duplicating it. Behavior is unchanged: fetch_issue_from_contract still raises the same ClickExceptions for not-found, non-bountied, and incomplete issues.

Related Issues

N/A

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other (performance)

Testing

  • New tests/cli/test_issue_single_read.py: single read does exactly one RPC,
    returns None when absent, fetch_issue_from_contract uses the single read
    (asserts the full scan is not called), and the not-found / non-bountied
    paths still raise ClickException.
  • uv run pytest — 882 passed.
  • uv run pyright / ruff check / vulture on changed files — clean.

Checklist

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

fetch_issue_from_contract resolved one issue by reading every issue on the contract (one childstate RPC per issue, growing with next_issue_id). Add read_issue_from_contract to fetch a single issue via its computed lazy key in one RPC, and route fetch_issue_from_contract through it. The per-issue decode is shared via _read_one_issue_from_child_storage so the full-scan and single-read paths don't duplicate it.
@xiao-xiao-mao xiao-xiao-mao Bot added the enhancement New feature or request label May 28, 2026
@anderdc anderdc added refactor Code restructuring without behavior change and removed enhancement New feature or request labels May 29, 2026
@anderdc
Copy link
Copy Markdown
Collaborator

anderdc commented May 29, 2026

fix conflicts

@anderdc
Copy link
Copy Markdown
Collaborator

anderdc commented May 29, 2026

Fix conflicts. Heads up: test now distinguishes a contract read error (ClickException "Error reading from contract: …") from a genuine not-found in fetch_issue_from_contract. read_issue_from_contract here swallows exceptions and returns None, which would report connection failures as "Issue ID N not found on-chain." Preserve that distinction when you rebase.

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