Repository: dbt-labs/dbt-core

Issue:
[CT-1894] Add logging to improve diagnosing of unable to partial parsing because vars have changed
It's difficult to diagnose situations in which partial parsing is aborted due to changes in vars. Add logging to make it easier to determine what changed.


Discussion:

Resolving patch:
diff --git a/core/dbt/events/proto_types.py b/core/dbt/events/proto_types.py
--- a/core/dbt/events/proto_types.py
+++ b/core/dbt/events/proto_types.py
@@ -1,5 +1,5 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
-# sources: core/dbt/events/types.proto
+# sources: types.proto
 # plugin: python-betterproto
 from dataclasses import dataclass
 from datetime import datetime
@@ -1055,6 +1055,23 @@ class UnableToPartialParseMsg(betterproto.Message):
     data: "UnableToPartialParse" = betterproto.message_field(2)
 
 
+@dataclass
+class StateCheckVarsHash(betterproto.Message):
+    """I025"""
+
+    checksum: str = betterproto.string_field(1)
+    vars: str = betterproto.string_field(2)
+    profile: str = betterproto.string_field(3)
+    target: str = betterproto.string_field(4)
+    version: str = betterproto.string_field(5)
+
+
+@dataclass
+class StateCheckVarsHashMsg(betterproto.Message):
+    info: "EventInfo" = betterproto.message_field(1)
+    data: "StateCheckVarsHash" = betterproto.message_field(2)
+
+
 @dataclass
 class PartialParsingNotEnabled(betterproto.Message):
     """I028"""
diff --git a/core/dbt/events/types.py b/core/dbt/events/types.py
--- a/core/dbt/events/types.py
+++ b/core/dbt/events/types.py
@@ -843,6 +843,15 @@ def message(self) -> str:
         return f"Unable to do partial parsing because {self.reason}"
 
 
+@dataclass
+class StateCheckVarsHash(DebugLevel, pt.StateCheckVarsHash):
+    def code(self):
+        return "I025"
+
+    def message(self) -> str:
+        return f"checksum: {self.checksum}, vars: {self.vars}, profile: {self.profile}, target: {self.target}, version: {self.version}"
+
+
 # Skipped I025, I026, I026, I027
 
 
diff --git a/core/dbt/parser/manifest.py b/core/dbt/parser/manifest.py
--- a/core/dbt/parser/manifest.py
+++ b/core/dbt/parser/manifest.py
@@ -8,6 +8,7 @@
 from itertools import chain
 import time
 from dbt.events.base_types import EventLevel
+import pprint
 
 import dbt.exceptions
 import dbt.tracking
@@ -29,6 +30,8 @@
     ParsedFileLoadFailed,
     InvalidDisabledTargetInTestNode,
     NodeNotFoundOrDisabled,
+    StateCheckVarsHash,
+    Note,
 )
 from dbt.logger import DbtProcessState
 from dbt.node_types import NodeType
@@ -569,6 +572,12 @@ def is_partial_parsable(self, manifest: Manifest) -> Tuple[bool, Optional[str]]:
                     reason="config vars, config profile, or config target have changed"
                 )
             )
+            fire_event(
+                Note(
+                    msg=f"previous checksum: {self.manifest.state_check.vars_hash.checksum}, current checksum: {manifest.state_check.vars_hash.checksum}"
+                ),
+                level=EventLevel.DEBUG,
+            )
             valid = False
             reparse_reason = ReparseReason.vars_changed
         if self.manifest.state_check.profile_hash != manifest.state_check.profile_hash:
@@ -702,16 +711,28 @@ def build_manifest_state_check(self):
         # arg vars, but since any changes to that file will cause state_check
         # to not pass, it doesn't matter.  If we move to more granular checking
         # of env_vars, that would need to change.
+        # We are using the parsed cli_vars instead of config.args.vars, in order
+        # to sort them and avoid reparsing because of ordering issues.
+        stringified_cli_vars = pprint.pformat(config.cli_vars)
         vars_hash = FileHash.from_contents(
             "\x00".join(
                 [
-                    getattr(config.args, "vars", "{}") or "{}",
+                    stringified_cli_vars,
                     getattr(config.args, "profile", "") or "",
                     getattr(config.args, "target", "") or "",
                     __version__,
                 ]
             )
         )
+        fire_event(
+            StateCheckVarsHash(
+                checksum=vars_hash.checksum,
+                vars=stringified_cli_vars,
+                profile=config.args.profile,
+                target=config.args.target,
+                version=__version__,
+            )
+        )
 
         # Create a FileHash of the env_vars in the project
         key_list = list(config.project_env_vars.keys())


Test patch:
diff --git a/tests/unit/test_events.py b/tests/unit/test_events.py
--- a/tests/unit/test_events.py
+++ b/tests/unit/test_events.py
@@ -185,6 +185,7 @@ def test_event_codes(self):
     PartialParsingError(exc_info={}),
     PartialParsingSkipParsing(),
     UnableToPartialParse(reason="something went wrong"),
+    StateCheckVarsHash(vars="testing", target="testing", profile="testing"),
     PartialParsingNotEnabled(),
     ParsedFileLoadFailed(path="", exc="", exc_info=""),
     StaticParserCausedJinjaRendering(path=""),