|
17 | 17 | HistoricalAllPartitionsSubsetSentinel, |
18 | 18 | ) |
19 | 19 | from dagster._core.definitions.partitions.definition import ( |
| 20 | + DynamicPartitionsDefinition, |
20 | 21 | PartitionsDefinition, |
21 | 22 | StaticPartitionsDefinition, |
22 | 23 | ) |
| 24 | +from dagster._core.definitions.partitions.partition_key_range import PartitionKeyRange |
| 25 | +from dagster._core.definitions.partitions.snap import PartitionsSnap |
| 26 | +from dagster._core.definitions.partitions.subset.key_ranges import KeyRangesPartitionsSubset |
23 | 27 | from dagster._core.definitions.run_request import InstigatorType |
24 | 28 | from dagster._core.definitions.sensor_definition import SensorType |
| 29 | +from dagster._core.errors import DagsterInvalidInvocationError |
25 | 30 | from dagster._core.instance import DagsterInstance |
26 | 31 | from dagster._core.remote_origin import RemoteInstigatorOrigin |
27 | 32 | from dagster._core.scheduler.instigation import ( |
@@ -527,6 +532,34 @@ def _get_condition_evaluation( |
527 | 532 | subsets_with_metadata=[], |
528 | 533 | ) |
529 | 534 |
|
| 535 | + def _get_key_range_condition_evaluation( |
| 536 | + self, |
| 537 | + asset_key: AssetKey, |
| 538 | + description: str, |
| 539 | + partition_key_ranges: Sequence[PartitionKeyRange], |
| 540 | + ) -> AutomationConditionEvaluation: |
| 541 | + partitions_snap = PartitionsSnap.from_def(DynamicPartitionsDefinition(name="dynamic")) |
| 542 | + subset = SerializableEntitySubset( |
| 543 | + key=asset_key, |
| 544 | + value=KeyRangesPartitionsSubset( |
| 545 | + partitions_snap=partitions_snap, |
| 546 | + key_ranges=partition_key_ranges, |
| 547 | + ), |
| 548 | + ) |
| 549 | + return AutomationConditionEvaluation( |
| 550 | + condition_snapshot=AutomationConditionNodeSnapshot( |
| 551 | + class_name="...", |
| 552 | + description=description, |
| 553 | + unique_id=str(random.randint(0, 100000000)), |
| 554 | + ), |
| 555 | + true_subset=subset, |
| 556 | + candidate_subset=HistoricalAllPartitionsSubsetSentinel(), |
| 557 | + start_timestamp=123, |
| 558 | + end_timestamp=456, |
| 559 | + child_evaluations=[], |
| 560 | + subsets_with_metadata=[], |
| 561 | + ) |
| 562 | + |
530 | 563 | def test_get_evaluations_with_partitions(self, graphql_context: WorkspaceRequestContext): |
531 | 564 | asset_key = AssetKey("upstream_static_partitioned_asset") |
532 | 565 | partitions_def = StaticPartitionsDefinition(["a", "b", "c", "d", "e", "f"]) |
@@ -822,6 +855,86 @@ def _get_node(id): |
822 | 855 | "d", |
823 | 856 | } |
824 | 857 |
|
| 858 | + def test_get_evaluations_tolerates_deleted_historical_partition_size( |
| 859 | + self, graphql_context: WorkspaceRequestContext |
| 860 | + ): |
| 861 | + asset_key = AssetKey("dynamic_partitioned_asset") |
| 862 | + graphql_context.instance.add_dynamic_partitions("dynamic", ["a"]) |
| 863 | + evaluation = self._get_key_range_condition_evaluation( |
| 864 | + asset_key, |
| 865 | + "stale historical subset", |
| 866 | + [PartitionKeyRange("a", "a")], |
| 867 | + ) |
| 868 | + |
| 869 | + check.not_none( |
| 870 | + graphql_context.instance.schedule_storage |
| 871 | + ).add_auto_materialize_asset_evaluations( |
| 872 | + evaluation_id=201, |
| 873 | + asset_evaluations=[ |
| 874 | + AutomationConditionEvaluationWithRunIds(evaluation=evaluation, run_ids=frozenset()) |
| 875 | + ], |
| 876 | + ) |
| 877 | + |
| 878 | + with patch.object( |
| 879 | + KeyRangesPartitionsSubset, |
| 880 | + "__len__", |
| 881 | + side_effect=DagsterInvalidInvocationError("Partition key was deleted."), |
| 882 | + ): |
| 883 | + results = execute_dagster_graphql( |
| 884 | + graphql_context, |
| 885 | + QUERY, |
| 886 | + variables={ |
| 887 | + "assetKey": {"path": ["dynamic_partitioned_asset"]}, |
| 888 | + "limit": 10, |
| 889 | + "cursor": None, |
| 890 | + }, |
| 891 | + ) |
| 892 | + |
| 893 | + assert not results.errors |
| 894 | + records = results.data["assetConditionEvaluationRecordsOrError"]["records"] |
| 895 | + assert len(records) == 1 |
| 896 | + assert records[0]["numRequested"] == 0 |
| 897 | + assert records[0]["evaluationNodes"][0]["numTrue"] == 0 |
| 898 | + |
| 899 | + def test_get_partition_evaluation_tolerates_deleted_historical_partition_membership( |
| 900 | + self, graphql_context: WorkspaceRequestContext |
| 901 | + ): |
| 902 | + asset_key = AssetKey("dynamic_partitioned_asset") |
| 903 | + graphql_context.instance.add_dynamic_partitions("dynamic", ["a"]) |
| 904 | + evaluation = self._get_key_range_condition_evaluation( |
| 905 | + asset_key, |
| 906 | + "stale historical subset", |
| 907 | + [PartitionKeyRange("a", "a")], |
| 908 | + ) |
| 909 | + |
| 910 | + check.not_none( |
| 911 | + graphql_context.instance.schedule_storage |
| 912 | + ).add_auto_materialize_asset_evaluations( |
| 913 | + evaluation_id=202, |
| 914 | + asset_evaluations=[ |
| 915 | + AutomationConditionEvaluationWithRunIds(evaluation=evaluation, run_ids=frozenset()) |
| 916 | + ], |
| 917 | + ) |
| 918 | + |
| 919 | + with patch.object( |
| 920 | + KeyRangesPartitionsSubset, |
| 921 | + "__contains__", |
| 922 | + side_effect=DagsterInvalidInvocationError("Partition key was deleted."), |
| 923 | + ): |
| 924 | + results = execute_dagster_graphql( |
| 925 | + graphql_context, |
| 926 | + LEGACY_QUERY_FOR_SPECIFIC_PARTITION, |
| 927 | + variables={ |
| 928 | + "assetKey": {"path": ["dynamic_partitioned_asset"]}, |
| 929 | + "partition": "a", |
| 930 | + "evaluationId": 202, |
| 931 | + }, |
| 932 | + ) |
| 933 | + |
| 934 | + assert not results.errors |
| 935 | + evaluation = results.data["assetConditionEvaluationForPartition"] |
| 936 | + assert evaluation["evaluationNodes"][0]["status"] == "FALSE" |
| 937 | + |
825 | 938 | def test_since_metadata_field(self, graphql_context: WorkspaceRequestContext): |
826 | 939 | """Test that the sinceMetadata field is correctly populated for SinceCondition evaluations.""" |
827 | 940 | asset_key = AssetKey("test_asset") |
|
0 commit comments