Skip to content

Commit cf07cf4

Browse files
committed
fix(backend): node_create_all duplicate AttributeValue rels
If there are duplicate AttributeValue nodes present (due to concurrent node creation for example), the MERGE statement would return more than one node. Thus the HAS_VALUE relationship would be created for all the nodes that have been returned. Due to that behavior, attribute retrieval is slower because more (duplicate) rows are returned in node_list_get_attribute. Fix this by using a LIMIT 1 statement right after MERGE. Using LIMIT 1 implies removing FOREACH statements which are not compatible with LIMIT (that implies WITH). Move to CALL subqueries instead to work that around. CALL subqueries do not work very well with UNWIND when the list is empty, make the whole query dynamic and remove parts when lists are empty to work that around. Signed-off-by: Fatih Acar <[email protected]>
1 parent bc6f627 commit cf07cf4

File tree

3 files changed

+70
-19
lines changed

3 files changed

+70
-19
lines changed

backend/infrahub/core/query/attribute.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No
6666
query = """
6767
MATCH (a:Attribute { uuid: $attr_uuid })
6868
MERGE (av:%(labels)s { %(props)s } )
69+
WITH av, a
70+
LIMIT 1
6971
CREATE (a)-[r:%(rel_label)s { branch: $branch, branch_level: $branch_level, status: "active", from: $at }]->(av)
7072
""" % {"rel_label": self.attr._rel_to_value_label, "labels": ":".join(labels), "props": ", ".join(prop_list)}
7173

backend/infrahub/core/query/node.py

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,16 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
203203
}
204204
ipnetwork_prop_list = [f"{key}: {value}" for key, value in ipnetwork_prop.items()]
205205

206-
query = """
207-
MATCH (root:Root)
208-
CREATE (n:Node:%(labels)s $node_prop )
209-
CREATE (n)-[r:IS_PART_OF $node_branch_prop ]->(root)
206+
attrs_query = """
210207
WITH distinct n
211-
FOREACH ( attr IN $attrs |
208+
UNWIND $attrs AS attr
209+
CALL {
210+
WITH n, attr
212211
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
213212
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
214213
MERGE (av:AttributeValue { value: attr.content.value, is_default: attr.content.is_default })
214+
WITH n, attr, av, a
215+
LIMIT 1
215216
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
216217
MERGE (ip:Boolean { value: attr.is_protected })
217218
MERGE (iv:Boolean { value: attr.is_visible })
@@ -225,11 +226,19 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
225226
MERGE (peer:Node { uuid: prop.peer_id })
226227
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
227228
)
228-
)
229-
FOREACH ( attr IN $attrs_iphost |
229+
}"""
230+
231+
attrs_iphost_query = """
232+
WITH distinct n
233+
UNWIND $attrs_iphost AS attr_iphost
234+
CALL {
235+
WITH n, attr_iphost
236+
WITH n, attr_iphost AS attr
230237
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
231238
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
232239
MERGE (av:AttributeValue:AttributeIPHost { %(iphost_prop)s })
240+
WITH n, attr, av, a
241+
LIMIT 1
233242
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
234243
MERGE (ip:Boolean { value: attr.is_protected })
235244
MERGE (iv:Boolean { value: attr.is_visible })
@@ -243,11 +252,20 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
243252
MERGE (peer:Node { uuid: prop.peer_id })
244253
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
245254
)
246-
)
247-
FOREACH ( attr IN $attrs_ipnetwork |
255+
}
256+
""" % {"iphost_prop": ", ".join(iphost_prop_list)}
257+
258+
attrs_ipnetwork_query = """
259+
WITH distinct n
260+
UNWIND $attrs_ipnetwork AS attr_ipnetwork
261+
CALL {
262+
WITH n, attr_ipnetwork
263+
WITH n, attr_ipnetwork AS attr
248264
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
249265
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
250266
MERGE (av:AttributeValue:AttributeIPNetwork { %(ipnetwork_prop)s })
267+
WITH n, attr, av, a
268+
LIMIT 1
251269
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
252270
MERGE (ip:Boolean { value: attr.is_protected })
253271
MERGE (iv:Boolean { value: attr.is_visible })
@@ -261,8 +279,14 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
261279
MERGE (peer:Node { uuid: prop.peer_id })
262280
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
263281
)
264-
)
265-
FOREACH ( rel IN $rels_bidir |
282+
}
283+
""" % {"ipnetwork_prop": ", ".join(ipnetwork_prop_list)}
284+
285+
rels_bidir_query = """
286+
WITH distinct n
287+
UNWIND $rels_bidir AS rel
288+
CALL {
289+
WITH n, rel
266290
MERGE (d:Node { uuid: rel.destination_id })
267291
CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
268292
CREATE (n)-[:IS_RELATED %(rel_prop)s ]->(rl)
@@ -279,8 +303,15 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
279303
MERGE (peer:Node { uuid: prop.peer_id })
280304
CREATE (rl)-[:HAS_OWNER { branch: rel.branch, branch_level: rel.branch_level, status: rel.status, from: $at }]->(peer)
281305
)
282-
)
283-
FOREACH ( rel IN $rels_out |
306+
}
307+
""" % {"rel_prop": rel_prop_str}
308+
309+
rels_out_query = """
310+
WITH distinct n
311+
UNWIND $rels_out AS rel_out
312+
CALL {
313+
WITH n, rel_out
314+
WITH n, rel_out as rel
284315
MERGE (d:Node { uuid: rel.destination_id })
285316
CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
286317
CREATE (n)-[:IS_RELATED %(rel_prop)s ]->(rl)
@@ -297,8 +328,15 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
297328
MERGE (peer:Node { uuid: prop.peer_id })
298329
CREATE (rl)-[:HAS_OWNER { branch: rel.branch, branch_level: rel.branch_level, status: rel.status, from: $at }]->(peer)
299330
)
300-
)
301-
FOREACH ( rel IN $rels_in |
331+
}
332+
""" % {"rel_prop": rel_prop_str}
333+
334+
rels_in_query = """
335+
WITH distinct n
336+
UNWIND $rels_in AS rel_in
337+
CALL {
338+
WITH n, rel_in
339+
WITH n, rel_in AS rel
302340
MERGE (d:Node { uuid: rel.destination_id })
303341
CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
304342
CREATE (n)<-[:IS_RELATED %(rel_prop)s ]-(rl)
@@ -315,14 +353,23 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
315353
MERGE (peer:Node { uuid: prop.peer_id })
316354
CREATE (rl)-[:HAS_OWNER { branch: rel.branch, branch_level: rel.branch_level, status: rel.status, from: $at }]->(peer)
317355
)
318-
)
356+
}
357+
""" % {"rel_prop": rel_prop_str}
358+
359+
query = f"""
360+
MATCH (root:Root)
361+
CREATE (n:Node:%(labels)s $node_prop )
362+
CREATE (n)-[r:IS_PART_OF $node_branch_prop ]->(root)
363+
{attrs_query if self.params["attrs"] else ""}
364+
{attrs_iphost_query if self.params["attrs_iphost"] else ""}
365+
{attrs_ipnetwork_query if self.params["attrs_ipnetwork"] else ""}
366+
{rels_bidir_query if self.params["rels_bidir"] else ""}
367+
{rels_out_query if self.params["rels_out"] else ""}
368+
{rels_in_query if self.params["rels_in"] else ""}
319369
WITH distinct n
320370
MATCH (n)-[:HAS_ATTRIBUTE|IS_RELATED]-(rn)-[:HAS_VALUE|IS_RELATED]-(rv)
321371
""" % {
322372
"labels": ":".join(self.node.get_labels()),
323-
"rel_prop": rel_prop_str,
324-
"iphost_prop": ", ".join(iphost_prop_list),
325-
"ipnetwork_prop": ", ".join(ipnetwork_prop_list),
326373
}
327374

328375
self.params["at"] = at.to_string()

backend/infrahub/core/query/resource_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No
276276
query = """
277277
MATCH (pool:%(number_pool)s { uuid: $pool_id })
278278
MERGE (value:AttributeValue { value: $reserved, is_default: false })
279+
WITH value, pool
280+
LIMIT 1
279281
CREATE (pool)-[rel:IS_RESERVED $rel_prop]->(value)
280282
""" % {"number_pool": InfrahubKind.NUMBERPOOL}
281283

0 commit comments

Comments
 (0)