◆ Infrastructure layer · consolidation

how-to-schema-consolidation

One clean star schema is the easy case. The real estate is 8,600 tables: every new server cloned the whole schema, the copies quietly drifted apart, and the data dictionary only ever covered the 1,300 logical tables. This skill audits that sprawl and does the one thing you cannot skip before merging - a column-level schema diff across all 9 servers. Real run, seed 42.

8,599
physical tables
9
server-schemas
1,300
logical tables
15%
documented
01
Step 1

Input - an estate that grew by cloning

Everrest scaled by standing up a new server for each region and workload - and every server setup cloned the entire schema. Years later the same logical table exists as up to nine physical copies across nine server-schemas, and nobody documented the clones. Three inputs describe the mess.

catalog.csv 8,599 rows
  • schema · one of 9 servers
  • physical_name · schema.table
  • logical_id · the true table it is a copy of
  • domain · orders, finance, ...
  • n_columns · per physical copy
columns.csv ~91,000 rows
  • schema · server
  • logical_id · fk
  • column_name · differs across copies
  • dtype · sometimes drifts too
data_dictionary.csv 1,300 rows
  • logical_id · pk
  • logical_name · documented name
  • description · what it is
  • owner · team
  • covers logical only, not clones
02
Step 2

Generate sample data - the estate at real scale

A seeded generator builds 1,300 logical tables, replicates each across a random subset of the 9 servers (avg ~6.6 copies), and drifts every copy - dropping optional columns, adding server-unique ones, occasionally changing a dtype. The result is ~8,600 physical tables that look identical and are not.

# replicate each logical table across a random subset of 9 servers
present = rng.random(N_SERVERS) < 0.735          # avg ~6.6 copies -> ~8,600 physical

# drift each copy: optional columns come and go, server-unique columns appear
for c in base_opt[lid]:
    if rng.random() < 0.85: cols.append(c)     # 15% chance a column is missing here
if rng.random() < 0.12: cols.append(f"{schema}_ext_{...}")  # a singleton column
🧬 Scale1,300 logical tables x up to 9 servers = 8,599 physical
📕 Doc gapdictionary covers 1,300 logical - only 15% of physical tables
🔀 Column driftoptional columns present on some servers, missing on others
📍 Singletonsserver-unique columns that exist on exactly one server
⚠️ Type driftsame column, a different dtype on one server (INT vs BIGINT)
♻️ Redundancy7,299 physical tables are clones of a documented logical one
03
Step 3

Objective - merge to one truth, without losing anything

The goal is one governed copy of each table. The trap is that the copies are not identical, so any careless merge either drops columns or fuses incompatible types.

How do we consolidate 8,600 drifted table copies across 9 servers into one trustworthy table each - without silently losing a column that lives on only one server?
  • How bad is the documentation gap, really - what share of the estate is undocumented clones?
  • How much of the estate is pure redundancy we can retire?
  • For a given table, exactly which columns differ across the 9 servers?
  • Which columns exist on only one server - the ones a blind UNION would drop?
  • Where do dtypes disagree, and what is the safe merged superset schema?
04
Step 4

Find-skills - the consolidation toolbox

Cataloging at this scale is a solved problem - reuse the tools. The one piece worth writing is the schema-diff engine that turns "these look the same" into a column-by-column truth.

OSS tool

OpenMetadata

Auto-catalog every physical table and its columns across all 9 servers - close the documentation gap without hand-writing 8,600 entries.

OSS tool

DataHub

Lineage + ownership so a consolidated table records which servers it merged from and who owns the result.

Skill

schema_diff engine

The reusable core (in this repo): compares a logical table across servers, finds singletons and type conflicts, emits a safe merged superset.

OSS engine

DuckDB

Fast set operations over the 91k-row column inventory - union, intersect and group across the whole estate in seconds.

OSS tool

Great Expectations

Once merged, lock the superset schema as a contract so the next server clone cannot silently drift again.

Skill

dataviz discipline

One anomaly color (amber) for "drift / on one server only" across every chart and the diff matrix.

05
Step 5

Build - audit the estate, then diff before you merge

First size the problem across all 9 servers, then zoom into one table and prove exactly how its copies differ. Grab the real code below.

Size the sprawl

