feat(switch-db): close third drift-gap direction, switch-side changes
Preventive companion to 126 (compatibility writes) + 138 (transceiver speed changes): the same physically-impossible-compat invariant also depends on switches.max_speed_gbps/ports_config via v_switch_effective_max, but nothing guarded THAT write direction. Audit 2026-07-17: currently a no-op live (nothing updates those columns today outside the one-time sql/125 backfill), but the documented open item -- 110 switches need ports_config datasheet enrichment -- is exactly the future job that would trigger this gap the same way the daily modulation/FEC cron triggered 138. Closing it now. Verified with a synthetic in-transaction test (1,250 rows correctly flagged, then rolled back).
This commit is contained in:
parent
668911756c
commit
bc53c5ebf3
70
sql/141-switch-speed-drift-guard.sql
Normal file
70
sql/141-switch-speed-drift-guard.sql
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
-- sql/141-switch-speed-drift-guard.sql
|
||||||
|
-- Preventive close of the THIRD direction of the same drift-gap class fixed in
|
||||||
|
-- sql/126 (compat_speed_guard, guards compatibility writes) and sql/138
|
||||||
|
-- (compat_speed_guard_on_transceiver_change, guards transceivers.speed_gbps
|
||||||
|
-- changes). The "no physically-impossible compatibility rows" invariant is
|
||||||
|
-- derived from THREE tables' data: compatibility.status, transceivers.speed_gbps,
|
||||||
|
-- and switches.max_speed_gbps/ports_config (via v_switch_effective_max). Only
|
||||||
|
-- two of those three write paths were guarded.
|
||||||
|
--
|
||||||
|
-- Audit 2026-07-17 found switches.max_speed_gbps/ports_config are currently
|
||||||
|
-- write-once (set at scrape/seed time, sql/125's one-time backfill) — no live
|
||||||
|
-- job updates them today, so this direction is NOT actively exploited yet. But
|
||||||
|
-- the documented open item "110 switches with understated ports_config need
|
||||||
|
-- datasheet enrichment" ([[tip-switch-database-audit-2026-07-06]]) describes
|
||||||
|
-- exactly the future job that WOULD trigger this gap: correcting a switch's
|
||||||
|
-- max_speed_gbps or ports_config downward after compat rows already exist would
|
||||||
|
-- silently leave now-impossible rows marked 'compatible', same failure mode as
|
||||||
|
-- sql/138, just from the other table. Closing it now, before that automation
|
||||||
|
-- ships, rather than waiting for the next backlog surprise.
|
||||||
|
--
|
||||||
|
-- Same "no false positives" design as 126/138: only algorithmic (non-curated)
|
||||||
|
-- rows are ever forced to incompatible; curated rows are trusted as-is.
|
||||||
|
|
||||||
|
create or replace function compat_speed_guard_on_switch_change() returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
new_ceiling numeric;
|
||||||
|
begin
|
||||||
|
if NEW.max_speed_gbps is distinct from OLD.max_speed_gbps
|
||||||
|
or NEW.ports_config is distinct from OLD.ports_config then
|
||||||
|
select greatest(
|
||||||
|
coalesce(NEW.max_speed_gbps, 0),
|
||||||
|
coalesce(fn_ports_max_gbps(NEW.ports_config), 0),
|
||||||
|
coalesce((select max(t.speed_gbps)
|
||||||
|
from compatibility c join transceivers t on t.id = c.transceiver_id
|
||||||
|
where c.switch_id = NEW.id and c.status = 'compatible'
|
||||||
|
and c.verification_method in ('vendor_matrix','vendor_compat')
|
||||||
|
and t.speed_gbps > 0), 0)
|
||||||
|
)
|
||||||
|
into new_ceiling;
|
||||||
|
|
||||||
|
if new_ceiling > 0 then
|
||||||
|
update compatibility c
|
||||||
|
set status = 'incompatible',
|
||||||
|
notes = case when c.notes is null or c.notes = ''
|
||||||
|
then 'auto(141): transceiver speed exceeds switch max port speed (post-hoc drift)'
|
||||||
|
else c.notes || ' | auto(141): transceiver speed exceeds switch max port speed (post-hoc drift)' end
|
||||||
|
from transceivers t
|
||||||
|
where t.id = c.transceiver_id
|
||||||
|
and c.switch_id = NEW.id
|
||||||
|
and c.status = 'compatible'
|
||||||
|
and c.verification_method = 'spec_match'
|
||||||
|
and t.speed_gbps is not null
|
||||||
|
and t.speed_gbps > new_ceiling;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
return NEW;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
comment on function compat_speed_guard_on_switch_change() is
|
||||||
|
'AFTER UPDATE OF max_speed_gbps/ports_config on switches: re-validates existing compatible spec_match compatibility rows for this switch against the (re-derived) evidence-based ceiling. Third leg of the compat_speed_guard family (126=compatibility writes, 138=transceiver speed changes, this=switch capacity changes). Never touches curated rows. Currently a no-op in practice -- nothing updates these columns live yet -- this is preventive, closing the gap before the planned switch datasheet-enrichment automation ships.';
|
||||||
|
|
||||||
|
drop trigger if exists trg_compat_speed_guard_on_switch_change on switches;
|
||||||
|
create trigger trg_compat_speed_guard_on_switch_change
|
||||||
|
after update of max_speed_gbps, ports_config on switches
|
||||||
|
for each row execute function compat_speed_guard_on_switch_change();
|
||||||
|
|
||||||
|
insert into _migrations (filename, applied_at)
|
||||||
|
select '141-switch-speed-drift-guard.sql', now()
|
||||||
|
where not exists (select 1 from _migrations where filename = '141-switch-speed-drift-guard.sql');
|
||||||
Loading…
x
Reference in New Issue
Block a user