@skydiveai/git-cache 0.1.0-beta.2618 → 0.1.0-beta.2647
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +26 -0
- package/dist/index.mjs +1 -1
- package/dist/{server-DDcKo4yG.mjs → server-BrGhh8-W.mjs} +20 -8
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -151,6 +151,32 @@ declare function shouldBackfillAfterPassthrough({
|
|
|
151
151
|
cacheable: any;
|
|
152
152
|
sha256: any;
|
|
153
153
|
}): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Decide how to serve a warm mirror for one request.
|
|
156
|
+
*
|
|
157
|
+
* `revalidateRefs` is set for the `info/refs` half of a fetch — the ref
|
|
158
|
+
* advertisement, where the client learns the remote's HEAD and branch tips.
|
|
159
|
+
* That answer must never be stale: serving a pre-merge advertisement is how a
|
|
160
|
+
* `git fetch` right after an upstream squash-merge silently returns the old
|
|
161
|
+
* `origin/<branch>` (the object-reuse win of the cache is not worth lying about
|
|
162
|
+
* where HEAD is). So for `info/refs` a stale mirror BLOCKS on a synchronous
|
|
163
|
+
* refresh before it answers, exactly as an explicit push invalidation does.
|
|
164
|
+
* The `git-upload-pack` half keeps stale-while-revalidate: its wants come from
|
|
165
|
+
* the just-served fresh advertisement, so the client only asks for objects the
|
|
166
|
+
* fresh refs named, and the caller's `mirrorHasObjects` gate already falls back
|
|
167
|
+
* to origin for any the mirror is missing (the new objects a merge introduced).
|
|
168
|
+
*
|
|
169
|
+
* A blocked refresh that FAILS (network blip, expired token, upstream 5xx)
|
|
170
|
+
* returns `cacheState: 'STALE_REVALIDATE_FAILED'` rather than throwing, so the
|
|
171
|
+
* caller can proxy `info/refs` straight to origin — fresh refs from upstream,
|
|
172
|
+
* never a 502 and never the stale advertisement.
|
|
173
|
+
*/
|
|
174
|
+
/**
|
|
175
|
+
* Whether a mirror stamped `lastFetchMs` is fresh: past the invalidation fence
|
|
176
|
+
* AND within the TTL window. `lastFetchMs` is a fetch-START time (see
|
|
177
|
+
* writeMeta), so a fetch that began before the fence is stale no matter when
|
|
178
|
+
* it completed — the data it holds predates the invalidation by construction.
|
|
179
|
+
*/
|
|
154
180
|
/**
|
|
155
181
|
* True when every OID is already present in the mirror.
|
|
156
182
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as isProtocolV2Request, c as parseAdvertisement, d as shouldBackfillAfterPassthrough, f as startServer, i as explainRejection, l as parseUpstream, n as classifyUploadRequest, o as metricsSnapshot, r as copyRequestHeaders, s as mirrorHasObjects, t as SidebandPackExtractor, u as recordColdBuildOutcome } from "./server-
|
|
1
|
+
import { a as isProtocolV2Request, c as parseAdvertisement, d as shouldBackfillAfterPassthrough, f as startServer, i as explainRejection, l as parseUpstream, n as classifyUploadRequest, o as metricsSnapshot, r as copyRequestHeaders, s as mirrorHasObjects, t as SidebandPackExtractor, u as recordColdBuildOutcome } from "./server-BrGhh8-W.mjs";
|
|
2
2
|
export { SidebandPackExtractor, classifyUploadRequest, copyRequestHeaders, explainRejection, isProtocolV2Request, metricsSnapshot, mirrorHasObjects, parseAdvertisement, parseUpstream, recordColdBuildOutcome, shouldBackfillAfterPassthrough, startServer };
|
|
@@ -821,10 +821,10 @@ var SidebandPackExtractor = class {
|
|
|
821
821
|
if (!this.errored) this.writable.end();
|
|
822
822
|
}
|
|
823
823
|
};
|
|
824
|
-
async function writeMeta(local, extra = {}) {
|
|
824
|
+
async function writeMeta(local, fetchStartMs, extra = {}) {
|
|
825
825
|
const tmp = `${metaPath(local)}.tmp.${INSTANCE_ID}`;
|
|
826
826
|
await writeFile(tmp, JSON.stringify({
|
|
827
|
-
lastFetchMs:
|
|
827
|
+
lastFetchMs: fetchStartMs,
|
|
828
828
|
...extra
|
|
829
829
|
}), "utf8");
|
|
830
830
|
await rename(tmp, metaPath(local));
|
|
@@ -1019,7 +1019,7 @@ async function backfillMirror(repo, upstreamUrl, credHeader = null) {
|
|
|
1019
1019
|
const published = await withRepoLock(repo, "write", async () => {
|
|
1020
1020
|
if (await pathExists(local)) return false;
|
|
1021
1021
|
await rename(temp, local);
|
|
1022
|
-
await writeMeta(local, { publishedMs: Date.now() });
|
|
1022
|
+
await writeMeta(local, startedMs, { publishedMs: Date.now() });
|
|
1023
1023
|
return true;
|
|
1024
1024
|
});
|
|
1025
1025
|
if (published !== true) {
|
|
@@ -1056,6 +1056,7 @@ async function backfillMirror(repo, upstreamUrl, credHeader = null) {
|
|
|
1056
1056
|
}
|
|
1057
1057
|
function startColdStream(req, res, upstreamUrl, repo, requestBody, credHeader) {
|
|
1058
1058
|
const local = localPath(repo);
|
|
1059
|
+
const captureStartedMs = Date.now();
|
|
1059
1060
|
const temp = `${config.cacheDir}/quarantine/${repo.replace(/\//g, "__")}.stream-${INSTANCE_ID}-${Date.now()}`;
|
|
1060
1061
|
metrics.cold_requests++;
|
|
1061
1062
|
coldLeaders.add(repo);
|
|
@@ -1230,7 +1231,7 @@ function startColdStream(req, res, upstreamUrl, repo, requestBody, credHeader) {
|
|
|
1230
1231
|
const published = await withRepoLock(repo, "write", async () => {
|
|
1231
1232
|
if (await pathExists(local)) return false;
|
|
1232
1233
|
await rename(temp, local);
|
|
1233
|
-
await writeMeta(local, { publishedMs: Date.now() });
|
|
1234
|
+
await writeMeta(local, captureStartedMs, { publishedMs: Date.now() });
|
|
1234
1235
|
return true;
|
|
1235
1236
|
});
|
|
1236
1237
|
if (published === true) {
|
|
@@ -1294,6 +1295,7 @@ async function refreshMirror(repo, local, credHeader = null) {
|
|
|
1294
1295
|
"true"
|
|
1295
1296
|
]);
|
|
1296
1297
|
await disableAutoMaintenance(local);
|
|
1298
|
+
const fetchStartMs = Date.now();
|
|
1297
1299
|
await runGit(upstreamGitArgs([
|
|
1298
1300
|
"--git-dir",
|
|
1299
1301
|
local,
|
|
@@ -1306,7 +1308,7 @@ async function refreshMirror(repo, local, credHeader = null) {
|
|
|
1306
1308
|
...GIT_ENV,
|
|
1307
1309
|
...upstreamGitCredentialEnv(credHeader)
|
|
1308
1310
|
} });
|
|
1309
|
-
await writeMeta(local);
|
|
1311
|
+
await writeMeta(local, fetchStartMs);
|
|
1310
1312
|
metrics.refreshes++;
|
|
1311
1313
|
log("info", "refresh", { repo });
|
|
1312
1314
|
return local;
|
|
@@ -1331,10 +1333,21 @@ async function refreshMirror(repo, local, credHeader = null) {
|
|
|
1331
1333
|
* caller can proxy `info/refs` straight to origin — fresh refs from upstream,
|
|
1332
1334
|
* never a 502 and never the stale advertisement.
|
|
1333
1335
|
*/
|
|
1336
|
+
/**
|
|
1337
|
+
* Whether a mirror stamped `lastFetchMs` is fresh: past the invalidation fence
|
|
1338
|
+
* AND within the TTL window. `lastFetchMs` is a fetch-START time (see
|
|
1339
|
+
* writeMeta), so a fetch that began before the fence is stale no matter when
|
|
1340
|
+
* it completed — the data it holds predates the invalidation by construction.
|
|
1341
|
+
*/
|
|
1342
|
+
function isMetaFresh(lastFetchMs, fenceMs, ttlSeconds, nowMs) {
|
|
1343
|
+
if (lastFetchMs <= 0) return false;
|
|
1344
|
+
if (lastFetchMs > nowMs) return false;
|
|
1345
|
+
return lastFetchMs >= fenceMs && (nowMs - lastFetchMs) / 1e3 < ttlSeconds;
|
|
1346
|
+
}
|
|
1334
1347
|
async function ensureWarm(repo, credHeader = null, revalidateRefs = false) {
|
|
1335
1348
|
const local = localPath(repo);
|
|
1336
1349
|
const lastFetchMs = (await readMeta(local))?.lastFetchMs ?? 0;
|
|
1337
|
-
if (
|
|
1350
|
+
if (!!isMetaFresh(lastFetchMs, invalidateBeforeMs, config.ttlSeconds, Date.now())) {
|
|
1338
1351
|
metrics.warm_hits++;
|
|
1339
1352
|
return {
|
|
1340
1353
|
local,
|
|
@@ -1357,8 +1370,7 @@ async function ensureWarm(repo, credHeader = null, revalidateRefs = false) {
|
|
|
1357
1370
|
}).finally(() => inflightRefresh.delete(repo)));
|
|
1358
1371
|
if (mustBlock) {
|
|
1359
1372
|
await inflightRefresh.get(repo);
|
|
1360
|
-
|
|
1361
|
-
if (!(refreshedMs >= invalidateBeforeMs && (Date.now() - refreshedMs) / 1e3 < config.ttlSeconds)) {
|
|
1373
|
+
if (!isMetaFresh((await readMeta(local))?.lastFetchMs ?? 0, invalidateBeforeMs, config.ttlSeconds, Date.now())) {
|
|
1362
1374
|
if (revalidateRefs && !fencedByPush) {
|
|
1363
1375
|
metrics.revalidate_failures++;
|
|
1364
1376
|
log("warn", "revalidate_failed", { repo });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skydiveai/git-cache",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.2647",
|
|
4
4
|
"description": "Sandbox-local Git smart-HTTP clone/fetch cache: a loopback daemon that transparently mirrors HTTPS git reads so repeated clone/fetch/ls-remote skip the origin round-trip. Runs inside an agent sandbox; pushes stay direct.",
|
|
5
5
|
"homepage": "https://skydive.com",
|
|
6
6
|
"license": "MIT",
|