-
Notifications
You must be signed in to change notification settings - Fork 20
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 issue that causes illegal diff query #5291
Changes from all commits
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 |
---|---|---|
@@ -1,7 +1,7 @@ | ||
from __future__ import annotations | ||
|
||
from dataclasses import dataclass, field | ||
from typing import TYPE_CHECKING | ||
from typing import TYPE_CHECKING, Iterable | ||
|
||
from infrahub import lock | ||
from infrahub.core import registry | ||
|
@@ -14,7 +14,6 @@ | |
EnrichedDiffs, | ||
NameTrackingId, | ||
NodeFieldSpecifier, | ||
TimeRange, | ||
TrackingId, | ||
) | ||
|
||
|
@@ -219,6 +218,30 @@ async def recalculate( | |
log.debug(f"Diff recalculation complete for {base_branch.name} - {diff_branch.name}") | ||
return enriched_diffs.diff_branch_diff | ||
|
||
def _get_ordered_diff_pairs( | ||
self, diff_pairs: Iterable[EnrichedDiffs], allow_overlap: bool = False | ||
) -> list[EnrichedDiffs]: | ||
ordered_diffs = sorted(diff_pairs, key=lambda d: d.diff_branch_diff.from_time) | ||
if allow_overlap: | ||
return ordered_diffs | ||
ordered_diffs_no_overlaps: list[EnrichedDiffs] = [] | ||
for candidate_diff_pair in ordered_diffs: | ||
if not ordered_diffs_no_overlaps: | ||
ordered_diffs_no_overlaps.append(candidate_diff_pair) | ||
continue | ||
# no time overlap | ||
previous_diff = ordered_diffs_no_overlaps[-1].diff_branch_diff | ||
candidate_diff = candidate_diff_pair.diff_branch_diff | ||
if previous_diff.to_time <= candidate_diff.from_time: | ||
ordered_diffs_no_overlaps.append(candidate_diff_pair) | ||
continue | ||
previous_interval = previous_diff.time_range | ||
candidate_interval = candidate_diff.time_range | ||
# keep the diff that covers the larger time frame | ||
if candidate_interval > previous_interval: | ||
ordered_diffs_no_overlaps[-1] = candidate_diff_pair | ||
return ordered_diffs_no_overlaps | ||
|
||
async def _update_diffs( | ||
self, | ||
base_branch: Branch, | ||
|
@@ -272,15 +295,17 @@ async def _get_aggregated_enriched_diffs( | |
if not partial_enriched_diffs: | ||
return await self._get_enriched_diff(diff_request=diff_request) | ||
|
||
remaining_diffs = sorted(partial_enriched_diffs, key=lambda d: d.diff_branch_diff.from_time) | ||
ordered_diffs = self._get_ordered_diff_pairs(diff_pairs=partial_enriched_diffs, allow_overlap=False) | ||
ordered_diff_reprs = [repr(d) for d in ordered_diffs] | ||
log.debug(f"Ordered diffs for aggregation: {ordered_diff_reprs}") | ||
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. a little logging to help us troubleshoot if this comes up again |
||
current_time = diff_request.from_time | ||
previous_diffs: EnrichedDiffs | None = None | ||
while current_time < diff_request.to_time: | ||
if remaining_diffs and remaining_diffs[0].diff_branch_diff.from_time == current_time: | ||
current_diffs = remaining_diffs.pop(0) | ||
if ordered_diffs and ordered_diffs[0].diff_branch_diff.from_time == current_time: | ||
current_diffs = ordered_diffs.pop(0) | ||
else: | ||
if remaining_diffs: | ||
end_time = remaining_diffs[0].diff_branch_diff.from_time | ||
if ordered_diffs: | ||
end_time = ordered_diffs[0].diff_branch_diff.from_time | ||
else: | ||
end_time = diff_request.to_time | ||
if previous_diffs is None: | ||
|
@@ -322,26 +347,6 @@ async def _get_enriched_diff(self, diff_request: EnrichedDiffRequest) -> Enriche | |
enriched_diff_pair = await self.diff_enricher.enrich(calculated_diffs=calculated_diff_pair) | ||
return enriched_diff_pair | ||
|
||
def _get_missing_time_ranges( | ||
self, time_ranges: list[TimeRange], from_time: Timestamp, to_time: Timestamp | ||
) -> list[TimeRange]: | ||
if not time_ranges: | ||
return [TimeRange(from_time=from_time, to_time=to_time)] | ||
sorted_time_ranges = sorted(time_ranges, key=lambda tr: tr.from_time) | ||
missing_time_ranges = [] | ||
if sorted_time_ranges[0].from_time > from_time: | ||
missing_time_ranges.append(TimeRange(from_time=from_time, to_time=sorted_time_ranges[0].from_time)) | ||
index = 0 | ||
while index < len(sorted_time_ranges) - 1: | ||
this_diff = sorted_time_ranges[index] | ||
next_diff = sorted_time_ranges[index + 1] | ||
if this_diff.to_time < next_diff.from_time: | ||
missing_time_ranges.append(TimeRange(from_time=this_diff.to_time, to_time=next_diff.from_time)) | ||
index += 1 | ||
if sorted_time_ranges[-1].to_time < to_time: | ||
missing_time_ranges.append(TimeRange(from_time=sorted_time_ranges[-1].to_time, to_time=to_time)) | ||
return missing_time_ranges | ||
|
||
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. dead code |
||
def _get_node_field_specifiers(self, enriched_diff: EnrichedDiffRoot) -> set[NodeFieldSpecifier]: | ||
specifiers: set[NodeFieldSpecifier] = set() | ||
schema_branch = registry.schema.get_schema_branch(name=enriched_diff.diff_branch_name) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -767,8 +767,10 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: | |
// ------------------------------------- | ||
MATCH diff_rel_path = (root:Root)<-[r_root:IS_PART_OF]-(n:Node)-[r_node]-(p)-[diff_rel {branch: $branch_name}]->(q) | ||
WHERE (node_field_specifiers_list IS NULL OR [n.uuid, p.name] IN node_field_specifiers_list) | ||
AND (from_time <= diff_rel.from < $to_time) | ||
AND (diff_rel.to IS NULL OR (from_time <= diff_rel.to < $to_time)) | ||
AND ( | ||
(from_time <= diff_rel.from < $to_time) | ||
OR (from_time <= diff_rel.to < $to_time) | ||
) | ||
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. fix in the query to calculate a diff |
||
// exclude attributes and relationships under added/removed nodes, attrs, and rels b/c they are covered above | ||
AND ALL( | ||
r in [r_root, r_node] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -694,7 +694,7 @@ async def test_relationship_one_property_branch_update( | |
single_relationship = single_relationships_by_peer_id[person_john_main.id] | ||
assert single_relationship.peer_id == person_john_main.id | ||
assert single_relationship.action is DiffAction.REMOVED | ||
assert len(single_relationship.properties) == 2 | ||
assert len(single_relationship.properties) == 3 | ||
assert before_main_change < single_relationship.changed_at < after_main_change | ||
property_diff_by_type = {p.property_type: p for p in single_relationship.properties} | ||
property_diff = property_diff_by_type[DatabaseEdgeType.IS_RELATED] | ||
|
@@ -709,6 +709,12 @@ async def test_relationship_one_property_branch_update( | |
assert property_diff.new_value is None | ||
assert property_diff.action is DiffAction.REMOVED | ||
assert before_main_change < property_diff.changed_at < after_main_change | ||
property_diff = property_diff_by_type[DatabaseEdgeType.IS_PROTECTED] | ||
assert property_diff.property_type == DatabaseEdgeType.IS_PROTECTED | ||
assert property_diff.previous_value is False | ||
assert property_diff.new_value is None | ||
assert property_diff.action is DiffAction.REMOVED | ||
assert before_main_change < property_diff.changed_at < after_main_change | ||
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. fix to this unit test after the fix to the diff query |
||
|
||
|
||
async def test_add_node_branch( | ||
|
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.
strip out overlapping diffs. when two diffs overlap, keep the one that covers the greater time range