Skip to content

canary environment: Migrate from dbt to mz-deploy#37123

Open
def- wants to merge 1 commit into
MaterializeInc:mainfrom
def-:pr-canary-env-dbt-deploy
Open

canary environment: Migrate from dbt to mz-deploy#37123
def- wants to merge 1 commit into
MaterializeInc:mainfrom
def-:pr-canary-env-dbt-deploy

Conversation

@def-

@def- def- commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

If we want customers to use it so should we.

Edit: Since I did that with Claude I asked it to summarize the issues we ran into:

Bugs

1. apply tables bundles a source-table's companion GRANT into the CREATE TABLE FROM SOURCE

transaction → fails

  • Symptom: applying a newly-created CREATE TABLE FROM SOURCE that has a
    GRANT companion fails with
    transactions which modify objects are restricted to just modifying objects
    (AdapterError::DDLOnlyTransaction).
  • Cause: apply_tables.rs sets transaction_group = Some(source_name) for
    CreateTableFromSource, so the CREATE + index + GRANT run in one
    BEGIN/COMMIT. Materialize won't mix a GRANT with object creation in one
    transaction. apply_sources.rs already does it right
    (transaction_group: None; grants run standalone).
  • Why it's latent: the basic test only grants on a pre-existing table
    (the UpToDate path runs the grant by itself), so it never exercises
    create+grant in one transaction.
  • Fix: emit companion grants/comments as separate (un-grouped) statements,
    like apply_sources does.
  • Location: src/mz-deploy/src/cli/commands/apply_tables.rs (~L95–125).

2. Reserved-word object names are unusable end-to-end

  • Path matching: a file table.sql containing CREATE TABLE "table"
    ObjectNameMismatch, because validate_ident compares stmt.ident().object
    (which re-quotes the keyword → "table") against the unquoted file stem. The
    "rename to table" hint is unsatisfiable (CREATE TABLE table won't parse).
  • As an external dependency: the dependency extractor demands the quoted
    form db.schema."table" in project.toml, but ObjectId::from_str splits
    naively on . with no unquoting, so the declared-dep form, the typecheck
    lookup (unquoted), and lock's catalog query (which searches for the literal
    "table") don't line up. Net: you can't even depend on a reserved-word
    object. We had to leave public_table.table entirely in testdrive.
  • Fix: normalize/unquote identifiers consistently across validate_ident,
    ObjectId::from_str, and the dependency extractor.
  • Location: src/mz-deploy/src/project/compiler/object_validation/identifiers.rs
    (validate_ident, ~L247); src/mz-deploy/src/project/ir/object_id.rs
    (from_str, ~L224).

Missing capabilities

3. CREATE SOURCE ... FROM WEBHOOK isn't accepted

It parses as Statement::CreateWebhookSource, which isn't in the accepted
ProjectStatement set → "unsupported statement type." Webhook sources are a
normal source kind; we had to keep ours in testdrive.

  • Location: src/mz-deploy/src/project/compiler/object_validation.rs
    (accepted-statement match, ~L256–297).

4. Secret resolver only resolves a top-level env_var(...)

decode(env_var('X'), 'base64') passes through unresolved, so you can't
base64-decode an env-provided secret — a problem for a base64-delivered GCP
service-account key (we pre-encoded to bytea-hex in the harness as a
workaround). A base64_decode(...) provider, or resolving nested provider
calls, would fix it.

  • Location: src/mz-deploy/src/secret_resolver.rs (resolve_expr, ~L121).

5. No ${ENV} expansion for project.toml profile variables

Only the profiles.toml password field expands env vars. So
environment-specific connection endpoints (broker, RDS hosts, GCS bucket) can't
come from the environment at deploy time — we rewrite project.toml at runtime.

  • Location: src/mz-deploy/src/config.rs (expand_env_vars).

Papercuts / UX

6. wait (and other long-running commands) produce no visible output off a TTY

The live hydration dashboard is buffered; through docker compose run / CI you
see nothing until the command exits. It should detect non-TTY and emit periodic
plain-text progress lines.

  • Location: src/mz-deploy/src/cli/commands/wait.rs.

7. compile always type-checks, with no --no-typecheck

A fresh checkout of a project with CREATE TABLE FROM SOURCE can't compile
offline without first generating types.lock against a live server (the
upstream columns aren't committable for a CDC/Kafka canary) — awkward for CI and
first-time dev.

  • Location: src/mz-deploy/src/cli/commands/compile.rs.

8. No warning that an apply-managed object's index on a swap-participating cluster is dropped at

promote
A CREATE TABLE FROM SOURCE index placed on the blue/green compute cluster is
destroyed by the cluster swap at promote. A compile-time lint would help.
Possibly by-design, but undocumented.

@def- def- force-pushed the pr-canary-env-dbt-deploy branch 2 times, most recently from 327aa15 to 18d0467 Compare June 18, 2026 12:44
@def- def- force-pushed the pr-canary-env-dbt-deploy branch from 18d0467 to 25087ce Compare June 18, 2026 13:09
@def- def- marked this pull request as ready for review June 18, 2026 13:15
@def- def- requested review from bosconi and sjwiesman June 18, 2026 13:15
@sjwiesman

sjwiesman commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Appreciate you opening this, going to tackle these next week. Are they blockers to merging?

Few comments:

wait (and other long-running commands) produce no visible output off a TTY

Good call out on detecting TTY. If helpful you can use --output=json today to get what you are looking for.

CREATE SOURCE ... FROM WEBHOOK isn't accepted

This is expected. We also don't support sub sources. It simplifies a lot of things to just assume we are in a post create table world. You can however run CREATE TABLE FROM WEBHOOK.

Secret resolver only resolves a top-level env_var(...)

Known, this one is ... hard. Going to think on it.

No warning that an apply-managed object's index on a swap-participating cluster is dropped at

This is a bug and worked at one point 😓 Will add a regression test.

@def-

def- commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Not blockers, I just work around them by using testdrive/SQL directly.

@sjwiesman

sjwiesman commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

@def-

  1. compile always type-checks, with no --no-typecheck
    A fresh checkout of a project with CREATE TABLE FROM SOURCE can't compile
    offline without first generating types.lock against a live server (the
    upstream columns aren't committable for a CDC/Kafka canary) — awkward for CI and
    first-time dev.

It's actually expected you check types.lock into your repo. Like Cargo.lock. Noticed you have it in your .gitignore.

There is also no --no-typecheck flag. Wonder where Claude got confused.

@sjwiesman sjwiesman left a comment

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.

Only one real change.

Comment on lines +5 to +7
# External-dependency column types, regenerated by `mz-deploy apply`/`lock`
# against the live environment (depends on the upstream RDS/Kafka schemas)
types.lock

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.

This is meant to be check in like Cargo.lock file.

Suggested change
# External-dependency column types, regenerated by `mz-deploy apply`/`lock`
# against the live environment (depends on the upstream RDS/Kafka schemas)
types.lock

Comment on lines +246 to +248
by the `create` workflow) and `mz-deploy lock` regenerate it, and it is
git-ignored for that reason. To compile offline without a connection, run
`mz-deploy lock` once against the environment (or hand-write a `types.lock`).

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.

Suggested change
by the `create` workflow) and `mz-deploy lock` regenerate it, and it is
git-ignored for that reason. To compile offline without a connection, run
`mz-deploy lock` once against the environment (or hand-write a `types.lock`).
by the `create` workflow) and `mz-deploy lock` regenerate it.

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