Skip to content

bug: relationship of cardinality one attached to multiple peers #7972

@fatih-acar

Description

@fatih-acar

Component

API Server / GraphQL

Infrahub version

1.6.2

Current Behavior

When creating multiple nodes linked to the same peer that has a cardinality one constraint (at most one peer) in parallel, we may end up in a situation where multiple nodes are attached to this same peer.

Image Image

Expected Behavior

Creating multiple nodes in parallel linked to the same cardinality one peer should allow only one successful creation.

Steps to Reproduce

load base schemas

infrahubctl schema load schema-library/base schema-library/extensions/location_minimal schema-library/extensions/compute/

infrahubctl object load objects/locations.yml

uv run reproducer.py

Should successfully create two interfaces linked to the same IP address.
Note: if you use the same interface name (for example eth0 on both interfaces), creation of one of the interfaces will fail (as expected). This is because internal locking is done on the uniqueness constaint.

#!/usr/bin/env python
# reproducer.py

import infrahub_sdk
from infrahub_sdk import InfrahubClientSync

client = InfrahubClientSync()

ip = client.create(kind="IpamIPAddress", data={"address": "10.0.0.1/24"})
ip.save(allow_upsert=True)

rack = client.create(kind="LocationRack", data={"name": "test-rack", "shortname": "test-rack"})
rack.save(allow_upsert=True)

device1 = client.create(kind="ComputePhysicalServer", data={"name": "serv1", "status": "active", "location": rack})
device1.save(allow_upsert=True)
device2 = client.create(kind="ComputePhysicalServer", data={"name": "serv2", "status": "active", "location": rack})
device2.save(allow_upsert=True)

intf1 = client.create(kind="InterfacePhysical", data={"name": "eth0", "device": device1, "ip_addresses": [ip]})
intf2 = client.create(kind="InterfacePhysical", data={"name": "eth1", "device": device2, "ip_addresses": [ip]})

batch = client.create_batch()

batch.add(task=intf1.save, allow_upsert=True)
batch.add(task=intf2.save, allow_upsert=True)
for _ in batch.execute():
    print("executed batch")
# locations.yml
---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: LocationCountry
  data:
    - name: test-country
      shortname: test-country
---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: LocationMetro
  data:
    - name: test-metro
      shortname: test-metro
      parent: test-country
---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: LocationSite
  data:
    - name: test-site
      shortname: test-site
      parent: test-metro
---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: LocationRack
  data:
    - name: test-rack
      shortname: test-rack
      parent: test-site

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugSomething isn't working as expected

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions