Skip to content

Commit 45da0bf

Browse files
committed
Merge branch 'stable' into jbr-conflits-02272025-stable-to-release12
2 parents bba5cd1 + 8c438c2 commit 45da0bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+349
-779
lines changed

backend/infrahub/core/diff/model/path.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ class EnrichedDiffAttribute(BaseSummary):
163163
def __hash__(self) -> int:
164164
return hash(self.name)
165165

166+
@property
167+
def num_properties(self) -> int:
168+
return len(self.properties)
169+
166170
def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]:
167171
return {prop.path_identifier: prop.conflict for prop in self.properties if prop.conflict}
168172

@@ -202,6 +206,10 @@ class EnrichedDiffSingleRelationship(BaseSummary):
202206
def __hash__(self) -> int:
203207
return hash(self.peer_id)
204208

209+
@property
210+
def num_properties(self) -> int:
211+
return len(self.properties)
212+
205213
def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]:
206214
all_conflicts: dict[str, EnrichedDiffConflict] = {}
207215
if self.conflict:
@@ -248,6 +256,10 @@ class EnrichedDiffRelationship(BaseSummary):
248256
def __hash__(self) -> int:
249257
return hash(self.name)
250258

259+
@property
260+
def num_properties(self) -> int:
261+
return sum(r.num_properties for r in self.relationships)
262+
251263
def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]:
252264
all_conflicts: dict[str, EnrichedDiffConflict] = {}
253265
for element in self.relationships:
@@ -308,6 +320,10 @@ class EnrichedDiffNode(BaseSummary):
308320
def __hash__(self) -> int:
309321
return hash(self.uuid)
310322

323+
@property
324+
def num_properties(self) -> int:
325+
return sum(a.num_properties for a in self.attributes) + sum(r.num_properties for r in self.relationships)
326+
311327
def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]:
312328
all_conflicts: dict[str, EnrichedDiffConflict] = {}
313329
if self.conflict:

