-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix full-refresh and vars for retry #9328
Changes from 14 commits
c118a5e
b398902
62f30d3
6477c45
fc7acbe
c9cdd2f
0fcf0d7
ef54150
75df690
b018326
5215d65
492ad69
1795f12
8b862be
736d35f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
kind: Fixes | ||
body: Preserve the value of vars and the --full-refresh flags when using retry. | ||
time: 2023-12-13T22:04:49.228294-05:00 | ||
custom: | ||
Author: peterallenwebb, ChenyuLInx | ||
Issue: "9112" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
from pathlib import Path | ||
from click import get_current_context | ||
from click.core import ParameterSource | ||
|
||
from dbt.cli.flags import Flags | ||
from dbt.flags import set_flags, get_flags | ||
from dbt.cli.types import Command as CliCommand | ||
from dbt.config import RuntimeConfig | ||
from dbt.contracts.results import NodeStatus | ||
from dbt.contracts.state import PreviousState | ||
from dbt.contracts.state import load_result_state | ||
from dbt.common.exceptions import DbtRuntimeError | ||
from dbt.graph import GraphQueue | ||
from dbt.task.base import ConfiguredTask | ||
|
@@ -17,9 +20,10 @@ | |
from dbt.task.seed import SeedTask | ||
from dbt.task.snapshot import SnapshotTask | ||
from dbt.task.test import TestTask | ||
from dbt.parser.manifest import parse_manifest | ||
|
||
RETRYABLE_STATUSES = {NodeStatus.Error, NodeStatus.Fail, NodeStatus.Skipped, NodeStatus.RuntimeErr} | ||
OVERRIDE_PARENT_FLAGS = { | ||
IGNORE_PARENT_FLAGS = { | ||
"log_path", | ||
"output_path", | ||
"profiles_dir", | ||
|
@@ -28,8 +32,11 @@ | |
"defer_state", | ||
"deprecated_state", | ||
"target_path", | ||
"warn_error", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @graciegoheen how do we feel about
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @peterallenwebb I think we talked about this became a behavior change in my version. But in the latest version this is actually not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is surprising to me. It ties back to the desire to have a consistent policy here. I would assume that all flags passed to the original command are pulled through to Ultimately, if we're worried about breaking something in a backport, we could address this for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So
Just to be super clear this is the current behavior of retry. |
||
} | ||
|
||
ALLOW_OVERRIDE_FLAGS = {"vars"} | ||
|
||
TASK_DICT = { | ||
"build": BuildTask, | ||
"compile": CompileTask, | ||
|
@@ -57,59 +64,64 @@ | |
|
||
class RetryTask(ConfiguredTask): | ||
def __init__(self, args, config, manifest) -> None: | ||
super().__init__(args, config, manifest) | ||
|
||
state_path = self.args.state or self.config.target_path | ||
|
||
if self.args.warn_error: | ||
RETRYABLE_STATUSES.add(NodeStatus.Warn) | ||
|
||
self.previous_state = PreviousState( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not need the full previous state here, just run results. |
||
state_path=Path(state_path), | ||
target_path=Path(self.config.target_path), | ||
project_root=Path(self.config.project_root), | ||
# load previous run results | ||
state_path = args.state or config.target_path | ||
self.previous_results = load_result_state( | ||
Path(config.project_root) / Path(state_path) / "run_results.json" | ||
) | ||
|
||
if not self.previous_state.results: | ||
if not self.previous_results: | ||
raise DbtRuntimeError( | ||
f"Could not find previous run in '{state_path}' target directory" | ||
) | ||
|
||
self.previous_args = self.previous_state.results.args | ||
self.previous_args = self.previous_results.args | ||
self.previous_command_name = self.previous_args.get("which") | ||
self.task_class = TASK_DICT.get(self.previous_command_name) # type: ignore | ||
|
||
def run(self): | ||
unique_ids = set( | ||
[ | ||
result.unique_id | ||
for result in self.previous_state.results.results | ||
if result.status in RETRYABLE_STATUSES | ||
] | ||
) | ||
|
||
cli_command = CMD_DICT.get(self.previous_command_name) | ||
# Reslove flags and config | ||
if args.warn_error: | ||
RETRYABLE_STATUSES.add(NodeStatus.Warn) | ||
|
||
cli_command = CMD_DICT.get(self.previous_command_name) # type: ignore | ||
# Remove these args when their default values are present, otherwise they'll raise an exception | ||
args_to_remove = { | ||
"show": lambda x: True, | ||
"resource_types": lambda x: x == [], | ||
"warn_error_options": lambda x: x == {"exclude": [], "include": []}, | ||
} | ||
|
||
for k, v in args_to_remove.items(): | ||
if k in self.previous_args and v(self.previous_args[k]): | ||
del self.previous_args[k] | ||
|
||
previous_args = { | ||
k: v for k, v in self.previous_args.items() if k not in OVERRIDE_PARENT_FLAGS | ||
k: v for k, v in self.previous_args.items() if k not in IGNORE_PARENT_FLAGS | ||
} | ||
click_context = get_current_context() | ||
current_args = { | ||
k: v | ||
for k, v in args.__dict__.items() | ||
if k in IGNORE_PARENT_FLAGS | ||
or ( | ||
click_context.get_parameter_source(k) == ParameterSource.COMMANDLINE | ||
and k in ALLOW_OVERRIDE_FLAGS | ||
) | ||
} | ||
current_args = {k: v for k, v in self.args.__dict__.items() if k in OVERRIDE_PARENT_FLAGS} | ||
combined_args = {**previous_args, **current_args} | ||
|
||
retry_flags = Flags.from_dict(cli_command, combined_args) | ||
retry_flags = Flags.from_dict(cli_command, combined_args) # type: ignore | ||
set_flags(retry_flags) | ||
retry_config = RuntimeConfig.from_args(args=retry_flags) | ||
|
||
# Parse manifest using resolved config/flags | ||
manifest = parse_manifest(retry_config, False, True, retry_flags.write_json) # type: ignore | ||
super().__init__(args, retry_config, manifest) | ||
self.task_class = TASK_DICT.get(self.previous_command_name) # type: ignore | ||
|
||
def run(self): | ||
unique_ids = set( | ||
[ | ||
result.unique_id | ||
for result in self.previous_results.results | ||
if result.status in RETRYABLE_STATUSES | ||
] | ||
) | ||
|
||
class TaskWrapper(self.task_class): | ||
def get_graph_queue(self): | ||
new_graph = self.graph.get_subset_graph(unique_ids) | ||
|
@@ -120,8 +132,8 @@ def get_graph_queue(self): | |
) | ||
|
||
task = TaskWrapper( | ||
retry_flags, | ||
retry_config, | ||
get_flags(), | ||
self.config, | ||
self.manifest, | ||
) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating the name to better reflect what this list is.