Stacked bar: 1300 documented tables versus 7299 undocumented clones
The documentation gap. The dictionary covers 1,300 logical tables - 15% of the 8,599 physical. The other 7,299 are undocumented clones.
Histogram of number of server copies per logical table, peaking at 7
Redundancy. Most logical tables exist on 6-8 servers; 7,299 physical tables are pure clones that consolidation can retire.
Bar chart: each of 9 server-schemas holds about 950 tables
Cloned wholesale. Each server-schema holds ~950 tables - every new server copied the whole estate, not a subset.
Bar chart: almost 100 percent column drift, 56 percent singleton columns, 63 percent dtype conflict
They are not identical. Almost every multi-server table has drifted; 56% have a column on one server only, 63% have a dtype conflict. A blind UNION loses data.

Diff one table across all 9 servers

The step you cannot skip before merging. For logical table L0646, compare every column across the 9 servers that host it. Amber = a column that exists on only one server, or whose dtype disagrees.

Schema diff matrix: columns by 9 servers, present cells in indigo with dtype, singleton and conflict rows in amber

The amber diagonal at the bottom is five server-unique columns - each present on exactly one server. A naive intersection would delete all of them.

Bar chart: common columns 6 versus merged superset 20
CREATE TABLE l0646_merged (
  server_source VARCHAR,   -- lineage
  id BIGINT,
  risk_score VARCHAR,   -- TYPE CONFLICT DATE/TIMESTAMP
  srv03_ext_3 VARCHAR, -- only 1 server
  srv09_ext_3 VARCHAR, -- only 1 server
  warehouse_id VARCHAR, -- TYPE CONFLICT TEXT/VARCHAR
  ...
);

Merge safely. The intersection keeps 6 columns; the superset keeps all 20. The generated DDL unions every column, tags each singleton, and widens type conflicts to a safe type - nothing dropped.

What the audit surfacedFound byResultStatus
Documentation gapcatalog vs dictionary15% covered · 7,299 undocumented✓ quantified
Clone redundancycopies per logical table7,299 retireable copies✓ quantified
Column-presence driftunion vs intersection~100% of multi-server tables✓ caught
Singleton columnsschema_diff engine56% of tables have one✓ caught
Dtype conflictsschema_diff engine63% of tables✓ caught
Safe merge (example L0646)merged superset DDL6 common → 20 superset✓ merged
06
Step 6

Expert review - a check that changes the merge

A panel of five senior reviewer agents - each with 10+ years in data platform, governance and migration - reviewed the first consolidation approach. The naive plan (a UNION of the common columns) ran and looked done, but it would have quietly deleted every server-unique column. Every fix below is in the engine above.

Principal Platform Architect
16 yrs · large migrations

"You cannot merge on the intersection of columns. For L0646 that keeps 6 of 20 - you would drop five columns that exist on exactly one server, and their data with them."

Fix applied: merge on the superset (union), tagging each singleton, so no column is lost.
Data Governance Lead
13 yrs · catalog & lineage

"15% documentation coverage means most of what you are merging is unlabeled. Catalog first, and record which servers each merged row came from."

Fix applied: added a server_source lineage column to every merged table; OpenMetadata cataloging is the step-4 tool.
Senior Data Engineer
12 yrs · pipelines

"A UNION ALL across servers where risk_score is DATE here and TIMESTAMP there will either fail or silently coerce. You have to detect type conflicts before you write the DDL."

Fix applied: schema_diff flags every dtype conflict; the generated DDL widens them to a safe type with a comment.
Migration / Consolidation Lead
11 yrs · server retirement

"Do not merge everything blindly - quantify the redundancy so leadership can approve retiring servers. 7,299 clones is the business case."

Fix applied: redundancy chart quantifies retireable copies per logical table for the decommission plan.
Data QA & Reproducibility
10 yrs · trust

"Prove the diff is exact and repeatable - a fixed seed and a re-runnable engine, not a one-off spreadsheet."

Fix applied: seed 42, deterministic schema_diff.py, importable and runnable on any catalog.
before → after · the merge that lost columns
# v1 (before): merge on columns common to every server
common = set.intersection(*[cols_on(s) for s in servers])   # L0646 -> 6 columns
#   -> silently drops 14 columns, incl. 5 that exist on one server only

# v2 (after): merge on the superset, tag singletons, widen type conflicts
d = diff_table(columns, "L0646")
ddl = merged_ddl(d)                              # -> 20 columns, nothing lost

Run this skill on your own estate

Point it at a catalog export of your servers - it audits coverage and redundancy, then diffs any table across schemas and emits a safe merged superset.

terminal
/plugin marketplace add phoebefu6/phoebe-data-skills
/plugin install how-to-schema-consolidation@phoebe-data-skills