probe: measure the GitCode upload path (TEMPORARY — do not merge) #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: probe-gitcode-upload | |
| # TEMPORARY diagnostic workflow — delete with this branch. | |
| # | |
| # WHY. `publish-ecosystem`'s GitCode leg has now blown its 180s per-asset cap | |
| # on four separate releases (0.0.94 / 0.0.97 / 0.0.105 / 2026.7.28.2), each | |
| # time on the two biggest tarballs, each time costing ~5min of manual | |
| # re-upload. Every previous fix was a guess about WHY it is slow (raise the | |
| # cap, skip-don't-retry, batch verify). This workflow measures instead. | |
| # | |
| # Each job isolates ONE hypothesis, and they run in parallel so a single push | |
| # answers all of them: | |
| # | |
| # baseline Is it the network, the direction, or GitCode specifically? | |
| # Down/up control against GitHub from the same runner. | |
| # sizes-urllib Does throughput collapse with size, or is it ~constant? | |
| # (constant MB/s => a pure bandwidth wall, not a stall.) | |
| # sizes-curl Is Python's read-it-all-then-PUT the bottleneck, or the wire? | |
| # concurrency Is bandwidth per-connection or per-host? If per-connection, | |
| # uploading assets in parallel is the whole fix — mirror_res.sh | |
| # currently uploads them SERIALLY within a host leg. | |
| # | |
| # Every job writes NDJSON to the step summary so results are comparable | |
| # without opening logs. | |
| on: | |
| push: | |
| branches: [ 'probe/**' ] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: probe-gitcode-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| GTC_REPO: xlings-res/mcpp | |
| # A real, already-mirrored asset used as the download reference (34.8MB). | |
| REF_ASSET: mcpp-2026.7.28.2-linux-x86_64.tar.gz | |
| REF_TAG: 2026.7.28.2 | |
| jobs: | |
| # ── H0: characterise the pipe itself, both directions, both hosts ──────── | |
| baseline: | |
| name: baseline — network + GitHub control | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| env: | |
| GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} | |
| GH_TOKEN: ${{ secrets.XLINGS_RES_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Runner egress identity | |
| run: | | |
| echo "runner public IP / region:" | |
| curl -s --max-time 20 https://ipinfo.io/json || echo "(ipinfo unavailable)" | |
| - name: Connect/TLS timing to each host | |
| run: | | |
| fmt=' dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s ttfb=%{time_starttransfer}s total=%{time_total}s code=%{http_code}\n' | |
| for h in https://api.gitcode.com https://gitcode.com https://api.github.com https://github.com; do | |
| echo "$h" | |
| curl -sS -o /dev/null --max-time 60 -w "$fmt" "$h" || echo " (failed)" | |
| done | |
| - name: Download throughput — gitcode vs github (same 34.8MB asset) | |
| run: | | |
| fmt=' code=%{http_code} bytes=%{size_download} speed=%{speed_download} B/s total=%{time_total}s\n' | |
| echo "gitcode.com:" | |
| curl -sSL -o /dev/null --max-time 900 -w "$fmt" \ | |
| "https://gitcode.com/${GTC_REPO}/releases/download/${REF_TAG}/${REF_ASSET}" || true | |
| echo "github.com:" | |
| curl -sSL -o /dev/null --max-time 900 -w "$fmt" \ | |
| "https://github.com/${GTC_REPO}/releases/download/${REF_TAG}/${REF_ASSET}" || true | |
| - name: Upload control — 32MB to GitHub from this same runner | |
| run: | | |
| set -x | |
| head -c 33554432 /dev/urandom > probe-github-32m.bin | |
| gh release view "probe-${{ github.run_id }}" -R "$GTC_REPO" >/dev/null 2>&1 \ | |
| || gh release create "probe-${{ github.run_id }}" -R "$GTC_REPO" \ | |
| --title "probe ${{ github.run_id }}" --notes "temporary upload probe; safe to delete" | |
| start=$SECONDS | |
| gh release upload "probe-${{ github.run_id }}" probe-github-32m.bin -R "$GTC_REPO" --clobber | |
| echo "GITHUB_UPLOAD_32MB_SECONDS=$((SECONDS - start))" | |
| # ── H1: is throughput size-dependent (stall) or flat (bandwidth wall)? ─── | |
| sizes-urllib: | |
| name: sizes — current urllib transport | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| env: | |
| GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} | |
| TAG: probe-${{ github.run_id }}-urllib | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Create probe release | |
| run: python3 .github/tools/gtc release create "$GTC_REPO" --tag "$TAG" --name "$TAG" | |
| - name: Upload 1 / 4 / 16 / 32 / 32 MB (serial) | |
| run: | | |
| # 32MB twice: run-to-run variance matters as much as the mean when | |
| # deciding whether a fixed 180s cap can ever be safe. | |
| for spec in 1 4 16 32 32; do | |
| f="probe-urllib-${spec}m-$RANDOM.bin" | |
| head -c $((spec * 1048576)) /dev/urandom > "$f" | |
| python3 .github/tools/probe_gtc_upload.py \ | |
| --repo "$GTC_REPO" --tag "$TAG" --file "$f" \ | |
| --method urllib --label "urllib-${spec}MB" >> results.ndjson | |
| rm -f "$f" | |
| done | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { echo '### sizes — urllib'; echo '```json'; cat results.ndjson; echo '```'; } \ | |
| >> "$GITHUB_STEP_SUMMARY" | |
| # ── H2: is the Python client the bottleneck, or the wire? ──────────────── | |
| sizes-curl: | |
| name: sizes — curl streaming transport | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| env: | |
| GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} | |
| TAG: probe-${{ github.run_id }}-curl | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Create probe release | |
| run: python3 .github/tools/gtc release create "$GTC_REPO" --tag "$TAG" --name "$TAG" | |
| - name: Upload 1 / 4 / 16 / 32 / 32 MB (serial) | |
| run: | | |
| for spec in 1 4 16 32 32; do | |
| f="probe-curl-${spec}m-$RANDOM.bin" | |
| head -c $((spec * 1048576)) /dev/urandom > "$f" | |
| python3 .github/tools/probe_gtc_upload.py \ | |
| --repo "$GTC_REPO" --tag "$TAG" --file "$f" \ | |
| --method curl --label "curl-${spec}MB" >> results.ndjson | |
| rm -f "$f" | |
| done | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { echo '### sizes — curl'; echo '```json'; cat results.ndjson; echo '```'; } \ | |
| >> "$GITHUB_STEP_SUMMARY" | |
| # ── H3: per-connection cap or per-host cap? This decides whether simply | |
| # parallelising mirror_res.sh's serial per-asset loop is the fix. ── | |
| concurrency: | |
| name: concurrency — 4x8MB serial vs parallel | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| env: | |
| GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} | |
| TAG: probe-${{ github.run_id }}-conc | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Create probe release | |
| run: python3 .github/tools/gtc release create "$GTC_REPO" --tag "$TAG" --name "$TAG" | |
| - name: Serial 4x8MB | |
| run: | | |
| for i in 1 2 3 4; do head -c 8388608 /dev/urandom > "ser-$i.bin"; done | |
| start=$SECONDS | |
| for i in 1 2 3 4; do | |
| python3 .github/tools/probe_gtc_upload.py --repo "$GTC_REPO" --tag "$TAG" \ | |
| --file "ser-$i.bin" --method curl --label "serial-$i" >> results.ndjson | |
| done | |
| echo "SERIAL_WALL_SECONDS=$((SECONDS - start))" | tee -a wall.txt | |
| - name: Parallel 4x8MB | |
| run: | | |
| for i in 1 2 3 4; do head -c 8388608 /dev/urandom > "par-$i.bin"; done | |
| start=$SECONDS | |
| for i in 1 2 3 4; do | |
| python3 .github/tools/probe_gtc_upload.py --repo "$GTC_REPO" --tag "$TAG" \ | |
| --file "par-$i.bin" --method curl --label "parallel-$i" >> "par-$i.json" & | |
| done | |
| wait | |
| echo "PARALLEL_WALL_SECONDS=$((SECONDS - start))" | tee -a wall.txt | |
| cat par-*.json >> results.ndjson | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { echo '### concurrency'; echo '```'; cat wall.txt; echo '```'; | |
| echo '```json'; cat results.ndjson; echo '```'; } >> "$GITHUB_STEP_SUMMARY" | |
| # ── Leave no garbage on the resource repo ─────────────────────────────── | |
| cleanup: | |
| name: cleanup probe tags | |
| needs: [baseline, sizes-urllib, sizes-curl, concurrency] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| env: | |
| GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} | |
| GH_TOKEN: ${{ secrets.XLINGS_RES_TOKEN }} | |
| steps: | |
| - name: Delete GitCode probe tags (deleting the tag deletes the release) | |
| run: | | |
| for t in "probe-${{ github.run_id }}-urllib" \ | |
| "probe-${{ github.run_id }}-curl" \ | |
| "probe-${{ github.run_id }}-conc"; do | |
| code=$(curl -sS -o /dev/null -w '%{http_code}' -X DELETE \ | |
| -H "PRIVATE-TOKEN: $GITCODE_TOKEN" \ | |
| "https://api.gitcode.com/api/v5/repos/${GTC_REPO}/tags/${t}" || echo ERR) | |
| echo "delete $t -> $code" | |
| done | |
| - name: Delete GitHub probe release | |
| run: | | |
| gh release delete "probe-${{ github.run_id }}" -R "$GTC_REPO" --yes --cleanup-tag \ | |
| || echo "(nothing to delete)" |