CREATE TYPE public.vmid_reservation_status AS ENUM ('reserved', 'released'); CREATE TABLE public.cluster_vmid_allocators ( cluster_id uuid PRIMARY KEY REFERENCES public.clusters(id) ON DELETE CASCADE, next_vmid integer NOT NULL DEFAULT 100, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT cluster_vmid_allocators_next_vmid_check CHECK (next_vmid > 0) ); CREATE TABLE public.vmid_reservations ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), cluster_id uuid NOT NULL REFERENCES public.clusters(id) ON DELETE CASCADE, proxmox_vmid integer NOT NULL, status public.vmid_reservation_status NOT NULL DEFAULT 'reserved', created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT vmid_reservations_proxmox_vmid_check CHECK (proxmox_vmid > 0) ); CREATE UNIQUE INDEX vmid_reservations_reserved_key ON public.vmid_reservations (cluster_id, proxmox_vmid) WHERE status = 'reserved'; CREATE INDEX vmid_reservations_cluster_id_idx ON public.vmid_reservations (cluster_id); CREATE OR REPLACE FUNCTION public.reserve_next_vmid(p_cluster_id uuid) RETURNS integer LANGUAGE plpgsql AS $$ DECLARE candidate integer; BEGIN INSERT INTO public.cluster_vmid_allocators (cluster_id) VALUES (p_cluster_id) ON CONFLICT (cluster_id) DO NOTHING; SELECT next_vmid INTO candidate FROM public.cluster_vmid_allocators WHERE cluster_id = p_cluster_id FOR UPDATE; LOOP EXIT WHEN NOT EXISTS ( SELECT 1 FROM public.vms WHERE cluster_id = p_cluster_id AND proxmox_vmid = candidate ) AND NOT EXISTS ( SELECT 1 FROM public.vmid_reservations WHERE cluster_id = p_cluster_id AND proxmox_vmid = candidate AND status = 'reserved' ); candidate := candidate + 1; END LOOP; INSERT INTO public.vmid_reservations (cluster_id, proxmox_vmid) VALUES (p_cluster_id, candidate); UPDATE public.cluster_vmid_allocators SET next_vmid = candidate + 1, updated_at = now() WHERE cluster_id = p_cluster_id; RETURN candidate; END; $$;