Mengekstrak ratusan riwayat commit, diff kode, atau mengirim chunk kode ke LLM gateway melalui automated CLI sering kali gagal di tengah jalan. Masalah utamanya bukan sekadar kuota request per jam habis, melainkan terpicunya secondary rate limit (abuse detection mechanism) akibat lonjakan request konkuren yang agresif.

Panduan ini membahas arsitektur penanganan rate limit pada Git CLI: membaca response header secara presisi, mengimplementasikan exponential backoff dengan full jitter, membatasi konkurensi request lokal, serta mengamankan Personal Access Token (PAT) menggunakan native OS keychain.

Akar Masalah: Primary vs Secondary Rate Limit

REST API GitHub membedakan batas konsumsi menjadi dua layer:

  • Primary Rate Limit: Kuota tetap berbasis akun (misal: 5.000 request per jam untuk user terotentikasi). Status kuota ini tercatat pada header x-ratelimit-remaining dan reset periodik di x-ratelimit-reset.
  • Secondary Rate Limit: Mekanisme perlindungan infrastruktur terhadap Denial of Service (DoS) internal. Limit ini dipicu jika klien mengirimkan request serial terlalu cepat tanpa jeda, membuat lebih dari 100 request konkuren sekaligus, atau mengeksekusi operasi CPU-intensive (seperti kalkulasi Git diff besar atau search code) secara masif. Respons menghasilkan status HTTP 403 Forbidden atau 429 Too Many Requests.

Pada pipeline analisis kode yang mengintegrasikan LLM gateway (seperti OpenAI atau Anthropic), secondary rate limit juga terjadi di layer AI provider melalui pembatasan RPM (Requests Per Minute) dan TPM (Tokens Per Minute).

Parsing Header: Retry-After dan x-ratelimit-reset

Ketika klien terkena throttle, server mengirimkan sinyal waktu tunggu melalui response header. Kegagalan membaca header ini menyebabkan retry loop prematur yang berujung pada pemblokiran IP sementara.

  • Retry-After: Durasi waktu tunggu dalam hitungan detik (integer). Sering muncul pada status HTTP 429.
  • x-ratelimit-reset: Timestamp waktu Unix epoch (UTC dalam detik) kapan kuota dipulihkan. Umum pada status HTTP 403 akibat exhaustion.

Implementasi Exponential Backoff dengan Full Jitter

Retry konstan atau sleep berbasis interval statis menyebabkan thundering herd problem: ketika limit terbuka, seluruh thread klien mengirim request serentak, langsung memicu secondary rate limit berikutnya. Solusi standarnya adalah Exponential Backoff dengan Full Jitter.

Berikut implementasi client HTTP resilient menggunakan Python:

import time
import random
import requests

def request_with_backoff(url, headers, max_retries=5, base_delay=1.0, max_delay=60.0):
    attempt = 0
    while attempt < max_retries:
        response = requests.get(url, headers=headers)
        
        # Sukses
        if response.status_code == 200:
            return response.json()

        # Handle rate limiting: 429 Too Many Requests atau 403 Forbidden (secondary limit)
        if response.status_code in (429, 403):
            retry_after = response.headers.get("Retry-After")
            reset_time = response.headers.get("x-ratelimit-reset")

            if retry_after:
                sleep_duration = float(retry_after)
            elif reset_time:
                # Hitung selisih epoch time dengan waktu sekarang
                now = time.time()
                sleep_duration = max(0.0, float(reset_time) - now)
            else:
                # Exponential backoff: base * 2^attempt
                backoff_limit = min(max_delay, base_delay * (2 ** attempt))
                # Full jitter: pilih angka acak antara 0 dan backoff_limit
                sleep_duration = random.uniform(0, backoff_limit)

            # Tambahkan buffer minimal agar tidak mendahului kalkulasi server
            sleep_duration += random.uniform(0.1, 0.5)
            time.sleep(sleep_duration)
            attempt += 1
            continue

        # Error non-transient, jangan retry
        response.raise_for_status()

    raise RuntimeError(f"Request gagal setelah {max_retries} percobaan karena rate limit.")

Batching dan Concurrency Control di Level CLI

Jangan pernah memicu request HTTP untuk setiap commit individual langsung ke remote API. Terapkan strategi hierarki data:

  1. Ekstraksi Lokal Terlebih Dahulu: Manfaatkan binary Git lokal untuk mengambil metadata riwayat. Jalankan git log --format atau git rev-list secara lokal untuk memfilter commit hash yang benar-benar membutuhkan data tambahan.
  2. Semaphore / Worker Pool: Batasi konkurensi request. Jika menganalisis 1.000 commit diff, gunakan concurrency pool terbatas (misalnya, maksimum 4-8 worker konkuren untuk GitHub API, atau 2-3 worker jika berhadapan dengan LLM parser).
  3. Bulk Payload untuk LLM: Daripada mengirim 1 file per 1 request LLM, kelompokkan potongan diff kecil ke dalam satu konteks prompt selama tidak melampaui context window dan batas TPM.

Penyimpanan Kredensial Aman: Native OS Keychain

Menyimpan GitHub PAT atau LLM API Key di file plain text seperti ~/.gittoolrc, config.json, atau .env berisiko tinggi terhadap credential leak via unauthorized read atau backup repositori tidak sengaja.

Manfaatkan API native credential store sistem operasi:

  • macOS: Apple Keychain Services via command line security.
  • Linux: FreeDesktop Secret Service API via secret-tool atau backend GNOME Keyring / KWallet.
  • Windows: Windows Credential Manager via cmdkey atau Win32 Credential API.

Contoh integrasi abstraksi keychain di CLI tool menggunakan modul Python keyring:

import keyring

SERVICE_NAME = "git_cli_analyzer"
ACCOUNT_NAME = "github_pat"

def store_pat(token: str):
    # Menyimpan langsung ke OS Keychain yang terenkripsi
    keyring.set_password(SERVICE_NAME, ACCOUNT_NAME, token)

def get_pat() -> str:
    token = keyring.get_password(SERVICE_NAME, ACCOUNT_NAME)
    if not token:
        raise ValueError("PAT tidak ditemukan di secure credential store. Jalankan setup auth.")
    return token

Rekomendasi Alternatif: Jika CLI dibangun dengan Bash/Go dan tidak ingin menyertakan dependensi wrapper pihak ketiga, panggil sub-command native Git: git credential fill dan git credential approve. Pendekatan ini otomatis mewarisi credential helper yang sudah dikonfigurasi pengguna (seperti GCM - Git Credential Manager).

Checklist Arsitektur Tangguh

  • Interseptor HTTP memeriksa status 403 dan membaca pesan spesifik body (seperti "You have triggered an abuse detection mechanism").
  • Prioritaskan parsing Retry-After, fallback ke x-ratelimit-reset, lalu fallback ke calculated exponential backoff.
  • Full Jitter aktif untuk menghindari sinkronisasi retry antar-proses.
  • Request paralel dikendalikan oleh local bounded queue/semaphore, bukan unconstrained background task.
  • Token otentikasi disimpan di hardware/OS-encrypted keychain.