How to lock a row while updating it in SqlAlchemy Core?

Viewed 179

It is incredibly difficult to unit test race conditions like this. I was hoping to verify this with experts here.

I have the following scenario where I would like to obtain the first VPN Profile that is not assigned to any device.

In the meanwhile any other process trying to obtain the same profile (since it's the next in line), should wait here until my transaction has completed.

I would then update this vpn profile and assign the given device id to it and finish the transaction.

At this point any process that was waiting on the select().first() statement shouldn't be obtaining this particular VPN profile, because it has already been assigned to a device_id. Instead it should obtain the next available one.

After some digging, this is the code I have come up with. I'm using with_for_update() in the select statement and keep everything within the same engine.begin() transaction. I'm using SQLAlchemy Core without ORM.

    async with engine.begin() as conn:
        query = (
            VpnProfileTable.select()
            .where(
                VpnProfileTable.c.device_id == None,
            )
            .with_for_update(nowait=False)
        )
        record = (await conn.execute(query)).first()
        if record:
            query = (
                VpnProfileTable.update()
                .where(VpnProfileTable.c.id == record.id)
                .values(device_id=device_id)
            )
            await conn.execute(query)
            await conn.commit()
  1. Is my code reflecting what I'm trying to achieve?
  2. Not sure if I need to commit(), since there is already a with engine.begin() statement. It should happen automatically at the end.

Many thanks

0 Answers
Related