Flow
Ways to count and save in one step
1Lock the key
Hold a lock on the key while you count and save. Easy to follow, but bursts on one key wait in line.
2Counter with a condition
Bump a counter only while it is under the cap. One step, but it has to come down when a device is removed.
The new licensing service has a cap on how many devices one key can run on. It works when people turn on one machine at a time. If two show up at once, I don't think the cap holds.
The code went in today as part of one huge commit, and I doubt it got a real look. I think an agent wrote most of it. When I read it back, the order of the steps bugged me.
Count first, save later
To turn on a device, the code counts the devices the key already has. It looks up the cap. It checks if this device is already on the key. If not, it compares the count to the cap and saves the new device.
The steps are fine. The order isn't. The count is taken at the start, and nothing stops it from going stale before the save. Two new devices that arrive together both see the same count, both see room, and both get saved. Now the key covers one machine too many.
The database won't catch it. Its only rule is that a device can't be on the same key twice.
The test would pass every time, because it turns devices on one at a time. That proves the cap works, not that it survives a crowd.
Two smaller holes
One device sending two requests at once trips the same gap. The database turns away the copy, which is right, but the code picks its error replies by reading message text, so the user gets a server error. The key check is weak too. Leave out the device ID, and a key that was never turned on anywhere still comes back good.
Doing the count and the save together
The check and the save need to be one step. You can lock the key while you count and save, or keep a counter and only bump it while it's under the cap:
UPDATE room SET used = used + 1 WHERE id = :room AND used < cap;
If no row changes, the key is full. Just remember to lower the counter when a device is removed. A duplicate from the same device should come back as a plain success.
I haven't tested any of this with requests landing together. It's all from reading the code, so I can't say yet how often two requests would really land in that gap.