Root cause: the form-factor fallback in flexoptix-compat matched transceivers by form factor only, ignoring speed, so a 400G OSFP port matched every OSFP module incl. 1.6T (5686+ impossible rows across 266 switches). - flexoptix-compat: fallback now filters transceiver speed <= switch max port speed (derived from ports_config + stored max_speed_gbps). Prevents regeneration. - sql/125: evidence-based effective-ceiling (stored/ports/curated) views; flag 5811 impossible spec_match rows status=incompatible (curated rows never touched, understated switches never false-flagged); backfill max_speed_gbps for 327 switches from ports_config; re-derive speed for 306 speed<=0 transceivers (form-factor capped, high-confidence only). v_switch_quality_alerts is the task list; v_switch_understated_ports surfaces switches needing datasheet enrichment.
238 lines
14 KiB
SQL
238 lines
14 KiB
SQL
-- sql/125-switch-quality.sql
|
|
-- Switch / compatibility data-quality remediation + monitoring layer.
|
|
--
|
|
-- Audit 2026-07-06 (read-only) found systematic errors in the switch database:
|
|
-- * 5.686+ physically-impossible compatibility rows (transceiver faster than the
|
|
-- switch's fastest port). Root cause: the form-factor fallback in
|
|
-- packages/scraper/src/scrapers/flexoptix-compat.ts matched by form factor ONLY,
|
|
-- ignoring speed -> a 400G-OSFP port matched every OSFP transceiver incl. 1.6T.
|
|
-- * 420/687 switches with NULL max_speed_gbps (ports_config present -> derivable).
|
|
-- * 528 transceivers with speed_gbps <= 0 (blocks compat validation; they silently
|
|
-- pass as "compatible" with everything).
|
|
-- * coding-pattern coverage as low as ~13% at 100G (vendor_compat = [] for most).
|
|
--
|
|
-- DESIGN (why this is safe at the "no errors" bar):
|
|
-- The naive ceiling (ports_config-derived max) is UNRELIABLE because some switches'
|
|
-- ports_config is understated (e.g. Cisco C9500-32QC lists 40G but does 100G; NCS 540
|
|
-- "Z" ports list 10G but are 25G-capable; A9K-20HG-FLEX lists 100G but does 400G).
|
|
-- Flagging by that ceiling would create FALSE "incompatible" flags on curated rows.
|
|
-- So the effective ceiling is evidence-based:
|
|
-- effective_max = GREATEST(stored max_speed_gbps,
|
|
-- ports_config-derived max,
|
|
-- fastest curated-compatible transceiver for the switch)
|
|
-- and ONLY algorithmic 'spec_match' fallback rows are ever auto-flagged; curated
|
|
-- ('vendor_matrix'/'vendor_compat') rows are never touched (they instead RAISE the
|
|
-- ceiling, protecting understated switches). Result: 0 curated false positives.
|
|
--
|
|
-- Idempotent + safe to re-run. Applied with psql --single-transaction.
|
|
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
-- 0) Ports-config -> max port speed (pure helper) + evidence-based switch ceiling
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
create or replace function fn_ports_max_gbps(cfg jsonb) returns numeric
|
|
language sql immutable as $fn$
|
|
select case when jsonb_typeof(cfg) = 'object'
|
|
then (select max(substring(k from '([0-9]+)G')::numeric)
|
|
from jsonb_object_keys(cfg) k)
|
|
else null end
|
|
$fn$;
|
|
|
|
comment on function fn_ports_max_gbps(jsonb) is
|
|
'Max port speed (Gbps) parsed from a switch ports_config JSONB object, e.g. {"400G_QSFP-DD":4,"100G_QSFP28":2} -> 400. NULL if config is empty/non-object.';
|
|
|
|
create or replace view v_switch_effective_max as
|
|
select s.id as switch_id,
|
|
s.max_speed_gbps as stored_max_gbps,
|
|
fn_ports_max_gbps(s.ports_config) as ports_max_gbps,
|
|
(select max(t.speed_gbps)
|
|
from compatibility c join transceivers t on t.id = c.transceiver_id
|
|
where c.switch_id = s.id and c.status = 'compatible'
|
|
and c.verification_method in ('vendor_matrix','vendor_compat')
|
|
and t.speed_gbps > 0) as curated_max_gbps,
|
|
greatest(
|
|
coalesce(s.max_speed_gbps, 0),
|
|
coalesce(fn_ports_max_gbps(s.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 = s.id and c.status = 'compatible'
|
|
and c.verification_method in ('vendor_matrix','vendor_compat')
|
|
and t.speed_gbps > 0), 0)
|
|
) as effective_max_gbps
|
|
from switches s;
|
|
|
|
comment on view v_switch_effective_max is
|
|
'Evidence-based max port speed per switch (greatest of stored max_speed_gbps, ports_config-derived, and fastest curated-compatible transceiver). Used to validate compatibility without false-flagging switches whose ports_config is understated.';
|
|
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
-- REMEDIATION A) Backfill max_speed_gbps from ports_config (additive, nulls only).
|
|
-- Highest leverage: unblocks validation for switches that currently pass no check.
|
|
-- Only fills NULLs; never overwrites a curated stored value (61/267 stored values
|
|
-- disagree with ports-derived in both directions -> curated values are preserved).
|
|
-- Switches with an empty {} ports_config stay NULL (need a datasheet -> see alerts).
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
update switches s
|
|
set max_speed_gbps = fn_ports_max_gbps(s.ports_config)
|
|
where s.max_speed_gbps is null
|
|
and fn_ports_max_gbps(s.ports_config) is not null
|
|
and fn_ports_max_gbps(s.ports_config) > 0;
|
|
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
-- REMEDIATION C) Re-derive speed_gbps for transceivers with speed <= 0, HIGH
|
|
-- confidence only. Speed comes from the manufacturer's standardized speed token
|
|
-- in the part number (e.g. SFP-10G-SR -> 10, QDD-2X400G -> 800, QSFP-4SFP25G -> 100),
|
|
-- and is applied ONLY when it is <= the form factor's physical maximum. Parts whose
|
|
-- token exceeds the form-factor cap (usually a wrong form_factor, e.g. QSFP-400G-AOC
|
|
-- mislabelled 'SFP') or that have no speed token are LEFT flagged for datasheet work.
|
|
-- No blind update: FastEthernet "100FX"/"100BASE" (no 'G') never matches.
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
with cand as (
|
|
select id,
|
|
case
|
|
when part_number ~* '([0-9]+)X([0-9]+)G'
|
|
then (regexp_match(part_number,'([0-9]+)X([0-9]+)G','i'))[1]::numeric
|
|
* (regexp_match(part_number,'([0-9]+)X([0-9]+)G','i'))[2]::numeric
|
|
when part_number ~* '([0-9]+)SFP([0-9]+)G'
|
|
then (regexp_match(part_number,'([0-9]+)SFP([0-9]+)G','i'))[1]::numeric
|
|
* (regexp_match(part_number,'([0-9]+)SFP([0-9]+)G','i'))[2]::numeric
|
|
when part_number ~* '([0-9]+)G'
|
|
then (regexp_match(part_number,'([0-9]+)G','i'))[1]::numeric
|
|
end as derived,
|
|
case upper(coalesce(form_factor,''))
|
|
when 'SFP' then 1 when 'SFP+' then 16 when 'SFP28' then 32 when 'SFP56' then 56
|
|
when 'XFP' then 10 when 'QSFP+' then 40 when 'QSFP28' then 100 when 'QSFP56' then 200
|
|
when 'QSFP-DD' then 800 when 'QSFP-DD800' then 800
|
|
when 'OSFP' then 1600 when 'OSFP224' then 1600
|
|
when 'CFP' then 100 when 'CFP2' then 400 when 'CFP4' then 100 when 'CPAK' then 100
|
|
else 99999 end as ff_cap
|
|
from transceivers
|
|
where speed_gbps is null or speed_gbps <= 0)
|
|
update transceivers t
|
|
set speed_gbps = c.derived
|
|
from cand c
|
|
where t.id = c.id
|
|
and c.derived is not null and c.derived > 0
|
|
and c.derived <= c.ff_cap;
|
|
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
-- REMEDIATION B) Flag physically-impossible compatibility rows.
|
|
-- Only 'spec_match' (algorithmic form-factor fallback) rows are flagged; curated
|
|
-- rows are never touched. status='incompatible' (constraint-allowed value) removes
|
|
-- them from every consumer that filters status='compatible' (e.g. queries.ts:302).
|
|
-- Runs AFTER A + C so it uses backfilled ceilings and corrected transceiver speeds.
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
with em as materialized (
|
|
select switch_id, effective_max_gbps from v_switch_effective_max)
|
|
update compatibility c
|
|
set status = 'incompatible',
|
|
notes = case when c.notes is null or c.notes = ''
|
|
then 'auto(125): transceiver speed exceeds switch max port speed'
|
|
else c.notes || ' | auto(125): transceiver speed exceeds switch max port speed' end
|
|
from em e, transceivers t
|
|
where e.switch_id = c.switch_id
|
|
and t.id = c.transceiver_id
|
|
and c.status = 'compatible'
|
|
and c.verification_method = 'spec_match'
|
|
and e.effective_max_gbps > 0
|
|
and t.speed_gbps is not null
|
|
and t.speed_gbps > e.effective_max_gbps;
|
|
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
-- MONITORING VIEWS (read-only). v_switch_quality_alerts is the task list.
|
|
-- ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
-- 1) Impossible rows still CLAIMING compatibility (drains to 0 after remediation B;
|
|
-- lights up again if a future scrape inserts a new impossible spec_match row).
|
|
create or replace view v_compat_speed_errors as
|
|
select c.id as compat_id,
|
|
s.model as switch,
|
|
e.effective_max_gbps as switch_max_gbps,
|
|
t.part_number as transceiver,
|
|
t.speed_gbps as transceiver_gbps,
|
|
round(t.speed_gbps / nullif(e.effective_max_gbps,0), 1) as over_ratio
|
|
from compatibility c
|
|
join v_switch_effective_max e on e.switch_id = c.switch_id
|
|
join switches s on s.id = c.switch_id
|
|
join transceivers t on t.id = c.transceiver_id
|
|
where c.status = 'compatible'
|
|
and c.verification_method = 'spec_match'
|
|
and e.effective_max_gbps > 0
|
|
and t.speed_gbps is not null
|
|
and t.speed_gbps > e.effective_max_gbps;
|
|
|
|
-- 2) Understated switch port data: a CURATED source pairs a transceiver faster than the
|
|
-- switch's stored/ports max. These are NOT errors to auto-fix -- they flag switches
|
|
-- whose ports_config needs datasheet enrichment (or a wrong curated claim to review).
|
|
create or replace view v_switch_understated_ports as
|
|
select c.id as compat_id,
|
|
v.name as manufacturer,
|
|
s.model as switch,
|
|
s.max_speed_gbps as stored_max_gbps,
|
|
fn_ports_max_gbps(s.ports_config) as ports_max_gbps,
|
|
t.part_number as transceiver,
|
|
t.speed_gbps as transceiver_gbps,
|
|
c.verification_method
|
|
from compatibility c
|
|
join switches s on s.id = c.switch_id
|
|
join vendors v on v.id = s.vendor_id
|
|
join transceivers t on t.id = c.transceiver_id
|
|
where c.status = 'compatible'
|
|
and c.verification_method in ('vendor_matrix','vendor_compat')
|
|
and t.speed_gbps is not null
|
|
and (coalesce(s.max_speed_gbps,0) > 0 or coalesce(fn_ports_max_gbps(s.ports_config),0) > 0)
|
|
and t.speed_gbps > greatest(coalesce(s.max_speed_gbps,0), coalesce(fn_ports_max_gbps(s.ports_config),0));
|
|
|
|
-- 3) Coding-pattern coverage per speed tier (module only) -- data-SOURCE gap, pipeline task.
|
|
create or replace view v_coding_coverage as
|
|
select t.speed_gbps::numeric as tier,
|
|
count(*)::int as modules,
|
|
count(*) filter (where jsonb_array_length(coalesce(t.vendor_compat,'[]'::jsonb)) > 0)::int as with_coding,
|
|
round(100.0 * count(*) filter (where jsonb_array_length(coalesce(t.vendor_compat,'[]'::jsonb)) > 0)
|
|
/ count(*), 1) as coverage_pct
|
|
from transceivers t
|
|
where t.product_type = 'module' and t.speed_gbps >= 100
|
|
group by 1 order by 1;
|
|
|
|
-- 4) Transceivers with an impossible speed (<=0). 100 Mbps (0.1) and FC/SONET rates
|
|
-- (0.155/0.622/2.488/8/8.5/14/28/32) are legitimate and intentionally NOT flagged.
|
|
create or replace view v_transceiver_speed_errors as
|
|
select id, part_number, speed_gbps, form_factor
|
|
from transceivers
|
|
where speed_gbps is null or speed_gbps <= 0;
|
|
|
|
-- 5) Switches that cannot validate compatibility (missing max_speed / total_ports).
|
|
create or replace view v_switch_completeness as
|
|
select s.id, v.name as manufacturer, s.model, s.max_speed_gbps, s.total_ports,
|
|
(jsonb_typeof(s.ports_config) = 'object' and s.ports_config <> '{}'::jsonb) as has_ports_config
|
|
from switches s join vendors v on v.id = s.vendor_id
|
|
where s.max_speed_gbps is null or s.total_ports is null;
|
|
|
|
-- 6) One-line alerts = the switch/coding data-quality task list.
|
|
create or replace view v_switch_quality_alerts as
|
|
select 'compatibility: ' || count(*) || ' physically-impossible spec_match rows (transceiver faster than switch)' as alert,
|
|
'critical' as severity from v_compat_speed_errors
|
|
union all
|
|
select 'coding matrix: ' || tier || 'G only ' || coverage_pct || '% populated (' || (modules - with_coding) || ' modules without coding patterns)',
|
|
case when coverage_pct < 25 then 'high' else 'warning' end
|
|
from v_coding_coverage where coverage_pct < 70
|
|
union all
|
|
select 'transceivers: ' || count(*) || ' with speed_gbps <= 0 (need form_factor/datasheet)', 'high'
|
|
from v_transceiver_speed_errors
|
|
union all
|
|
select 'switches: ' || count(*) || ' missing max_speed_gbps (empty ports_config -> needs datasheet)', 'warning'
|
|
from v_switch_completeness where max_speed_gbps is null
|
|
union all
|
|
select 'switches: ' || count(*) || ' with understated port data (curated compat faster than ports_config)', 'warning'
|
|
from v_switch_understated_ports;
|
|
|
|
-- Usage: select * from v_switch_quality_alerts order by severity;
|
|
-- select * from v_compat_speed_errors order by over_ratio desc limit 50;
|
|
-- select * from v_switch_understated_ports order by transceiver_gbps desc;
|
|
|
|
insert into _migrations (filename, applied_at)
|
|
select '125-switch-quality.sql', now()
|
|
where not exists (select 1 from _migrations where filename = '125-switch-quality.sql');
|