a manifest cannot ask for a particular port
A preference sounds helpful and fails silently. When the wished-for number is taken the plugin is handed a different one, the install succeeds, and whatever was configured against the old number goes on pointing at nothing - with nobody told. This afternoon that nearly happened: 3011 was only free because fofos-dev had been stopped twenty minutes earlier, and had it still been running the site would have been given a random port behind an nginx host still aimed at 3011. So the number is always drawn fresh and always kept. Always drawn means it is never something you can assume; always kept means the proxy host is configured against it exactly once. The failure mode that remains - having to go and read the number - is one that announces itself, which the other one did not. isFree went with it; it existed only to test a wish.
This commit is contained in:
@@ -77,17 +77,6 @@ export async function getPluginPorts(appName: string): Promise<Record<string, nu
|
||||
* remembered port must say so loudly rather than quietly choosing another, because the number is written
|
||||
* down in configuration this database cannot see.
|
||||
*/
|
||||
/** Can we bind it right now? The only honest test, and true only for this instant. */
|
||||
async function isFree(port: number): Promise<boolean> {
|
||||
try {
|
||||
const probe = Bun.serve({ port, hostname: '127.0.0.1', fetch: () => new Response('') });
|
||||
probe.stop(true);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function freePort(taken: Set<number>): Promise<number> {
|
||||
for (let attempt = 0; attempt < 20; attempt++) {
|
||||
const probe = Bun.serve({ port: 0, hostname: '127.0.0.1', fetch: () => new Response('') });
|
||||
@@ -105,7 +94,7 @@ async function freePort(taken: Set<number>): Promise<number> {
|
||||
* point — see the note on `pluginPorts`. Re-installing, upgrading and restarting all return what was
|
||||
* given the first time.
|
||||
*/
|
||||
export async function allocatePluginPort(appName: string, name: string, preferred?: number): Promise<number> {
|
||||
export async function allocatePluginPort(appName: string, name: string): Promise<number> {
|
||||
const [existing] = await db
|
||||
.select()
|
||||
.from(pluginPorts)
|
||||
@@ -120,16 +109,7 @@ export async function allocatePluginPort(appName: string, name: string, preferre
|
||||
*/
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
const taken = new Set((await db.select().from(pluginPorts)).map((r) => r.port));
|
||||
/*
|
||||
The wish first, and only on the first attempt.
|
||||
|
||||
A preferred port that is already spoken for, or that something else on this machine is listening
|
||||
on, is not an error — it is a plugin arriving somewhere its ideal address is taken, which is
|
||||
ordinary. It falls back to any free one rather than failing, because an install that refuses over
|
||||
a preference would make `preferred` a claim.
|
||||
*/
|
||||
const wish = attempt === 0 && preferred && !taken.has(preferred) && (await isFree(preferred)) ? preferred : null;
|
||||
const port = wish ?? (await freePort(taken));
|
||||
const port = await freePort(taken);
|
||||
try {
|
||||
const [row] = await db.insert(pluginPorts).values({ appName, name, port }).returning();
|
||||
return row!.port;
|
||||
|
||||
Reference in New Issue
Block a user