backend/infrahub/core/diff/query/save.py

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -83,33 +83,55 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa
8383
query = """
8484
UNWIND $node_details_list AS node_details
8585
WITH node_details.root_uuid AS root_uuid, node_details.node_map AS node_map
86+
MATCH (diff_root:DiffRoot {uuid: root_uuid})
87+
MERGE (diff_root)-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_map.node_properties.uuid})
88+
WITH root_uuid, node_map, diff_node, (node_map.conflict_params IS NOT NULL) AS has_node_conflict
89+
SET
90+
diff_node.kind = node_map.node_properties.kind,
91+
diff_node.label = node_map.node_properties.label,
92+
diff_node.changed_at = node_map.node_properties.changed_at,
93+
diff_node.action = node_map.node_properties.action,
94+
diff_node.path_identifier = node_map.node_properties.path_identifier
95+
WITH root_uuid, node_map, diff_node, has_node_conflict
8696
CALL {
87-
WITH root_uuid, node_map
88-
MATCH (diff_root {uuid: root_uuid})
89-
MERGE (diff_root)-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_map.node_properties.uuid})
90-
SET
91-
diff_node.kind = node_map.node_properties.kind,
92-
diff_node.label = node_map.node_properties.label,
93-
diff_node.changed_at = node_map.node_properties.changed_at,
94-
diff_node.action = node_map.node_properties.action,
95-
diff_node.path_identifier = node_map.node_properties.path_identifier
9697
// -------------------------
97-
// add/remove node-level conflict
98+
// delete parent-child relationships for included nodes, they will be added in EnrichedNodesLinkQuery
9899
// -------------------------
99-
WITH diff_node, node_map
100-
OPTIONAL MATCH (diff_node)-[:DIFF_HAS_CONFLICT]->(current_diff_node_conflict:DiffConflict)
101-
WITH diff_node, node_map, current_diff_node_conflict, (node_map.conflict_params IS NOT NULL) AS has_node_conflict
102-
FOREACH (i in CASE WHEN has_node_conflict = FALSE THEN [1] ELSE [] END |
103-
DETACH DELETE current_diff_node_conflict
104-
)
105-
FOREACH (i in CASE WHEN has_node_conflict = TRUE THEN [1] ELSE [] END |
106-
MERGE (diff_node)-[:DIFF_HAS_CONFLICT]->(diff_node_conflict:DiffConflict)
107-
SET diff_node_conflict = node_map.conflict_params
108-
)
100+
WITH diff_node
101+
MATCH (diff_node)-[:DIFF_HAS_RELATIONSHIP]->(:DiffRelationship)-[parent_rel:DIFF_HAS_NODE]->(:DiffNode)
102+
DELETE parent_rel
103+
}
104+
OPTIONAL MATCH (diff_node)-[:DIFF_HAS_CONFLICT]->(current_node_conflict:DiffConflict)
105+
CALL {
106+
// -------------------------
107+
// create a node-level conflict, if necessary
108+
// -------------------------
109+
WITH diff_node, current_node_conflict, has_node_conflict
110+
WITH diff_node, current_node_conflict, has_node_conflict
111+
WHERE current_node_conflict IS NULL AND has_node_conflict = TRUE
112+
CREATE (diff_node)-[:DIFF_HAS_CONFLICT]->(:DiffConflict)
113+
}
114+
CALL {
115+
// -------------------------
116+
// delete a node-level conflict, if necessary
117+
// -------------------------
118+
WITH current_node_conflict, has_node_conflict
119+
WITH current_node_conflict, has_node_conflict
120+
WHERE current_node_conflict IS NOT NULL AND has_node_conflict = FALSE
121+
DETACH DELETE current_node_conflict
122+
}
123+
WITH root_uuid, node_map, diff_node, has_node_conflict, node_map.conflict_params AS node_conflict_params
124+
CALL {
125+
// -------------------------
126+
// set the properties of the node-level conflict, if necessary
127+
// -------------------------
128+
WITH diff_node, has_node_conflict, node_conflict_params
129+
WITH diff_node, has_node_conflict, node_conflict_params
130+
WHERE has_node_conflict = TRUE
131+
OPTIONAL MATCH (diff_node)-[:DIFF_HAS_CONFLICT]->(node_conflict:DiffConflict)
132+
SET node_conflict = node_conflict_params
109133
}
110134
CALL {
111-
WITH root_uuid, node_map
112-
MATCH (diff_root {uuid: root_uuid})-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_map.node_properties.uuid})
113135
// -------------------------
114136
// remove stale attributes for this node
115137
// -------------------------
@@ -426,14 +448,14 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa
426448
node_link_details.root_uuid AS root_uuid,
427449
node_link_details.parent_uuid AS parent_uuid,
428450
node_link_details.child_uuid AS child_uuid,
429-
node_link_details.relationship_name AS relationship_name
451+
node_link_details.child_relationship_name AS relationship_name
430452
CALL {
431453
WITH root_uuid, parent_uuid, child_uuid, relationship_name
432454
MATCH (diff_root {uuid: root_uuid})
433-
MATCH (diff_root)-[:DIFF_HAS_NODE]->(parent_node:DiffNode {uuid: parent_uuid})
434-
-[:DIFF_HAS_RELATIONSHIP]->(diff_rel_group:DiffRelationship {name: relationship_name})
435455
MATCH (diff_root)-[:DIFF_HAS_NODE]->(child_node:DiffNode {uuid: child_uuid})
436-
MERGE (diff_rel_group)-[:DIFF_HAS_NODE]->(child_node)
456+
-[:DIFF_HAS_RELATIONSHIP]->(diff_rel_group:DiffRelationship {name: relationship_name})
457+
MATCH (diff_root)-[:DIFF_HAS_NODE]->(parent_node:DiffNode {uuid: parent_uuid})
458+
MERGE (diff_rel_group)-[:DIFF_HAS_NODE]->(parent_node)
437459
}
438460
"""
439461
self.add_to_query(query)
@@ -443,14 +465,14 @@ def _build_node_parent_links(self, enriched_node: EnrichedDiffNode, root_uuid: s
443465
return []
444466
parent_links = []
445467
for relationship in enriched_node.relationships:
446-
for child_node in relationship.nodes:
468+
for parent_node in relationship.nodes:
447469
parent_links.append(
448470
{
449-
"parent_uuid": enriched_node.uuid,
450-
"relationship_name": relationship.name,
451-
"child_uuid": child_node.uuid,
471+
"child_uuid": enriched_node.uuid,
472+
"child_relationship_name": relationship.name,
473+
"parent_uuid": parent_node.uuid,
452474
"root_uuid": root_uuid,
453475
}
454476
)
455-
parent_links.extend(self._build_node_parent_links(enriched_node=child_node, root_uuid=root_uuid))
477+
parent_links.extend(self._build_node_parent_links(enriched_node=parent_node, root_uuid=root_uuid))
456478
return parent_links

backend/infrahub/core/diff/repository/repository.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from collections import defaultdict
22
from typing import AsyncGenerator, Generator, Iterable
33

4+
from neo4j.exceptions import TransientError
5+
46
from infrahub import config
57
from infrahub.core import registry
68
from infrahub.core.diff.query.field_summary import EnrichedDiffNodeFieldSummaryQuery
@@ -42,11 +44,10 @@
4244

4345

4446
class DiffRepository:
45-
MAX_SAVE_BATCH_SIZE: int = 100
46-
47-
def __init__(self, db: InfrahubDatabase, deserializer: EnrichedDiffDeserializer):
47+
def __init__(self, db: InfrahubDatabase, deserializer: EnrichedDiffDeserializer, max_save_batch_size: int = 1000):
4848
self.db = db
4949
self.deserializer = deserializer
50+
self.max_save_batch_size = max_save_batch_size
5051

5152
async def _run_get_diff_query(
5253
self,
@@ -230,20 +231,44 @@ def _get_node_create_request_batch(
230231
) -> Generator[list[EnrichedNodeCreateRequest], None, None]:
231232
node_requests = []
232233
for diff_root in (enriched_diffs.base_branch_diff, enriched_diffs.diff_branch_diff):
234+
size_count = 0
233235
for node in diff_root.nodes:
234-
node_requests.append(EnrichedNodeCreateRequest(node=node, root_uuid=diff_root.uuid))
235-
if len(node_requests) == self.MAX_SAVE_BATCH_SIZE:
236+
node_size_count = node.num_properties
237+
if size_count + node_size_count < self.max_save_batch_size:
238+
node_requests.append(EnrichedNodeCreateRequest(node=node, root_uuid=diff_root.uuid))
239+
size_count += node_size_count
240+
else:
241+
log.info(f"Num nodes in batch: {len(node_requests)}, num properties in batch: {size_count}")
236242
yield node_requests
237-
node_requests = []
243+
size_count = node_size_count
244+
node_requests = [EnrichedNodeCreateRequest(node=node, root_uuid=diff_root.uuid)]
238245
if node_requests:
246+
log.info(f"Num nodes in batch: {len(node_requests)}, num properties in batch: {size_count}")
239247
yield node_requests
240248

241-
@retry_db_transaction(name="enriched_diff_save")
242-
async def save(self, enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata, do_summary_counts: bool = True) -> None:
249+
@retry_db_transaction(name="enriched_diff_metadata_save")
250+
async def _save_root_metadata(self, enriched_diffs: EnrichedDiffsMetadata) -> None:
243251
log.info("Updating diff metadata...")
244252
root_query = await EnrichedDiffRootsUpsertQuery.init(db=self.db, enriched_diffs=enriched_diffs)
245253
await root_query.execute(db=self.db)
246254
log.info("Diff metadata updated.")
255+
256+
async def _save_node_batch(self, node_create_batch: list[EnrichedNodeCreateRequest]) -> None:
257+
node_query = await EnrichedNodeBatchCreateQuery.init(db=self.db, node_create_batch=node_create_batch)
258+
try:
259+
await node_query.execute(db=self.db)
260+
except TransientError as exc:
261+
if not exc.code or "OutOfMemoryError".lower() not in str(exc.code).lower():
262+
raise
263+
log.exception("Database memory error during save. Trying smaller transactions")
264+
for node_request in node_create_batch:
265+
single_node_query = await EnrichedNodeBatchCreateQuery.init(
266+
db=self.db, node_create_batch=[node_request]
267+
)
268+
await single_node_query.execute(db=self.db)
269+
270+
async def save(self, enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata, do_summary_counts: bool = True) -> None:
271+
await self._save_root_metadata(enriched_diffs=enriched_diffs)
247272
if not isinstance(enriched_diffs, EnrichedDiffs):
248273
return
249274
num_nodes = len(enriched_diffs.base_branch_diff.nodes) + len(enriched_diffs.diff_branch_diff.nodes)
@@ -252,9 +277,8 @@ async def save(self, enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata, do_s
252277
self._get_node_create_request_batch(enriched_diffs=enriched_diffs)
253278
):
254279
log.info(f"Saving node batch #{batch_num}...")
255-
node_query = await EnrichedNodeBatchCreateQuery.init(db=self.db, node_create_batch=node_create_batch)
256-
await node_query.execute(db=self.db)
257-
log.info(f"Batch #{batch_num} saved")
280+
await self._save_node_batch(node_create_batch=node_create_batch)
281+
log.info(f"Batch saved. num_nodes={len(node_create_batch)}")
258282
link_query = await EnrichedNodesLinkQuery.init(db=self.db, enriched_diffs=enriched_diffs)
259283
await link_query.execute(db=self.db)
260284
log.info("Diff saved.")
@@ -444,6 +468,7 @@ async def get_node_field_specifiers(self, diff_id: str) -> dict[str, set[str]]:
444468
offset += limit
445469
return specifiers
446470

471+
@retry_db_transaction(name="enriched_diff_summary_counts")
447472
async def add_summary_counts(
448473
self,
449474
diff_branch_name: str,
@@ -460,4 +485,4 @@ async def add_summary_counts(
460485
node_uuids=node_uuids,
461486
)
462487
await query.execute(db=self.db)
463-
log.info("Summary counts updated...")
488+
log.info("Summary counts updated.")

backend/infrahub/core/node/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@
77
from infrahub_sdk.uuidt import UUIDT
88

99
from infrahub.core import registry
10+
<<<<<<< HEAD
1011
from infrahub.core.changelog.models import NodeChangelog
1112
from infrahub.core.constants import (
1213
OBJECT_TEMPLATE_NAME_ATTR,
1314
OBJECT_TEMPLATE_RELATIONSHIP_NAME,
15+
=======
16+
from infrahub.core.constants import (
17+
GLOBAL_BRANCH_NAME,
18+
>>>>>>> stable
1419
BranchSupportType,
1520
ComputedAttributeKind,
1621
InfrahubKind,
1722
RelationshipCardinality,
23+
<<<<<<< HEAD
1824
RelationshipKind,
25+
=======
26+
>>>>>>> stable
1927
)
2028
from infrahub.core.constants.schema import SchemaElementPathType
2129
from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
@@ -28,6 +36,7 @@
2836

2937
from ...graphql.constants import KIND_GRAPHQL_FIELD_NAME
3038
from ...graphql.models import OrderModel
39+
from ..query.relationship import RelationshipDeleteAllQuery
3140
from ..relationship import RelationshipManager
3241
from ..utils import update_relationships_to
3342
from .base import BaseNode, BaseNodeMeta, BaseNodeOptions
@@ -690,6 +699,7 @@ async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) ->
690699
if deleted_attribute:
691700
node_changelog.add_attribute(attribute=deleted_attribute)
692701

702+
<<<<<<< HEAD
693703
# Go over the list of relationships and update them one by one
694704
for name in self._relationships:
695705
rel: RelationshipManager = getattr(self, name)
@@ -698,8 +708,15 @@ async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) ->
698708

699709
# Need to check if there are some unidirectional relationship as well
700710
# For example, if we delete a tag, we must check the permissions and update all the relationships pointing at it
711+
=======
712+
>>>>>>> stable
701713
branch = self.get_branch_based_on_support_type()
702714

715+
delete_query = await RelationshipDeleteAllQuery.init(
716+
db=db, node_id=self.get_id(), branch=branch, at=delete_at, branch_agnostic=branch.name == GLOBAL_BRANCH_NAME
717+
)
718+
await delete_query.execute(db=db)
719+
703720
# Update the relationship to the branch itself
704721
query = await NodeGetListQuery.init(
705722
db=db,

0 commit comments

Comments
 (0)