Skip to content

feat: additive schema evolution via add_managed_table#24

Merged
eddietejeda merged 4 commits into
mainfrom
feat/additive-schema-evolution
Jun 30, 2026
Merged

feat: additive schema evolution via add_managed_table#24
eddietejeda merged 4 commits into
mainfrom
feat/additive-schema-evolution

Conversation

@eddietejeda

Copy link
Copy Markdown
Contributor

Summary

Replaces the destination's destructive schema-evolution workaround with an additive table declaration now that the SDK/framework support it.

Previously, when a later run required a table the managed database lacked, ensure_managed_database would: snapshot every existing table into memory → delete the whole database → recreate it with the union of tables → reload all snapshots. This had a data-loss window (non-atomic delete), re-uploaded all data on every new-table run, and round-tripped everything through parquet.

Now it calls HotdataClient.add_managed_table per newly-required table: the table is declared in place on the existing database (added empty, populated by the subsequent load). Existing tables and their data — including dlt's _dlt_version / _dlt_loads / _dlt_pipeline_state bookkeeping — are left untouched.

Changes

  • hotdata_client.py: additive add; removed _reload_table and the snapshot/parquet/tempfile machinery (99 → 70 lines).
  • job_client.py: refreshed the now-stale drop_storage comment.
  • pyproject.toml: hotdata-framework>=0.6.0 (adds add_managed_table).
  • Tests: test_ensure_adds_missing_table_without_recreate (unit) asserts no delete/recreate/reload; in-memory e2e test_schema_evolution_preserves_data now asserts the database id is unchanged across runs. 51 passed.
  • Docs: architecture / runbook / README updated; "New tables" matrix row upgraded ⚠️ → ✅.

⚠️ Blocked on release

Depends on hotdata-framework 0.6.0 (adds add_managed_table) — see hotdata-dev/sdk-python-framework#32. CI will fail until 0.6.0 is on PyPI and uv.lock is regenerated. Draft until then.

Verification (local, against the framework branch)

  • ruff check — clean
  • pytest — 51 passed
  • mypy src/hotdata_dlt_destination/hotdata_client.py — clean

Not changed

Merge/append stay read-modify-write — the SDK load mode is still replace-only. The large-table read-modify-write cost is tracked separately.

Replace the destructive snapshot -> delete-database -> recreate -> reload
dance in ensure_managed_database with an additive add_managed_table call
per newly-required table. New tables are declared in place on the existing
managed database; existing tables and their data (including dlt bookkeeping)
are left untouched.

- hotdata_client.py: declare missing tables in place; drop _reload_table and
  the parquet/tempfile snapshot machinery.
- job_client.py: refresh the now-stale drop_storage comment.
- pyproject: require hotdata-framework>=0.6.0 (adds add_managed_table).
- tests: assert additive add with no recreate (unit + in-memory e2e).
- docs: architecture/runbook/README updated for in-place table declaration.
@eddietejeda eddietejeda marked this pull request as ready for review June 30, 2026 17:20
claude[bot]
claude Bot previously approved these changes Jun 30, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Clean additive schema-evolution swap. Logic preserves existing tables and dlt bookkeeping, the create/KeyError paths are untouched, and both unit and e2e tests meaningfully assert no recreate / stable database id. LGTM.

claude[bot]
claude Bot previously approved these changes Jun 30, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Additive add_managed_table cleanly replaces the destructive recreate. Verified: no broken imports (write_table_parquet still used by job_client.py), unit + e2e tests meaningfully assert no recreate/data movement and a stable db id, and docs are consistent. LGTM.

super nit (not blocking): tests/test_e2e_inmemory.py module docstring (line 7) still says "the data-preserving schema-evolution recreate" — that path is gone now; consider "in-place schema evolution".

Resolve conflicts after the v0.5.0 release (#23) landed on main:
- pyproject: keep version 0.5.0 (main) with hotdata-framework>=0.6.0 (this branch)
- CHANGELOG: keep the [Unreleased] additive-evolution note above the [0.5.0] section
- uv.lock: regenerate for hotdata-framework 0.6.0

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Additive schema evolution via add_managed_table is correct and a clear improvement over the destructive recreate — no data-loss window, no re-upload, and partial-failure recovery is idempotent (already-added tables are filtered by existing). Stale parquet/snapshot machinery removed cleanly, and the parquet helper is still correctly used by the load path. Tests, docs, CHANGELOG, and the 0.6.0 dependency bump are all consistent.

@eddietejeda eddietejeda merged commit cd05116 into main Jun 30, 2026
3 checks passed
@eddietejeda eddietejeda deleted the feat/additive-schema-evolution branch June 30, 2026 18:37
eddietejeda added a commit that referenced this pull request Jun 30, 2026
Resolve CHANGELOG [Unreleased] conflict after #24 merged: combine the
merge schema-preservation Fixed note with #24's additive-evolution and
write-modes Changed notes.
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.

1 participant