Ubisam-MobileGit/tasks/046-commit-history-load-performance.md
choiminyeong 5caa6057f1 TASK-046: commit history load performance and loading feedback
See tasks/046-commit-history-load-performance.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 13:17:32 +09:00

6.0 KiB

TASK-046: Commit history load performance and loading feedback

Status: implemented; all required checks pass; live-confirmed on the human product owner's device Owner: Claude Reviewer: Codex

Outcome

Live-testing TASK-045's redesign surfaced a real, pre-existing bug: opening the 커밋 (commit history) tab on a real, large company repository (RobotAutomationDevelopment-Dept/LGES-OC5-PtoP-Welder) consistently failed with "서버에 연결할 수 없습니다" (offline error), even though the device had a normal network connection throughout. Root-caused, fixed, and confirmed live with the product owner.

Scope

  • Included: GiteaRepositoryBrowserApi.listCommits now requests stat=false; HttpTransport's shared OkHttp client's readTimeout raised 30s -> 60s as a safety margin; a new EstimatedProgressIndicator (percentage progress bar) replaces the plain spinner on History's three loading states.
  • Excluded: no change to any other API call's stat/file-list behavior (only the commit-list endpoint was touched); no change to commit detail, which legitimately needs per-file data and correctly keeps returning it.

Root cause

Gitea's GET /repos/{owner}/{repo}/commits endpoint computes and returns full per-commit file-change lists and add/remove line stats by default, regardless of what the client asked for. This particular repository's history contains one commit (a vendored packages/ folder checked into git) touching ~5.8 million lines. Computing that commit's stats made the entire request -- even a 5-item page -- take 31+ seconds server-side, tripping the client's then-30s read timeout. Live-measured directly against the real endpoint:

default (stat computed):  31.3s, 678653 bytes
stat=false:                0.4s, 678567 bytes

Note the response size is nearly identical either way -- the cost is server-side compute time, not payload size. GiteaRepositoryBrowserApi.mapCommit only ever reads sha/commit.message/ commit.author.{name,date} from this response; it never reads files or stats. stat=false is therefore a pure win: Gitea skips work the app already threw away, with no loss of data the app used.

Design notes

  • stat=false is the real fix; the timeout bump and progress bar are defense in depth, not the primary fix -- kept anyway since OkHttp's previous 30s ceiling was already documented (see HttpTransport.kt's existing comment) as too tight for other genuinely-slow cases against this same home-NAS-hosted Gitea instance (e.g. very large directory listings), and a percentage indicator is a real UX improvement for any request that legitimately takes a while.
  • The progress bar is a time-based estimate, not a byte-accurate percentage. Confirmed via a live header check that Gitea serves this endpoint Transfer-Encoding: chunked with no Content-Length, so there is no real total-bytes figure to compute a true percentage from. EstimatedProgressIndicator ramps linearly toward a 94% cap over an assumed ~20s typical duration and holds there until the real response arrives (replacing the whole composable with actual content/error) -- it never visually claims 100% before the data is actually in. This trade-off (an honest estimate, not a precise measurement) was not explicitly re-confirmed with the product owner after implementing, since the underlying request became near-instant once stat=false was added, making the estimate's accuracy largely moot in practice.
  • Applied to all three of History's loading states (commit list, commit detail, patch), not just the one that was reported, since all three hit the same class of Gitea host and the fix is a same-shape, low-risk composable swap.

Acceptance criteria

  • listCommits requests stat=false.
  • Live-measured against the real failing repository: request completes in under a second instead of 30+.
  • Loading states show an estimated percentage progress bar instead of a bare spinner.
  • No change to what CommitSummary exposes or how it's built.
  • Build, unit tests (including a new query-parameter assertion), and lint pass.
  • Live-confirmed on the human product owner's device, against the exact repository that originally failed.

Required checks

./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug

Evidence

  • data/repositories/GiteaRepositoryBrowserApi.kt: listCommits adds .addQueryParameter("stat", "false").
  • core/network/HttpTransport.kt: readTimeout 30s -> 60s, comment extended with this task's measured numbers.
  • feature/history/HistoryScreen.kt: new EstimatedProgressIndicator + history_loading_progress string, replacing CircularProgressIndicator() at all three loading call sites (commit list, commit detail, patch).
  • GiteaRepositoryBrowserApiTest.kt: existing listCommits test extended to assert url.queryParameter("stat") == "false".
  • Direct curl measurement against the live server (see Root cause above): 31.3s -> 0.4s, near-identical response size either way.
  • ./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug: BUILD SUCCESSFUL, all unit tests passing, 0 lint errors.
  • Live device: installed over wireless adb. Human product owner navigated to the previously-failing repository's commit tab themselves and confirmed it now works well ("내가 확인했어").

Known limitations

  • The progress percentage is a time-based estimate (see Design notes), not derived from real bytes transferred -- inherent to Gitea serving this endpoint as chunked with no Content-Length.
  • Only listCommits (the list endpoint) was changed. If a future repository has a similarly huge commit and a user opens that commit's detail view, the detail fetch still legitimately needs and requests full file/stat data, and could be slow for the same underlying reason -- not fixable the same way, since that view's entire purpose is showing the file changes.

Review findings

None yet -- pending Codex/human review.