gitmote

gitmote / docs / architecture / request-flows.md  ·  history  ·  branches

4303 bytes · raw

Request flows

Part of the gitmote architecture.

Clone / fetch (read path)

No lock. Refs come from s3lite; the object closure to serve is hydrated from S3.

sequenceDiagram
    participant C as git client
    participant G as gitmote
    participant D as s3lite
    participant S as S3
    C->>G: GET /{repo}/info/refs?service=git-upload-pack
    G->>D: authorize (read on repo)
    G->>D: SELECT name, sha FROM refs WHERE repo_id=?
    G->>G: materialize refs into working repo
    G-->>C: advertise refs
    C->>G: POST /{repo}/git-upload-pack (wants/haves)
    G->>S: hydrate the object closure to serve
    G->>G: git upload-pack (negotiate + build packfile)
    G-->>C: packfile

Push (write path) — the CAS

Serialized by a per-repo in-process mutex; the safety-critical ordering is objects durable in S3 first, ref CAS in s3lite second. The catch that shapes everything below: git receive-pack updates refs and acknowledges the client itself, at the end of its run — so the durable commit cannot happen after it returns (the ack has already gone out). It must gate inside receive-pack's lifecycle, at its one designed seam: the pre-receive hook.

sequenceDiagram
    participant C as git client
    participant R as receive-pack + hook
    participant G as gitmote (sole writer)
    participant S as S3
    participant D as s3lite
    C->>G: POST /{repo}/git-receive-pack (packfile + ref commands)
    G->>D: authorize (write on repo)
    G->>G: acquire per-repo write lock
    G->>D: read ref tips → materialize working repo
    G->>R: spawn receive-pack, pipe request body
    R->>R: index pack to quarantine · enforce fast-forward
    R->>G: pre-receive RPC (ref commands + quarantine path)
    G->>S: PUT quarantined objects — content before pointer
    G->>D: BEGIN · per-ref CAS (WHERE sha=:expected) · COMMIT
    Note over G,D: any mismatch ⇒ ROLLBACK ⇒ reject
    G-->>R: verdict (ok / reject)
    R->>R: ok ⇒ migrate quarantine + update local refs · else ⇒ discard
    R-->>G: report-status
    G-->>C: response (per-ref status)
    G->>G: release lock

Why it looks like this: