89 lines
2.3 KiB
JavaScript
89 lines
2.3 KiB
JavaScript
// src/services/vmidAllocator.js
|
||
// Counter-based VMID allocator with wrap + clash probe.
|
||
|
||
import * as prismaSvc from './prisma.js';
|
||
const prisma = prismaSvc.prisma ?? prismaSvc.default;
|
||
|
||
const RANGES = {
|
||
game: { min: 5000, max: 5999 },
|
||
dev: { min: 6000, max: 6999 },
|
||
};
|
||
|
||
/**
|
||
* Internal helper: allocate the next VMID in a given range for a key ("game" | "dev").
|
||
* - Uses VmidCounter as the single source of truth.
|
||
* - Wraps when exceeding max.
|
||
* - Probes ContainerInstance to avoid collisions.
|
||
*/
|
||
async function nextId(key, { min, max }) {
|
||
return prisma.$transaction(async (tx) => {
|
||
// Get or create the counter row
|
||
let row = await tx.vmidCounter.findUnique({ where: { key } });
|
||
if (!row) {
|
||
row = await tx.vmidCounter.create({
|
||
data: { key, current: min - 1 },
|
||
});
|
||
}
|
||
|
||
const totalSlots = max - min + 1;
|
||
let candidate = row.current;
|
||
let attempts = 0;
|
||
|
||
while (attempts < totalSlots) {
|
||
candidate += 1;
|
||
if (candidate > max) candidate = min;
|
||
|
||
// Check for an existing ContainerInstance with this vmid
|
||
const existing = await tx.containerInstance.findUnique({
|
||
where: { vmid: candidate },
|
||
});
|
||
|
||
if (!existing) {
|
||
// Update the counter to this new value
|
||
await tx.vmidCounter.update({
|
||
where: { key },
|
||
data: { current: candidate },
|
||
});
|
||
|
||
return candidate;
|
||
}
|
||
|
||
attempts += 1;
|
||
}
|
||
|
||
// If we got here, the range is fully exhausted
|
||
throw new Error(
|
||
`No free VMIDs available in range ${min}-${max} for key="${key}".`
|
||
);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Allocate a VMID for a container type.
|
||
* ctype: "game" | "dev"
|
||
*/
|
||
export async function allocateVmid(ctype) {
|
||
const key = ctype === 'dev' ? 'dev' : 'game';
|
||
return nextId(key, RANGES[key]);
|
||
}
|
||
|
||
/**
|
||
* Stub for now – kept for API compatibility.
|
||
* If you ever want to do extra verification after provisioning,
|
||
* you can expand this.
|
||
*/
|
||
export async function confirmVmidAllocated(_vmid) {
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Stub for now – VMIDs are not re-used immediately.
|
||
* If you later decide to support "returning" VMIDs to a free pool,
|
||
* implement that here (carefully).
|
||
*/
|
||
export async function releaseVmid(_vmid) {
|
||
return true;
|
||
}
|
||
|
||
export default { allocateVmid, confirmVmidAllocated, releaseVmid };
|