perf(sync): reuse the TLS session and skip the pull's needless fetch

Two halves of the connection-cost lever (first on-device pull: 9.7s
fetch to learn 'up to date'; a rejected-push :sync runs three full
handshakes):

- esp_mbedtls_stream.c caches the mbedTLS session at stream close and
  offers it on the next connect to the same host — the server then
  skips the certificate exchange and most of the key exchange. Second
  vendor delta, single git thread, best-effort (any refusal falls back
  to a full handshake).

- pull_once now ls-refs first: the ref advertisement alone answers
  'anything new?', so up-to-date and local-ahead return without ever
  entering pack negotiation, and a needed download rides the SAME open
  connection instead of a second handshake. Tracking-ref updates share
  one update_tracking helper with the reconcile fetch.
This commit is contained in:
Julien Calixte
2026-07-14 09:21:58 +02:00
parent 75166f74cf
commit 44c65e9ea5
2 changed files with 116 additions and 19 deletions

View File

@@ -743,14 +743,22 @@ fn fetch_origin(repo: &Repository, branch: &str) -> Result<Oid> {
.peel_to_commit()
.context("FETCH_HEAD is not a commit")?
.id();
update_tracking(repo, branch, theirs)?;
Ok(theirs)
}
/// Point the remote-tracking ref at `tip` (which must already be in the local
/// odb). Keeps [`tracking_tip`] — and with it publish's radio-free up-to-date
/// check — honest about what origin has.
fn update_tracking(repo: &Repository, branch: &str, tip: Oid) -> Result<()> {
repo.reference(
&format!("refs/remotes/origin/{branch}"),
theirs,
tip,
true,
"typoena fetch",
)
.context("updating remote-tracking ref")?;
Ok(theirs)
Ok(())
}
/// Open `/sd/repo`, fetch origin, and **fast-forward only** — never a merge.
@@ -781,33 +789,69 @@ fn pull_once() -> Result<PullOutcome> {
.shorthand()
.context("HEAD has no branch shorthand")?
.to_string();
let t_fetch = Instant::now();
let theirs = fetch_origin(&repo, &branch)?;
let fetch_ms = t_fetch.elapsed().as_millis();
log::info!(
"pull: fetched origin @ {} in {fetch_ms}ms — free heap {} ({} internal)",
short(theirs),
free_heap(),
internal_free_heap()
);
let head = repo.head()?.peel_to_commit()?.id();
// ls-refs first, download only if needed: the ref advertisement alone
// answers "anything new?", so the common shapes (up to date, local ahead)
// never enter pack negotiation — the first on-device pull paid a 9.7 s
// fetch just to learn it was up to date. When a download IS needed it
// rides the same open connection (no second TLS handshake).
let mut remote = repo.find_remote("origin")?;
let t_ls = Instant::now();
remote
.connect_auth(git2::Direction::Fetch, Some(auth_callbacks()), None)
.context("connecting to origin")?;
let refname = format!("refs/heads/{branch}");
let theirs = remote
.list()
.context("listing origin refs")?
.iter()
.find(|h| h.name() == refname)
.map(|h| h.oid())
.with_context(|| format!("origin does not advertise {refname}"))?;
let ls_ms = t_ls.elapsed().as_millis();
if theirs == head {
log::info!("pull: origin @ {} == HEAD — up to date (fetch {fetch_ms}ms)", short(head));
let _ = remote.disconnect();
update_tracking(&repo, &branch, theirs)?;
log::info!("pull: origin @ {} == HEAD — up to date (ls-refs {ls_ms}ms, no fetch)", short(head));
return Ok(PullOutcome::UpToDate);
}
if repo
.graph_descendant_of(head, theirs)
.context("descendant check (local ahead)")?
// `theirs` an ancestor of HEAD ⇒ it is already in the local odb (all
// ancestors of a local commit are local) — no download needed either.
if repo.odb()?.exists(theirs)
&& repo
.graph_descendant_of(head, theirs)
.context("descendant check (local ahead)")?
{
let _ = remote.disconnect();
update_tracking(&repo, &branch, theirs)?;
log::info!(
"pull: HEAD {} is ahead of origin {} — nothing to pull, :sync publishes it",
"pull: HEAD {} is ahead of origin {} — nothing to pull, :sync publishes it (ls-refs {ls_ms}ms, no fetch)",
short(head),
short(theirs)
);
return Ok(PullOutcome::LocalAhead);
}
// Origin has commits we lack: download them over the already-open
// connection (callbacks were bound at connect_auth).
let t_fetch = Instant::now();
let mut fo = FetchOptions::new();
fo.remote_callbacks(auth_callbacks());
remote
.download(&[branch.as_str()], Some(&mut fo))
.context("downloading from origin")?;
let _ = remote.disconnect();
update_tracking(&repo, &branch, theirs)?;
let fetch_ms = t_fetch.elapsed().as_millis();
log::info!(
"pull: downloaded origin @ {} — ls-refs {ls_ms}ms, download {fetch_ms}ms, free heap {} ({} internal)",
short(theirs),
free_heap(),
internal_free_heap()
);
if !repo
.graph_descendant_of(theirs, head)
.context("descendant check (fast-forward)")?