Skip to content

[default values] Spark catalog itest for ReadBridge initial-default overlay - #681

Open
cbb330 wants to merge 2 commits into
chbush/read-bridge-applyfrom
chbush/read-bridge-spark-itest
Open

[default values] Spark catalog itest for ReadBridge initial-default overlay#681
cbb330 wants to merge 2 commits into
chbush/read-bridge-applyfrom
chbush/read-bridge-spark-itest

Conversation

@cbb330

@cbb330 cbb330 commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #679. Spark catalog itest that runs create → insert → ADD COLUMN through the OpenHouse catalog and asserts ReadBridge stamped initial-default on Spark's Iceberg table.

OSS has no deployment ColumnDefaultsSource; the local-server fixture supplies one for d1_column_default so the client overlay is exercised. SQL fill/filter cases are present but @Disabled until OpenHouse bumps Iceberg past 1.2.0.20 (linkedin/iceberg#267–269): that runtime's Spark parquet/ORC readers do not fill initial-default.

Changes

  • Client-facing API Changes
  • Internal API Changes
  • Bug Fixes
  • New Features
  • Performance Improvements
  • Code Style
  • Refactoring
  • Documentation
  • Tests

Adds ColumnDefaultSparkITest and a local-server ColumnDefaultsSource stub (SparkItestColumnDefaults) imported by SpringH2TestApplication.

Testing Done

  • Manually Tested on local docker setup. Please include commands ran, and their output.
  • Added new tests for the changes made.
  • Updated existing tests to reflect the changes made.
  • No tests added or updated. Please explain why. If unsure, please feel free to ask for help.
  • Some other form of testing like staging or soak time in production. Please explain.

./gradlew :integrations:spark:spark-3.1:openhouse-spark-itest:catalogTest --tests com.linkedin.openhouse.spark.catalogtest.ColumnDefaultSparkITest (JDK 11). Overlay test passed; SQL fill test remains disabled.

Additional Information

  • Breaking Changes
  • Deprecations
  • Large PR broken into smaller PRs, and PR plan linked in the description.

Stack

  1. #645 — substrate (seam + encode + decode hook)
  2. #668 — decode off Iceberg’s retry path
  3. #674 — policy / ramp in OpenHouse
  4. #678 — sanitize-on-write
  5. #679ReadBridge.apply
  6. This PR — Spark catalog itest (overlay)

Depends on #679. Enable columnDefaultBackfillViaReadBridge after the Iceberg bump that includes reader fill.

@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 36e818e to d799227 Compare August 13, 2026 05:59
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from d799227 to 610d668 Compare August 13, 2026 06:48
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 610d668 to b1a506a Compare August 13, 2026 22:28
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from b1a506a to 8c170e5 Compare August 13, 2026 22:36
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 8c170e5 to d6dbba3 Compare August 13, 2026 23:56
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from d6dbba3 to a301754 Compare August 14, 2026 04:44
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from a301754 to 2228148 Compare August 14, 2026 20:41
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 2228148 to 601170a Compare August 19, 2026 20:44
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 601170a to b0b6bd0 Compare August 20, 2026 15:08
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from b0b6bd0 to 41d1a76 Compare August 21, 2026 03:31
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 41d1a76 to 82a44f2 Compare August 21, 2026 22:43
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 82a44f2 to 3b03a4a Compare August 21, 2026 23:02
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 3b03a4a to df6c123 Compare August 22, 2026 00:17
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from df6c123 to 327f974 Compare August 22, 2026 00:30
}

static Map<Integer, JsonNode> defaults(TableDto tableDto) {
if (tableDto == null || !DATABASE.equals(tableDto.getDatabaseId())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we handling nulls in test code?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolver never passes a null TableDto. Dropped the null check.

@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 327f974 to 000b355 Compare August 25, 2026 22:59
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch 3 times, most recently from c047856 to 3b81639 Compare August 26, 2026 04:39
cbb330 added a commit that referenced this pull request Aug 27, 2026
#678)

## Summary

Depends on [#674](#674)
(policy), stacked on
[#668](#668) →
[#645](#645).
[#679](#679)
(`ReadBridge.apply`) restacks on this PR.

Read-bridge overlays must not persist. This PR puts the drop on the
**server** *before* apply lands, so there is no deployable cut where
overlays are applied but not stripped.

Default-aware clients send `initial-default` on stamped field-ids (the
handshake). The server gates Type 1 / Type 2, then strips those keys
before Iceberg commit. Unstamped ids keep the writer's defaults.

Until #679, apply is still a no-op; PUTs only carry `initial-default` if
the writer set it. The server is already safe: drop is a no-op when the
handshake is absent, and Type 2 fail-closes unaware rewrites on ramped
tables.

## How strip protection works

| Gate | When | Result |
|---|---|---|
| Type 1 | Ramped table; a previously stamped field-id is still in the
schema but missing from incoming stamps | 400 `COLUMN_DEFAULT_REMOVED` |
| Type 2 | Ramped rewrite (`replace` / `overwrite` on main) while
previous stamps are nonempty | 400 `COLUMN_DEFAULT_REWRITE` unless each
remaining id's `initial-default` JSON equals the stamp |
| Unusable | Ramped write; source throw / unreadable schema / unreadable
snapshots | 400 `COLUMN_DEFAULT_UNUSABLE` |
| Drop | After gates pass | Remove `initial-default` on stamped ids
(same `findParents("id")` walk as the client) |

Unramped tables are a no-op. GET `resolve()` still fail-opens; only the
write path is fail-closed. Awareness is `JsonNode.equals` against the
stamped config, not key presence.

OSS never parses ASL. `ColumnDefaultsSource` is the seam (`NONE` in
OSS); LinkedIn fills it. Type 1 compares stamped field-id sets from the
resolver.

## Changes

- [x] New Features — `ReadBridgeStripProtection.prepare` on PUT table /
snapshots
- [x] Internal API Changes — resolver write APIs `stampedColumnDefaults`
/ `isRampedForCommit`
- [x] Tests — unit Type 1/2/unusable, mock bean for
`MockTablesApplication`, HTTP e2e that GET `initialDefault()` is null
after overlay PUT

## Testing Done

- [x] Added new tests for the changes made.
- [x] Updated existing tests to reflect the changes made.
- Local (Java 17): `./gradlew :services:tables:test --tests
'*ReadBridgeStripProtectionTest' --tests '*ReadBridgeConfigResolverTest'
--tests '*ReadBridgeColumnDefaultE2ETest'`

## Additional Information

- [x] Large PR broken into smaller PRs, and PR plan linked in the
description.

### Stack

1. [#645](#645) — substrate
(seam + encode + decode hook)
2. [#668](#668) — decode off
Iceberg’s retry path
3. [#674](#674) — policy /
ramp in OpenHouse
4. **This PR** — handshake on PUT, Type 1/2, drop before persist
5. [#679](#679) —
`ReadBridge.apply` (merge after this PR)
6. [#681](#681) — Spark
catalog itest (overlay)

### Rollback Plan

Revert this PR. After #679, clients send overlays; without the server
drop they persist in Iceberg metadata.
@cbb330
cbb330 force-pushed the chbush/read-bridge-spark-itest branch from 013dc44 to 69fee6d Compare August 27, 2026 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants