Browse Source

fix(functions): Truncate functions fingerprint to 32 bits (#52475)

Snuba doesn't handle 64 bit unsigned integers well. Here we truncate the
function fingerprint to 32 bits as that should still provide enough
uniqueness. Long erm, we will want to generate a 32 bit fingerprint and
update the underlying table but this will ensure correctness for the
time being.
Tony Xiao 1 year ago
parent
commit
0babb74e74

+ 14 - 3
src/sentry/search/events/datasets/profile_functions.py

@@ -68,7 +68,7 @@ COLUMNS = [
     Column(alias="project_id", column="project_id", kind=Kind.INTEGER),
     Column(alias="transaction", column="transaction_name", kind=Kind.STRING),
     Column(alias="timestamp", column="timestamp", kind=Kind.DATE),
-    Column(alias="fingerprint", column="fingerprint", kind=Kind.INTEGER),
+    Column(alias="_fingerprint", column="fingerprint", kind=Kind.INTEGER),
     Column(alias="function", column="name", kind=Kind.STRING),
     Column(alias="package", column="package", kind=Kind.STRING),
     Column(alias="is_application", column="is_application", kind=Kind.INTEGER),
@@ -137,7 +137,7 @@ class ProfileFunctionsDatasetConfig(DatasetConfig):
         "project_id",
         "transaction",
         "timestamp",
-        "fingerprint",
+        "_fingerprint",
         "function",
         "package",
         "is_application",
@@ -161,7 +161,7 @@ class ProfileFunctionsDatasetConfig(DatasetConfig):
     def _fingerprint_filter_converter(self, search_filter: SearchFilter) -> Optional[WhereType]:
         try:
             return Condition(
-                self.builder.column("fingerprint"),
+                self.builder.resolve_column("fingerprint"),
                 Op.EQ if search_filter.operator in EQUALITY_OPERATORS else Op.NEQ,
                 int(search_filter.value.value),
             )
@@ -208,10 +208,21 @@ class ProfileFunctionsDatasetConfig(DatasetConfig):
     @property
     def field_alias_converter(self) -> Mapping[str, Callable[[str], SelectType]]:
         return {
+            "fingerprint": self._resolve_fingerprint_alias,
             PROJECT_ALIAS: self._resolve_project_slug_alias,
             PROJECT_NAME_ALIAS: self._resolve_project_slug_alias,
         }
 
+    def _resolve_fingerprint_alias(self, alias: str) -> SelectType:
+        # HACK: temporarily truncate the fingerprint to 32 bits
+        # as snuba cannot handle 64 bit unsigned fingerprints
+        # once we migrate to a 32 bit unsigned fingerprint
+        # we can remove this field alias and directly use the column
+        #
+        # When removing this, make sure to update the test helper to
+        # generate 32 bit function fingerprints as well.
+        return Function("toUInt32", [self.builder.column("_fingerprint")], alias)
+
     def _resolve_project_slug_alias(self, alias: str) -> SelectType:
         return field_aliases.resolve_project_slug_alias(self.builder, alias)
 

+ 6 - 6
tests/sentry/api/endpoints/test_organization_profiling_functions.py

@@ -100,7 +100,7 @@ class OrganizationProfilingFunctionTrendsEndpointTest(ProfilesSnubaTestCase):
                 "change": "improvement",
                 "project": str(self.project.id),
                 "transaction": str(
-                    self.function_fingerprint({"package": "foo", "function": "bar"})
+                    self.function_fingerprint({"package": "foo", "function": "bar"}) & 0xFFFFFFFF
                 ),
                 "trend_difference": -10000000.0,
                 "trend_percentage": 0.9090909090909091,
@@ -115,7 +115,7 @@ class OrganizationProfilingFunctionTrendsEndpointTest(ProfilesSnubaTestCase):
                 "change": "improvement",
                 "project": str(self.project.id),
                 "transaction": str(
-                    self.function_fingerprint({"package": "foo", "function": "baz"})
+                    self.function_fingerprint({"package": "foo", "function": "baz"}) & 0xFFFFFFFF
                 ),
                 "trend_difference": -900000000.0,
                 "trend_percentage": 0.1,
@@ -177,7 +177,7 @@ class OrganizationProfilingFunctionTrendsEndpointTest(ProfilesSnubaTestCase):
                 "change": "regression",
                 "project": str(self.project.id),
                 "transaction": str(
-                    self.function_fingerprint({"package": "foo", "function": "baz"})
+                    self.function_fingerprint({"package": "foo", "function": "baz"}) & 0xFFFFFFFF
                 ),
                 "trend_difference": 400000000.0,
                 "trend_percentage": 5.0,
@@ -192,7 +192,7 @@ class OrganizationProfilingFunctionTrendsEndpointTest(ProfilesSnubaTestCase):
                 "change": "regression",
                 "project": str(self.project.id),
                 "transaction": str(
-                    self.function_fingerprint({"package": "foo", "function": "bar"})
+                    self.function_fingerprint({"package": "foo", "function": "bar"}) & 0xFFFFFFFF
                 ),
                 "trend_difference": 900000000.0,
                 "trend_percentage": 10.0,
@@ -256,7 +256,7 @@ class OrganizationProfilingFunctionTrendsEndpointTest(ProfilesSnubaTestCase):
                 "change": "improvement",
                 "project": str(self.project.id),
                 "transaction": str(
-                    self.function_fingerprint({"package": "foo", "function": "bar"})
+                    self.function_fingerprint({"package": "foo", "function": "bar"}) & 0xFFFFFFFF
                 ),
                 "trend_difference": -400000000.0,
                 "trend_percentage": 0.2,
@@ -271,7 +271,7 @@ class OrganizationProfilingFunctionTrendsEndpointTest(ProfilesSnubaTestCase):
                 "change": "improvement",
                 "project": str(self.project.id),
                 "transaction": str(
-                    self.function_fingerprint({"package": "foo", "function": "baz"})
+                    self.function_fingerprint({"package": "foo", "function": "baz"}) & 0xFFFFFFFF
                 ),
                 "trend_difference": -900000000.0,
                 "trend_percentage": 0.1,

+ 2 - 2
tests/sentry/search/events/builder/test_profile_functions.py

@@ -62,12 +62,12 @@ def params():
         ),
         pytest.param(
             "fingerprint:123",
-            Condition(Column("fingerprint"), Op("="), 123),
+            Condition(Function("toUInt32", [Column("fingerprint")], "fingerprint"), Op("="), 123),
             id="fingerprint",
         ),
         pytest.param(
             "!fingerprint:123",
-            Condition(Column("fingerprint"), Op("!="), 123),
+            Condition(Function("toUInt32", [Column("fingerprint")], "fingerprint"), Op("!="), 123),
             id="not fingerprint",
         ),
     ],