Skip to content

Commit 5921b0b

Browse files
cnolanminichDagster Devtools
authored andcommitted
[docs] add declarative automation migration guide (#21988)
Internal-RevId: eb7c7f70c6ec914bd78f6c48fad8d90b2fd9009b
1 parent 2e79ab6 commit 5921b0b

20 files changed

Lines changed: 721 additions & 0 deletions

docs/docs/guides/automate/declarative-automation/migrating-from-sensors.md

Lines changed: 375 additions & 0 deletions
Large diffs are not rendered by default.

examples/docs_snippets/docs_snippets/concepts/declarative_automation/migration/__init__.py

Whitespace-only changes.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import dagster as dg
2+
3+
4+
@dg.asset(
5+
deps=["asset_a", "asset_b"],
6+
automation_condition=dg.AutomationCondition.on_cron("0 * * * *"),
7+
)
8+
def downstream(): ...
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import dagster as dg
2+
3+
# start_all_deps_custom
4+
all_deps_updated_since_target = (
5+
dg.AutomationCondition.all_deps_match(
6+
dg.AutomationCondition.newly_updated().since(
7+
dg.AutomationCondition.asset_matches(
8+
"my_downstream_asset",
9+
dg.AutomationCondition.newly_requested()
10+
| dg.AutomationCondition.newly_updated(),
11+
)
12+
)
13+
)
14+
& ~dg.AutomationCondition.in_progress()
15+
)
16+
# end_all_deps_custom
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import dagster as dg
2+
3+
4+
@dg.asset(
5+
deps=["asset_a", "asset_b"],
6+
automation_condition=dg.AutomationCondition.eager(),
7+
)
8+
def downstream(): ...
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import dagster as dg
2+
3+
4+
@dg.asset(
5+
deps=["raw_data"],
6+
automation_condition=dg.AutomationCondition.eager(),
7+
)
8+
def downstream(): ...
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from datetime import datetime, timezone
2+
3+
import dagster as dg
4+
5+
raw_orders = dg.AssetSpec("raw_orders", group_name="external_db")
6+
raw_customers = dg.AssetSpec("raw_customers", group_name="external_db")
7+
8+
9+
@dg.sensor(minimum_interval_seconds=60)
10+
def db_table_update_sensor(
11+
context: dg.SensorEvaluationContext,
12+
) -> dg.SensorResult:
13+
last_checked = float(context.cursor) if context.cursor else 0
14+
updated_tables = query_information_schema(last_checked)
15+
16+
asset_events = [
17+
dg.AssetMaterialization(
18+
asset_key=table["table_name"],
19+
metadata={
20+
"last_altered": dg.MetadataValue.text(
21+
datetime.fromtimestamp(
22+
table["last_altered"], tz=timezone.utc
23+
).isoformat()
24+
),
25+
},
26+
)
27+
for table in updated_tables
28+
]
29+
30+
new_cursor = (
31+
str(max(t["last_altered"] for t in updated_tables))
32+
if updated_tables
33+
else str(last_checked)
34+
)
35+
36+
return dg.SensorResult(asset_events=asset_events, cursor=new_cursor)
37+
38+
39+
@dg.asset(
40+
deps=[raw_orders, raw_customers],
41+
automation_condition=dg.AutomationCondition.on_cron("0 * * * *"),
42+
)
43+
def sales_report(): ...
44+
45+
46+
@dg.asset(
47+
deps=[raw_orders],
48+
automation_condition=dg.AutomationCondition.eager(),
49+
)
50+
def order_metrics(): ...
51+
52+
53+
def query_information_schema(since_timestamp: float) -> list:
54+
# In practice, this would query your database, e.g.:
55+
# SELECT table_name, last_altered
56+
# FROM information_schema.tables
57+
# WHERE last_altered > :since_timestamp
58+
...
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import dagster as dg
2+
3+
4+
def load_sales_data() -> int:
5+
return 1500
6+
7+
8+
@dg.asset
9+
def daily_sales() -> dg.MaterializeResult:
10+
row_count = load_sales_data()
11+
return dg.MaterializeResult(metadata={"row_count": row_count})
12+
13+
14+
@dg.asset_check(asset="daily_sales")
15+
def sales_row_count_check(
16+
context: dg.AssetCheckExecutionContext,
17+
) -> dg.AssetCheckResult:
18+
event = context.instance.get_latest_materialization_event(
19+
dg.AssetKey("daily_sales")
20+
)
21+
metadata = event.asset_materialization.metadata
22+
row_count = metadata["row_count"].value
23+
passed = row_count > 1000
24+
return dg.AssetCheckResult(
25+
passed=passed,
26+
severity=dg.AssetCheckSeverity.ERROR,
27+
metadata={"row_count": row_count},
28+
)
29+
30+
31+
all_deps_checks_passed = dg.AutomationCondition.all_deps_match(
32+
dg.AutomationCondition.all_checks_match(
33+
~dg.AutomationCondition.check_failed()
34+
| dg.AutomationCondition.will_be_requested(),
35+
)
36+
)
37+
38+
39+
@dg.asset(
40+
deps=["daily_sales"],
41+
automation_condition=dg.AutomationCondition.eager() & all_deps_checks_passed,
42+
)
43+
def analytics(): ...
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import dagster as dg
2+
3+
4+
# start_on_missing
5+
# on_missing: materialize each weekly partition ONCE when all hourly partitions exist.
6+
# Does NOT re-trigger if hourly partitions re-run later.
7+
@dg.asset(
8+
deps=["hourly_asset"],
9+
partitions_def=dg.WeeklyPartitionsDefinition(start_date="2024-01-01"),
10+
automation_condition=dg.AutomationCondition.on_missing(),
11+
)
12+
def weekly_asset_on_missing(): ...
13+
14+
15+
# end_on_missing
16+
17+
18+
# start_eager
19+
# eager: materialize when all hourly partitions exist, AND re-materialize whenever
20+
# any hourly partition updates. This can produce many redundant downstream runs.
21+
@dg.asset(
22+
deps=["hourly_asset"],
23+
partitions_def=dg.WeeklyPartitionsDefinition(start_date="2024-01-01"),
24+
automation_condition=dg.AutomationCondition.eager(),
25+
)
26+
def weekly_asset_eager(): ...
27+
28+
29+
# end_eager
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import dagster as dg
2+
3+
4+
def process_file(file_path: str): ...
5+
6+
7+
@dg.asset(
8+
deps=["raw_data"],
9+
automation_condition=dg.AutomationCondition.eager(),
10+
)
11+
def processed_data(context: dg.AssetExecutionContext):
12+
instance = context.instance
13+
event = instance.get_latest_materialization_event(dg.AssetKey("raw_data"))
14+
metadata = event.asset_materialization.metadata
15+
file_path = metadata["file_path"].value
16+
return process_file(file_path)

0 commit comments

Comments
 (0)