gitea: a merge poll looks only at repositories that moved, one pass at a time
With the poll the only announcer of a merge, its cost showed: every 30 s it asked every repository for its pull requests, a pass outlasted the tick, and passes piled up beside each other — a merge was announced four and a half minutes late, and two passes at once could each announce it. A pass now asks only repositories updated since a minute before the last look, and the next pass starts when this one ends. hq issue 250.
This commit is contained in:
+16
-4
@@ -14,7 +14,7 @@
|
||||
// the forge for an immediacy nobody asked for is not.
|
||||
|
||||
import { emit } from "@novox/mesh-sdk/events";
|
||||
import { GiteaClient } from "./client.js";
|
||||
import { GiteaClient, movedSince } from "./client.js";
|
||||
|
||||
// Without a way to a token — configured, or mintable with the admin account (token.ts) — there is
|
||||
// nothing to watch; log and stay quiet rather than crash the runtime. With one, the first poll mints
|
||||
@@ -84,8 +84,16 @@ function keepAnnounced(): void {
|
||||
writeFileSync(tmp, JSON.stringify({ announced: [...announced].slice(-2000), since }));
|
||||
renameSync(tmp, mergedRecord);
|
||||
}
|
||||
// **Only the repositories that moved** (novox/hq issue 250). A merge is a push, and a push moves the
|
||||
// repository's update time; asking every repository for its pull requests on every tick took longer than the
|
||||
// tick itself, so ticks piled up and a merge was announced minutes late. A repository unchanged since a
|
||||
// minute before the last look is skipped — the minute absorbs the forge's clock against this one.
|
||||
let lastLook = "";
|
||||
const MARGIN_MS = 60_000;
|
||||
async function pollMerged(client: GiteaClient): Promise<void> {
|
||||
const repos = await client.listAllRepos();
|
||||
const began = new Date().toISOString();
|
||||
const floor = lastLook && primedMerges ? new Date(Date.parse(lastLook) - MARGIN_MS).toISOString() : "";
|
||||
const repos = movedSince(await client.listAllRepos(), floor);
|
||||
let changed = false;
|
||||
for (const repo of repos) {
|
||||
const pulls = await client.listPullRequests(repo.owner, repo.name, { state: "closed", sort: "recentupdate", limit: "20" });
|
||||
@@ -124,6 +132,7 @@ async function pollMerged(client: GiteaClient): Promise<void> {
|
||||
if (!primedMerges) since = new Date().toISOString();
|
||||
if (!primedMerges || changed) keepAnnounced();
|
||||
primedMerges = true;
|
||||
lastLook = began;
|
||||
}
|
||||
|
||||
if (gitea) {
|
||||
@@ -132,6 +141,9 @@ if (gitea) {
|
||||
// yet, the admin account refused on a restored forge) is one fact, and a recovery is worth a line.
|
||||
let failing: string | null = null;
|
||||
const tick = (fn: () => Promise<void>, everyMs: number): void => {
|
||||
// **One pass at a time** (novox/hq issue 250): the next pass is scheduled when this one has ended, so a
|
||||
// pass that outlasts its interval delays the next instead of running beside it — two passes at once
|
||||
// could each announce the same merge before either recorded it.
|
||||
const run = (): void =>
|
||||
void fn()
|
||||
.then(() => {
|
||||
@@ -142,8 +154,8 @@ if (gitea) {
|
||||
const why = err instanceof Error ? err.message : String(err);
|
||||
if (why !== failing) console.error(`[gitea] not watching until this clears — ${why}`);
|
||||
failing = why;
|
||||
});
|
||||
setInterval(run, everyMs);
|
||||
})
|
||||
.finally(() => setTimeout(run, everyMs));
|
||||
run();
|
||||
};
|
||||
tick(() => pollRepos(client), 60_000);
|
||||
|
||||
Reference in New Issue
Block a user