Skip to content

feat(snowflake): add scheduler support for dynamic tables #1710

Open
igorbelianski-cyber wants to merge 5 commits intodbt-labs:mainfrom
igorbelianski-cyber:ibelianski-snowflake-dynamic-table-schedile-singleton-clean
Open

feat(snowflake): add scheduler support for dynamic tables #1710
igorbelianski-cyber wants to merge 5 commits intodbt-labs:mainfrom
igorbelianski-cyber:ibelianski-snowflake-dynamic-table-schedile-singleton-clean

Conversation

@igorbelianski-cyber
Copy link
Contributor

Add support for the Snowflake dynamic table scheduler property, which controls whether the internal refresh scheduler is ENABLE or DISABLE. This is distinct from scheduling_state (ACTIVE/SUSPENDED), which is a Snowflake-managed runtime status.

Key changes:

  • Add Scheduler enum (ENABLE/DISABLE) and make target_lag optional in SnowflakeDynamicTableConfig to support scheduler=DISABLE without a target lag specification.

  • Apply implicit scheduler defaults in both parse_relation_config and parse_relation_results: when scheduler is not explicitly set, default to ENABLE if target_lag is present, DISABLE otherwise. This aligns the Python-side change detection logic with the Jinja macro defaults.

  • Add scheduler change detection in dynamic_table_config_changeset and prevent target_lag from being included in ALTER when its new value is None (avoids generating target_lag = 'None' in SQL).

  • Update CREATE, REPLACE, and ALTER macros to render scheduler in DDL with correct ENABLE/DISABLE defaults. Guard ALTER target_lag rendering to skip when context is None.

  • Update materialization to issue ALTER DYNAMIC TABLE ... REFRESH when scheduler is DISABLE (explicit or implicit via missing target_lag), since Snowflake won't auto-refresh in that case.

  • Read the scheduler column from SHOW DYNAMIC TABLES when available.

  • Fix FileSystemLoader in test_alter_relation_comment_macro.py to use an absolute path, avoiding TemplateNotFound errors from different CWDs.

  • Add comprehensive unit tests for scheduler config parsing, changeset detection, and change detection logic, plus functional tests covering scheduler in CREATE/ALTER/REPLACE DDL and refresh behavior.

Made-with: Cursor

resolves #
docs dbt-labs/docs.getdbt.com/#

Problem

Solution

Checklist

  • I have read the contributing guide and understand what's expected of me
  • I have run this code in development and it appears to resolve the stated issue
  • This PR includes tests, or tests are not required/relevant for this PR
  • This PR has no interface changes (e.g. macros, cli, logs, json artifacts, config files, adapter interface, etc) or this PR has already received feedback and approval from Product or DX

@igorbelianski-cyber igorbelianski-cyber requested a review from a team as a code owner March 4, 2026 02:10
@cla-bot cla-bot bot added the cla:yes The PR author has signed the CLA label Mar 4, 2026
Add support for the Snowflake dynamic table `scheduler` property, which
controls whether the internal refresh scheduler is ENABLE or DISABLE.
This is distinct from `scheduling_state` (ACTIVE/SUSPENDED), which is a
Snowflake-managed runtime status.

Key changes:

- Add `Scheduler` enum (ENABLE/DISABLE) and make `target_lag` optional
  in `SnowflakeDynamicTableConfig` to support scheduler=DISABLE without
  a target lag specification.

- Apply implicit scheduler defaults in both `parse_relation_config` and
  `parse_relation_results`: when scheduler is not explicitly set, default
  to ENABLE if target_lag is present, DISABLE otherwise. This aligns the
  Python-side change detection logic with the Jinja macro defaults.

- Add scheduler change detection in `dynamic_table_config_changeset` and
  prevent target_lag from being included in ALTER when its new value is
  None (avoids generating `target_lag = 'None'` in SQL).

- Update CREATE, REPLACE, and ALTER macros to render `scheduler` in DDL
  with correct ENABLE/DISABLE defaults. Guard ALTER target_lag rendering
  to skip when context is None.

- Update materialization to issue `ALTER DYNAMIC TABLE ... REFRESH` when
  scheduler is DISABLE (explicit or implicit via missing target_lag),
  since Snowflake won't auto-refresh in that case.

- Read the `scheduler` column from SHOW DYNAMIC TABLES when available.

- Fix FileSystemLoader in test_alter_relation_comment_macro.py to use an
  absolute path, avoiding TemplateNotFound errors from different CWDs.

- Add comprehensive unit tests for scheduler config parsing, changeset
  detection, and change detection logic, plus functional tests covering
  scheduler in CREATE/ALTER/REPLACE DDL and refresh behavior.

Made-with: Cursor
@igorbelianski-cyber igorbelianski-cyber force-pushed the ibelianski-snowflake-dynamic-table-schedile-singleton-clean branch from a4393d5 to 3b2447d Compare March 4, 2026 02:12
@github-actions
Copy link
Contributor

Thank you for your pull request! We could not find a changelog entry for this change in the dbt-snowflake package. For details on how to document a change, see the Contributing Guide.

if scheduler := relation_config.config.extra.get("scheduler"): # type:ignore
config_dict["scheduler"] = scheduler.upper()
elif config_dict.get("target_lag"):
config_dict["scheduler"] = "ENABLE"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use the enum values?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

scheduler = dynamic_table.get("scheduler")
target_lag = dynamic_table.get("target_lag")
if scheduler is None:
scheduler = "ENABLE" if target_lag else "DISABLE"
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

query: str
target_lag: str
snowflake_warehouse: str
target_lag: Optional[str] = None
Copy link
Contributor

Choose a reason for hiding this comment

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

target_lag is still required if scheduler is enabled right? We should raise a validation exception in that case.

Copy link
Contributor Author

@igorbelianski-cyber igorbelianski-cyber Mar 17, 2026

Choose a reason for hiding this comment

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

target lag is not mandatory any more
with scheduler = DISABLED , target_lag will not be allowed ( we treat dynamic tables as a unit of incrementalization and completely disconnected from the dynamic table built in scheduler)
with scheduler = ENABLED it is target lag is still mandatory

PS
i was debating whether DBT needs to validate it as opposed allowing snowflake to produce appropriate precise error ( implementing it here seem like extra validation effort that doesn't add a lot of value and make chnages (if snowflake adds some new modes hard to version)

return cls("ON_CREATE")


class Scheduler(StrEnum):
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if there's actually three states:

  1. DISABLE - we don't want the MV to be updated
  2. ENABLE - require target_lag (default state today
  3. MANUAL - dbt runs the refresh manually on every run

Copy link
Contributor Author

@igorbelianski-cyber igorbelianski-cyber Mar 17, 2026

Choose a reason for hiding this comment

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

There are 2 states:
ENABLE ( default like Dynamic TAbles /MV ) work today subject to be scheduled by Snowflake directly or part of the downstream schedule
DISABLED ( only manual refreshes allowed , Dynamic table scheduler will never touch it. directly or through upstream dependency). => DT's

i do not see a meaningful scenario to justify 3rd mode where we want to disable Dynamic tables from both DBT refreshes and Snowflake Dynamic table scheduler.
( i guess target_lag = downstream on all DTs in the pipeline achieves similar effect functionally , but i am not seeing meaningful scenario for that case)

moreover 2 modes provide really simple syntax for users ( they just need to specify warehouse, absence of target lag implies disabling Dynmic table scheduler and DBT running refreshes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla:yes The PR author has signed the CLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants