opsmill / infrahub

Infrahub - A new approach to Infrastructure Management
https://opsmill.com/
GNU Affero General Public License v3.0
185 stars 8 forks source link

bug: Number Resource Pool Skips Increment After Object Creation Fails #4303

Open BeArchiTek opened 1 week ago

BeArchiTek commented 1 week ago

Component

API Server / GraphQL

Infrahub version

0.16.0

Current Behavior

When attempting to create a DummyNumber object with a Number Resource Pool, if the creation fails for a reason unrelated to the number pool (such as a validation error or missing required fields), the next number generated from the pool increments by the number of failed attempts instead of only incrementing by 1.

Expected Behavior

Steps to Reproduce

version: "1.0"

nodes:
  - name: Number
    namespace: Dummy
    description: "A dummy model to demonstrate number resource pool."
    label: "Dummy Number"
    icon: mdi:numeric
    attributes:
      - name: name
        kind: Text
        unique: true
      - name: random
        kind: Number
        description: "A number generated from the number pool."

Additional Information

No response

ogenstad commented 1 week ago

I'd guess that this is due to how the transaction is setup in the base mutation. Here we use a transaction for the save operation and we might need to move that so that the transaction begins before calling .new()

    @classmethod
    @retry_db_transaction(name="object_create")
    async def mutate_create_object(
        cls,
        data: InputObjectType,
        db: InfrahubDatabase,
        branch: Branch,
        at: str,
    ) -> Node:
        component_registry = get_component_registry()
        node_constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
        node_class = Node
        if cls._meta.schema.kind in registry.node:
            node_class = registry.node[cls._meta.schema.kind]

        try:
            obj = await node_class.init(db=db, schema=cls._meta.schema, branch=branch, at=at)
            await obj.new(db=db, **data)
            fields_to_validate = list(data)
            await node_constraint_runner.check(node=obj, field_filters=fields_to_validate)
            if db.is_transaction:
                await obj.save(db=db)
            else:
                async with db.start_transaction() as dbt:
                    await obj.save(db=dbt)