Add /ultraresearch-local for structured research combining local codebase analysis with external knowledge via parallel agent swarms. Produces research briefs with triangulation, confidence ratings, and source quality assessment. New command: /ultraresearch-local with modes --quick, --local, --external, --fg. New agents: research-orchestrator (opus), docs-researcher, community-researcher, security-researcher, contrarian-researcher, gemini-bridge (all sonnet). New template: research-brief-template.md. Integration: --research flag in /ultraplan-local accepts pre-built research briefs (up to 3), enriches the interview and exploration phases. Planning orchestrator cross-references brief findings during synthesis. Design principle: Context Engineering — right information to right agent at right time. Research briefs are structured artifacts in the pipeline: ultraresearch → brief → ultraplan --research → plan → ultraexecute. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
398 lines
16 KiB
Markdown
398 lines
16 KiB
Markdown
# Lakehouse Architecture Design and Patterns
|
|
|
|
**Last updated:** 2026-02
|
|
**Status:** GA
|
|
**Category:** Data Engineering for AI
|
|
|
|
---
|
|
|
|
## Introduksjon
|
|
|
|
Lakehouse-arkitekturen kombinerer de beste egenskapene fra data lakes (fleksibel lagring av strukturerte, semi-strukturerte og ustrukturerte data) med data warehouse-funksjonalitet (ACID-transaksjoner, skjemahåndtering og høy spørringsytelse). Microsoft Fabric standardiserer på Delta Lake-formatet, som gir denne hybridkapabiliteten nativt på tvers av alle Fabric-opplevelser.
|
|
|
|
For AI-løsninger er lakehouse-arkitekturen spesielt verdifull fordi den muliggjør lagring av rådata, mellomtransformasjoner og ferdige feature-sett i ett og samme system -- med full versjonskontroll og tidsreise. Dette eliminerer behovet for separate data lakes og data warehouses, og forenkler dataflytene mellom data engineering og data science-team.
|
|
|
|
Norsk offentlig sektor har strenge krav til datalagring, personvern og sporbarhet. Lakehouse-arkitekturen på Fabric adresserer dette gjennom ACID-garantier, Delta Lake-revisjonssporbarhet (audit trail), og OneLake-basert dataforvaltning som sikrer at data holdes i norsk/europeisk region.
|
|
|
|
---
|
|
|
|
## Delta Lake Transaction Semantics
|
|
|
|
### ACID-egenskaper i Delta Lake
|
|
|
|
Delta Lake gir full ACID-transaksjonsstøtte over Apache Parquet-filer:
|
|
|
|
| Egenskap | Implementasjon | Konsekvens for AI |
|
|
|---|---|---|
|
|
| **Atomicity** | Alle endringer i en transaksjon committes komplett eller ikke i det hele tatt | Treningsdata er alltid konsistent |
|
|
| **Consistency** | Schema enforcement hindrer ugyldig data | Feature-kvalitet opprettholdes |
|
|
| **Isolation** | Serializable isolation via optimistic concurrency | Samtidige les/skriv-operasjoner er trygge |
|
|
| **Durability** | Data persistert til OneLake i Parquet-format | Ingen tap ved feil |
|
|
|
|
### Transaksjonsloggen (_delta_log)
|
|
|
|
```
|
|
lakehouse/Tables/customer_features/
|
|
├── _delta_log/
|
|
│ ├── 00000000000000000000.json # Initial create
|
|
│ ├── 00000000000000000001.json # First insert
|
|
│ ├── 00000000000000000002.json # Update/merge
|
|
│ └── 00000000000000000003.json # Delete
|
|
├── part-00000-*.parquet
|
|
├── part-00001-*.parquet
|
|
└── part-00002-*.parquet
|
|
```
|
|
|
|
Hver JSON-fil i `_delta_log` inneholder:
|
|
- **Add file**: Nye Parquet-filer som legges til
|
|
- **Remove file**: Parquet-filer som logisk fjernes
|
|
- **Metadata**: Skjemaendringer og tabellegenskaper
|
|
- **Protocol**: Minimums reader/writer-versjoner
|
|
|
|
### Concurrent Writes
|
|
|
|
```python
|
|
# Delta Lake håndterer samtidige skrivinger via optimistic concurrency
|
|
# Eksempel: To jobber skriver til samme tabell
|
|
|
|
# Jobb 1: Batch-oppdatering fra Data Factory
|
|
spark.read.format("delta").table("silver.features") \
|
|
.where("region = 'Norway'") \
|
|
.write.format("delta").mode("overwrite").option("replaceWhere", "region = 'Norway'") \
|
|
.saveAsTable("silver.features")
|
|
|
|
# Jobb 2: Streaming append fra Eventstream
|
|
stream.writeStream.format("delta").outputMode("append").toTable("silver.features")
|
|
|
|
# Begge operasjonene kan kjøre samtidig uten konflikter
|
|
# Delta Lake bruker optimistic concurrency control (OCC)
|
|
```
|
|
|
|
---
|
|
|
|
## Schema-on-Read versus Schema-on-Write Tradeoffs
|
|
|
|
### Sammenligning
|
|
|
|
| Aspekt | Schema-on-Write | Schema-on-Read |
|
|
|---|---|---|
|
|
| **Skjemadefinisjon** | Ved skriving (enforce) | Ved lesing (infer) |
|
|
| **Datakvalitet** | Høy -- ugyldig data avvises | Variabel -- feil oppdages sent |
|
|
| **Fleksibilitet** | Lav -- skjemaendringer krever migrering | Høy -- nye felter aksepteres |
|
|
| **Ytelse** | Raskere lesing (kjent skjema) | Tregere lesing (skjemainferens) |
|
|
| **Bruk i Lakehouse** | Silver/Gold-lag | Bronze-lag |
|
|
|
|
### Schema Enforcement i Delta Lake
|
|
|
|
```python
|
|
# Schema enforcement er aktivert som standard
|
|
# Forsøk på å legge til kolonne som ikke finnes feiler:
|
|
try:
|
|
new_data_with_extra_col.write.format("delta").mode("append") \
|
|
.saveAsTable("silver.strict_table")
|
|
except Exception as e:
|
|
print(f"Schema mismatch: {e}")
|
|
|
|
# For å tillate schema evolution:
|
|
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", "true")
|
|
|
|
# Eller per operasjon:
|
|
new_data.write.format("delta") \
|
|
.mode("append") \
|
|
.option("mergeSchema", "true") \
|
|
.saveAsTable("silver.flexible_table")
|
|
```
|
|
|
|
### Anbefalt strategi per lag
|
|
|
|
| Lag | Schema-strategi | Begrunnelse |
|
|
|---|---|---|
|
|
| **Bronze** | Schema-on-read + autoMerge | Aksepter alle kildedata, inkl. nye felter |
|
|
| **Silver** | Schema enforcement med mergeSchema | Kontrollert evolusjon, avvis ugyldig data |
|
|
| **Gold** | Streng schema enforcement | ML-features må ha forutsigbart format |
|
|
|
|
---
|
|
|
|
## Time-Travel and Data Versioning
|
|
|
|
### Tidsreise i Delta Lake
|
|
|
|
Delta Lake lagrer historikk for alle endringer, noe som muliggjør "tidsreise" -- spørring mot tidligere versjoner av data.
|
|
|
|
```python
|
|
# Les en spesifikk versjon
|
|
df_v0 = spark.read.format("delta").option("versionAsOf", 0).load("Tables/customer_features")
|
|
df_v5 = spark.read.format("delta").option("versionAsOf", 5).load("Tables/customer_features")
|
|
|
|
# Les data slik de var på et gitt tidspunkt
|
|
df_yesterday = spark.read.format("delta") \
|
|
.option("timestampAsOf", "2026-02-10T00:00:00Z") \
|
|
.load("Tables/customer_features")
|
|
|
|
# Vis historikk
|
|
from delta.tables import DeltaTable
|
|
dt = DeltaTable.forPath(spark, "Tables/customer_features")
|
|
history = dt.history()
|
|
display(history.select("version", "timestamp", "operation", "operationMetrics"))
|
|
```
|
|
|
|
### Bruksområder for tidsreise i AI
|
|
|
|
| Bruksområde | Teknikk | Eksempel |
|
|
|---|---|---|
|
|
| **Reproduserbar trening** | `versionAsOf` | Tren modell på eksakt samme data |
|
|
| **Feature drift-analyse** | Sammenlign versjoner | Finn endringer i feature-distribusjon |
|
|
| **Rollback** | `RESTORE TABLE` | Angre feilaktig dataoppdatering |
|
|
| **Audit trail** | `DESCRIBE HISTORY` | Dokumenter alle dataendringer |
|
|
| **Point-in-time lookup** | `timestampAsOf` | Feature lookup for historisk inferens |
|
|
|
|
### Rollback
|
|
|
|
```sql
|
|
-- SQL: Gjenopprett tabell til versjon 3
|
|
RESTORE TABLE silver.customer_features TO VERSION AS OF 3;
|
|
|
|
-- Eller til et tidspunkt
|
|
RESTORE TABLE silver.customer_features TO TIMESTAMP AS OF '2026-02-01T00:00:00Z';
|
|
```
|
|
|
|
```python
|
|
# PySpark: Rollback
|
|
dt = DeltaTable.forPath(spark, "Tables/silver/customer_features")
|
|
dt.restoreToVersion(3)
|
|
```
|
|
|
|
### Retensjons- og VACUUM-policy
|
|
|
|
```python
|
|
# Fjern gamle filer (frigjør lagring, fjerner tidsreise-mulighet)
|
|
# Standard: 7 dager
|
|
dt.vacuum(168) # Timer (7 * 24)
|
|
|
|
# For å sette annen retensjon:
|
|
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "false")
|
|
dt.vacuum(24) # Behold kun siste 24 timer
|
|
|
|
# VIKTIG: Etter VACUUM kan du ikke tidsreise til slettede versjoner
|
|
```
|
|
|
|
---
|
|
|
|
## Upsert and Merge Patterns for Slowly-Changing Dimensions
|
|
|
|
### MERGE (Upsert) Operasjoner
|
|
|
|
Delta Lake MERGE støtter SQL-standard og utvidet syntaks:
|
|
|
|
```python
|
|
from delta.tables import DeltaTable
|
|
|
|
# Target: eksisterende dimensjonstabell
|
|
target = DeltaTable.forPath(spark, "Tables/silver/dim_customer")
|
|
|
|
# Source: nye/endrede rader
|
|
source = spark.read.format("delta").table("bronze.crm_customers")
|
|
|
|
# MERGE: SCD Type 1 (overskrive)
|
|
target.alias("t").merge(
|
|
source.alias("s"),
|
|
"t.customer_id = s.customer_id"
|
|
).whenMatchedUpdate(
|
|
set={
|
|
"customer_name": "s.customer_name",
|
|
"email": "s.email",
|
|
"phone": "s.phone",
|
|
"updated_at": "current_timestamp()"
|
|
}
|
|
).whenNotMatchedInsert(
|
|
values={
|
|
"customer_id": "s.customer_id",
|
|
"customer_name": "s.customer_name",
|
|
"email": "s.email",
|
|
"phone": "s.phone",
|
|
"created_at": "current_timestamp()",
|
|
"updated_at": "current_timestamp()"
|
|
}
|
|
).execute()
|
|
```
|
|
|
|
### SCD Type 2 (historikk-bevaring)
|
|
|
|
```python
|
|
from pyspark.sql.functions import current_timestamp, lit, col
|
|
|
|
# SCD Type 2: Bevar full historikk
|
|
# Steg 1: Identifiser endrede rader
|
|
changes = source.alias("s").join(
|
|
target.toDF().alias("t"),
|
|
(col("s.customer_id") == col("t.customer_id")) &
|
|
(col("t.is_current") == True),
|
|
"inner"
|
|
).where(
|
|
(col("s.customer_name") != col("t.customer_name")) |
|
|
(col("s.email") != col("t.email"))
|
|
).select("s.*")
|
|
|
|
# Steg 2: Lukk eksisterende rader
|
|
target.alias("t").merge(
|
|
changes.alias("s"),
|
|
"t.customer_id = s.customer_id AND t.is_current = true"
|
|
).whenMatchedUpdate(
|
|
set={
|
|
"is_current": lit(False),
|
|
"end_date": current_timestamp()
|
|
}
|
|
).execute()
|
|
|
|
# Steg 3: Sett inn nye versjoner
|
|
new_rows = changes.withColumn("is_current", lit(True)) \
|
|
.withColumn("start_date", current_timestamp()) \
|
|
.withColumn("end_date", lit(None).cast("timestamp"))
|
|
|
|
new_rows.write.format("delta").mode("append").saveAsTable("silver.dim_customer")
|
|
```
|
|
|
|
### SQL MERGE-syntaks
|
|
|
|
```sql
|
|
-- SQL-ekvivalent for upsert
|
|
MERGE INTO silver.dim_customer AS target
|
|
USING bronze.crm_customers AS source
|
|
ON target.customer_id = source.customer_id
|
|
WHEN MATCHED THEN
|
|
UPDATE SET
|
|
target.customer_name = source.customer_name,
|
|
target.email = source.email,
|
|
target.updated_at = current_timestamp()
|
|
WHEN NOT MATCHED THEN
|
|
INSERT (customer_id, customer_name, email, created_at, updated_at)
|
|
VALUES (source.customer_id, source.customer_name, source.email,
|
|
current_timestamp(), current_timestamp())
|
|
WHEN NOT MATCHED BY SOURCE AND target.is_active = true THEN
|
|
UPDATE SET target.is_active = false;
|
|
```
|
|
|
|
---
|
|
|
|
## Lakehouse Performance Tuning
|
|
|
|
### V-Order Optimalisering
|
|
|
|
Fabric bruker V-Order som standard ved skriving til Delta-tabeller. V-Order er en skrive-tids-optimalisering av Parquet-filer som gir raskere lesing:
|
|
|
|
| Optimalisering | Effekt | Automatisk i Fabric |
|
|
|---|---|---|
|
|
| **V-Order** | Raskere les for alle Fabric-engines | Ja |
|
|
| **Bin Compaction** | Slår sammen små filer | Manuell eller planlagt |
|
|
| **Z-Order** | Organiserer data for raskere filtrering | Manuell |
|
|
| **Deletion Vectors** | Raskere DELETE/UPDATE uten rewrite | Ja (Runtime 1.2+) |
|
|
| **Liquid Clustering** | Automatisk dataorganisering (preview) | Manuell aktivering |
|
|
|
|
### Table Maintenance
|
|
|
|
```sql
|
|
-- Optimaliser tabell (bin compaction + V-Order)
|
|
OPTIMIZE silver.customer_features;
|
|
|
|
-- Z-Order for ofte filtrerte kolonner
|
|
OPTIMIZE silver.customer_features
|
|
ZORDER BY (region, customer_segment);
|
|
|
|
-- Fjern gamle filer
|
|
VACUUM silver.customer_features RETAIN 168 HOURS;
|
|
|
|
-- Analyser tabell for statistikk
|
|
ANALYZE TABLE silver.customer_features COMPUTE STATISTICS;
|
|
```
|
|
|
|
### Partisjoneringsstrategier
|
|
|
|
| Strategi | Anbefalt for | Eksempel |
|
|
|---|---|---|
|
|
| **Dato-partisjonering** | Tidsseriedata, inkrementelle laster | `PARTITIONED BY (date)` |
|
|
| **Region-partisjonering** | Geografisk filtrering, RLS | `PARTITIONED BY (region)` |
|
|
| **Z-Order** | Multi-kolonne filtrering | `ZORDER BY (customer_id, date)` |
|
|
| **Liquid Clustering** | Dynamisk endring av klyngenøkler | `CLUSTER BY (customer_id)` |
|
|
| **Ingen partisjonering** | Tabeller < 1 GB | Standard |
|
|
|
|
### Performance Best Practices
|
|
|
|
```python
|
|
# 1. Bruk predicate pushdown
|
|
# Dårlig: Les alt, filtrer etterpå
|
|
df = spark.read.format("delta").table("silver.events").filter("date = '2026-02-10'")
|
|
|
|
# Bedre: Partition pruning (hvis partisjonert på date)
|
|
# Delta Lake hopper automatisk over irrelevante partisjoner
|
|
|
|
# 2. Bruk column pruning
|
|
# Dårlig: Velg alle kolonner
|
|
df = spark.read.format("delta").table("gold.features")
|
|
|
|
# Bedre: Velg kun nødvendige kolonner
|
|
df = spark.read.format("delta").table("gold.features").select("feature_1", "feature_2", "target")
|
|
|
|
# 3. Cache for gjentatt bruk
|
|
df_cached = spark.read.format("delta").table("gold.ml_features").cache()
|
|
df_cached.count() # Trigger caching
|
|
|
|
# 4. Optimalisert skriving
|
|
spark.conf.set("spark.microsoft.delta.optimizeWrite.enabled", "true")
|
|
spark.conf.set("spark.microsoft.delta.optimizeWrite.binSize", "128") # MB per fil
|
|
```
|
|
|
|
---
|
|
|
|
## Medallion Architecture Deployment
|
|
|
|
### Pattern 1: Alle lag som Lakehouses
|
|
|
|
```
|
|
Workspace: Bronze-LH Workspace: Silver-LH Workspace: Gold-LH
|
|
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
|
|
│ Raw data │ │ Validated data │ │ Business-ready │
|
|
│ - Original format │──>│ - Deduplicated │──>│ - Aggregated │
|
|
│ - Minimal transform│ │ - Typed columns │ │ - Feature tables │
|
|
│ - Append-only │ │ - Referential int. │ │ - Semantic models │
|
|
└────────────────────┘ └────────────────────┘ └────────────────────┘
|
|
│ │ │
|
|
SQL Endpoint SQL Endpoint SQL Endpoint
|
|
(read-only) (read-only) (read-only)
|
|
```
|
|
|
|
### Pattern 2: Bronze+Silver Lakehouse, Gold Data Warehouse
|
|
|
|
```
|
|
Lakehouse (Bronze + Silver) Data Warehouse (Gold)
|
|
┌────────────────────────────┐ ┌────────────────────────┐
|
|
│ bronze.raw_events │ │ gold.fact_predictions │
|
|
│ bronze.raw_transactions │ │ gold.dim_customer │
|
|
│ silver.validated_events │─────>│ gold.dim_product │
|
|
│ silver.customer_360 │ │ gold.agg_daily_metrics │
|
|
│ silver.feature_base │ │ Stored Procedures │
|
|
└────────────────────────────┘ │ Views, Functions │
|
|
└────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Referanser
|
|
|
|
- [What is a Lakehouse in Microsoft Fabric?](https://learn.microsoft.com/en-us/fabric/data-engineering/lakehouse-overview) -- Oversikt over Fabric Lakehouse
|
|
- [Understand medallion lakehouse architecture for Microsoft Fabric](https://learn.microsoft.com/en-us/fabric/onelake/onelake-medallion-lakehouse-architecture) -- Medallion-arkitektur
|
|
- [Delta Lake table format interoperability](https://learn.microsoft.com/en-us/fabric/fundamentals/delta-lake-interoperability) -- Delta Lake-kompatibilitet på tvers av Fabric
|
|
- [Better together: the Lakehouse and Warehouse](https://learn.microsoft.com/en-us/fabric/data-warehouse/get-started-lakehouse-sql-analytics-endpoint) -- Kombinasjon av Lakehouse og Warehouse
|
|
- [Greenfield lakehouse on Microsoft Fabric](https://learn.microsoft.com/en-us/azure/architecture/example-scenario/data/greenfield-lakehouse-fabric) -- Referansearkitektur
|
|
- [Upsert into a Delta Lake table using merge](https://learn.microsoft.com/en-us/azure/databricks/delta/merge) -- MERGE-syntaks og mønstre
|
|
- [Delta Lake table optimization and V-Order](https://learn.microsoft.com/en-us/fabric/data-engineering/delta-optimization-and-v-order) -- Ytelsesoptimalisering
|
|
- [Lakehouse end-to-end scenario](https://learn.microsoft.com/en-us/fabric/data-engineering/tutorial-lakehouse-introduction) -- Komplett tutorial
|
|
|
|
---
|
|
|
|
## For Cosmo
|
|
|
|
- **Bruk denne referansen** når brukeren planlegger en ny dataarkitektur på Fabric, vurderer lakehouse vs warehouse, eller trenger mønstre for SCD, MERGE og Delta Lake-optimalisering for AI-treningsdata.
|
|
- Anbefal **medallion architecture** (bronze/silver/gold) som standard for alle AI-prosjekter. Bronze for rådata, Silver for validert/denormalisert data, Gold for ML-features og aggregater.
|
|
- For norsk offentlig sektor: Fremhev **tidsreise** som et viktig verktøy for revisjonsporbarhet og etterlevelse av Utredningsinstruksen -- alle dataendringer er sporbare og reverserbare.
|
|
- **V-Order** er aktivert som standard i Fabric og gir 2-3x raskere lesing for Power BI Direct Lake. Ikke deaktiver dette med mindre det er spesifikke grunner.
|
|
- Bruk **separate workspaces** per lag (bronze, silver, gold) for bedre tilgangskontroll og governance, spesielt i organisasjoner med flere team som deler data.
|