Skip to content
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

feat(suspect-spans): Add new endpoint for suspect spans #29357

Merged
merged 6 commits into from
Oct 20, 2021

Conversation

Zylphrex
Copy link
Member

This introduced a new endpoint for suspect spans.


self.min_ago = before_now(minutes=1).replace(microsecond=0)

self.update_snuba_config_ensure({"write_span_columns_projects": f"[{self.project.id}]"})
Copy link
Member Author

@Zylphrex Zylphrex Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds nearly 10s per test. If there's not an easy way to make this faster in CI, we may want to xfail these tests for now.

@Zylphrex Zylphrex marked this pull request as ready for review October 19, 2021 18:39
@Zylphrex Zylphrex requested a review from a team as a code owner October 19, 2021 18:39
@Zylphrex Zylphrex requested a review from a team October 19, 2021 18:40
Copy link
Member

@wmak wmak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM;

  • small question around moving some of the classes and functions out of the endpoint to make it smaller
  • Also I think if these tests do end up taking 10s each, we likely want to skip instead of xfail cause I think pytest still runs the tests
    • Also should leave 1 in as a general smoke test IMO

Comment on lines 134 to 487
data.get("spans", []),
)
]
)

return ExampleTransaction(
id=transaction_id,
description=description,
start_timestamp=data["start_timestamp"],
finish_timestamp=data["timestamp"],
non_overlapping_exclusive_time=sum(
window.duration_ms for window in non_overlapping_exclusive_time_windows
),
spans=spans,
)


@dataclasses.dataclass(frozen=True)
class TimeWindow:
# Timestamps are in seconds
start: float
end: float

def as_tuple(self) -> Tuple[float, float]:
return (self.start, self.end)

@property
def duration_ms(self) -> float:
return (self.end - self.start) * 1000

def __add__(self, other: "TimeWindow") -> Tuple[Optional["TimeWindow"], "TimeWindow"]:
if self.start < other.start:
if self.end < other.start:
return self, other
return None, TimeWindow(start=self.start, end=max(self.end, other.end))
else:
if self.start > other.end:
return other, self
return None, TimeWindow(start=other.start, end=max(self.end, other.end))

def __sub__(self, other: "TimeWindow") -> Tuple[Optional["TimeWindow"], "TimeWindow"]:
if self.start < other.start:
if self.end > other.end:
return (
TimeWindow(start=self.start, end=other.start),
TimeWindow(start=other.end, end=self.end),
)
return None, TimeWindow(start=self.start, end=min(self.end, other.start))
else:
if self.end < other.end:
return None, TimeWindow(start=self.end, end=self.end)
return None, TimeWindow(start=max(self.start, other.end), end=self.end)


def union_time_windows(time_windows: List[TimeWindow]) -> List[TimeWindow]:
if not time_windows:
return []

previous, *time_windows = sorted(time_windows, key=lambda window: window.as_tuple())

unioned: List[TimeWindow] = []

for current in time_windows:
window, previous = previous + current
if window:
unioned.append(window)

unioned.append(previous)

return unioned


def remove_time_windows(source: TimeWindow, time_windows: List[TimeWindow]) -> List[TimeWindow]:
if not time_windows:
return [source]

removed: List[TimeWindow] = []

for current in time_windows:
window, source = source - current
if window:
removed.append(window)

removed.append(source)

return removed


def get_exclusive_time_windows(span: ExampleSpan, spans: List[Any]) -> List[TimeWindow]:
non_overlapping_children_time_windows = union_time_windows(
[
TimeWindow(start=child["start_timestamp"], end=child["timestamp"])
for child in spans
if child["parent_span_id"] == span.id
]
)
return remove_time_windows(
TimeWindow(start=span.start_timestamp, end=span.finish_timestamp),
non_overlapping_children_time_windows,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on moving these to utils.py?

Comment on lines 79 to 87
# pagination will try to get 1 extra result to decide if there is a next page,
# drop the extra result, if present, to fetch less events
alias = get_function_alias(
SPAN_PERFORMANCE_COLUMNS[orderby_column].suspect_example_column
)
orderby = direction + alias
transaction_ids = query_example_transactions(
params, query, orderby, suspects[: limit - 1]
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 won't this break pagination?
If we don't want pagination shouldn't we disable it entirely like we do with the noPagination param?
Or is this for another reason

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is done intentionally. Pagination is done by checking the existence of an extra result. What this is doing is skipping the nodestore calls + any calculation done on the event for this extra result. However, when serializing, the extra result will be present but without any example events attached. This way pagination still works as expected but we don't need to make the extra nodestore calls for this extra result.

@Zylphrex Zylphrex merged commit 36b1637 into master Oct 20, 2021
@Zylphrex Zylphrex deleted the feat/add-new-suspect-span-endpoint branch October 20, 2021 15:52
@github-actions github-actions bot locked and limited conversation to collaborators Nov 5, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants