Skip to content

Commit 9d776f3

Browse files
committed
feat(resilience): auto-detect best index mirror at first init (the real stability fix)
Implements TODO(mirror-default) option (b). The first-init seed used a hardcoded "CN" mirror, which strands overseas users and GitHub-hosted CI behind a slow/unreachable gitcode mirror — the actual root cause behind the repeated 75_index_status_offline.sh cold-bootstrap failures (a US runner seeding CN can't reach gitcode, so the index clone fails fast and the index reports 'missing'). detect_best_mirror() runs a short, tight-timeout HEAD probe to github.com (GLOBAL) and gitcode.com (CN), and pins the lower-latency reachable one into .xlings.json. Priority matches the intended design: explicit --mirror (config) > lower-latency auto-probe > GLOBAL fallback An explicit `mcpp self config --mirror CN|GLOBAL` always wins; the probe only runs on a fresh init with no explicit choice. Falls back to GLOBAL (reachable nearly everywhere) if neither host answers. Verified locally: on a CN host the probe picks CN (gitcode 150ms < github 380ms) and logs 'mirror: probe github=380ms gitcode=150ms -> CN' under MCPP_VERBOSE; a US CI runner will symmetrically pick GLOBAL. e2e 75/80/81 green.
1 parent 4107d4a commit 9d776f3

2 files changed

Lines changed: 48 additions & 14 deletions

File tree

src/config.cppm

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,11 @@ bool write_default_xlings_json(const std::filesystem::path& path,
374374
// construct a temporary Env with home = path.parent_path().
375375
mcpp::xlings::Env env;
376376
env.home = path.parent_path();
377+
// No explicit --mirror: auto-detect the lower-latency reachable mirror
378+
// instead of the historic hardcoded "CN" (which strands overseas users and
379+
// GitHub-hosted CI). An explicit choice always wins (the else branch).
377380
if (mirror_override.empty())
378-
mcpp::xlings::seed_xlings_json(env, pairs);
381+
mcpp::xlings::seed_xlings_json(env, pairs, mcpp::xlings::detect_best_mirror());
379382
else
380383
mcpp::xlings::seed_xlings_json(env, pairs, mirror_override);
381384
return std::filesystem::exists(path);

src/xlings.cppm

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,23 +225,25 @@ int install_direct(const Env& env, std::string_view target, bool quiet = false);
225225

226226
// Write .xlings.json seed file.
227227
//
228-
// TODO(mirror-default): the default `"CN"` is the historical setting for
229-
// the project's initial Chinese-mainland user base, but it bites overseas
230-
// users (and CI on GitHub-hosted runners) — the first network roundtrip
231-
// goes through a CN mirror that is slow/unreachable for them. The
232-
// `mcpp self config --mirror X` flow now passes the user's choice as an
233-
// override through to here, so they can pick the right mirror BEFORE the
234-
// first download. Longer term, consider:
235-
// (a) flip the default to "GLOBAL" and have CN users opt in via
236-
// `mcpp self config --mirror CN` (smaller blast radius once docs
237-
// cover the switch); or
238-
// (b) auto-detect on first init (env hint like LANG, a quick HEAD probe
239-
// to github.com vs. ghproxy with a tight timeout, and pin the
240-
// winning value into .xlings.json).
228+
// The `mirror` parameter still defaults to "CN" for direct callers, but the
229+
// first-init seed path (config.cppm) now passes the result of
230+
// `detect_best_mirror()` when the user gave no explicit `--mirror` — i.e.
231+
// TODO(mirror-default) option (b) is implemented: a quick latency probe to the
232+
// GLOBAL vs CN hosts picks the faster reachable one, so overseas users and
233+
// GitHub-hosted CI no longer get stranded on the historical CN default. An
234+
// explicit `mcpp self config --mirror X` always wins (config priority).
241235
void seed_xlings_json(const Env& env,
242236
std::span<const std::pair<std::string,std::string>> repos,
243237
std::string_view mirror = "CN");
244238

239+
// Probe both index mirrors and return the lower-latency reachable one
240+
// ("GLOBAL" | "CN") — TODO(mirror-default) option (b). Used at first init when
241+
// the user gave no explicit --mirror; an explicit choice always wins (config
242+
// priority). Falls back to "GLOBAL" (reachable nearly everywhere) when neither
243+
// probe succeeds, since the historic "CN" default stranded overseas users and
244+
// GitHub-hosted CI behind a slow/unreachable mirror.
245+
std::string detect_best_mirror();
246+
245247
// Persist the xlings mirror selection in .xlings.json via xlings itself.
246248
int config_show(const Env& env);
247249
int config_set_mirror(const Env& env, std::string_view mirror, bool quiet = false);
@@ -1092,6 +1094,35 @@ void seed_xlings_json(const Env& env,
10921094
write_file(path, json);
10931095
}
10941096

1097+
std::string detect_best_mirror() {
1098+
using namespace std::chrono;
1099+
// A short HEAD probe to each mirror host, timed by wall clock. Non-zero rc
1100+
// (DNS failure, connection refused, or the tight --max-time elapsing) means
1101+
// "unreachable". curl is present on every platform mcpp targets; if it is
1102+
// somehow absent both probes fail and we fall back to GLOBAL.
1103+
auto probe = [](std::string_view url) -> std::optional<double> {
1104+
// -I writes response headers to stdout; redirect both streams to null
1105+
// (cross-platform) so the probe stays silent — only its timing matters.
1106+
auto cmd = std::format("curl -s -I --max-time 3 {} {}",
1107+
url, mcpp::platform::shell::silent_redirect);
1108+
auto t0 = steady_clock::now();
1109+
if (mcpp::platform::process::run_silent(cmd) != 0) return std::nullopt;
1110+
return duration<double>(steady_clock::now() - t0).count();
1111+
};
1112+
auto g = probe("https://github.com");
1113+
auto c = probe("https://gitcode.com");
1114+
if (g && c) {
1115+
std::string pick = (*g <= *c) ? "GLOBAL" : "CN";
1116+
mcpp::log::verbose("mirror", std::format(
1117+
"probe github={:.0f}ms gitcode={:.0f}ms -> {}", *g * 1000, *c * 1000, pick));
1118+
return pick;
1119+
}
1120+
if (g) { mcpp::log::verbose("mirror", "only github reachable -> GLOBAL"); return "GLOBAL"; }
1121+
if (c) { mcpp::log::verbose("mirror", "only gitcode reachable -> CN"); return "CN"; }
1122+
mcpp::log::verbose("mirror", "neither mirror reachable; defaulting -> GLOBAL");
1123+
return "GLOBAL";
1124+
}
1125+
10951126
int config_show(const Env& env) {
10961127
auto cmd = std::format("{} config", build_command_prefix(env));
10971128
return mcpp::platform::process::run_silent(cmd);

0 commit comments

Comments
 (0)