Steps to reproduce
- Configure a hostname prefix so that
AssetLastHostname.increment_hostname(prefix) is used when assets are created.
- Start with an existing
AssetLastHostname row where counter = N.
- Trigger two concurrent asset creations, or call
increment_hostname(prefix) concurrently from two workers.
- Let worker A perform the
F("counter") + 1 update first, then let worker B perform its update before worker A performs the follow-up get(pk=obj.pk) read.
Expected behavior
Each concurrent allocation should receive a distinct hostname, for example prefix-N+1 and prefix-N+2.
Actual behavior
increment_hostname() performs an atomic increment and then a separate unlocked read of the row. If one worker reads after another worker has already committed a later increment, both workers can format and return the same final counter value.
Relevant code:
src/ralph/assets/models/assets.py:280-291 updates counter and then reads the row again.
- The method contains a
TODO mentioning select_for_update, which is the missing serialization primitive here.
Minimal interleaving:
Initial counter = N
Worker A: UPDATE counter = N + 1
Worker B: UPDATE counter = N + 2
Worker A: SELECT counter -> N + 2, returns hostname prefix-(N+2)
Worker B: SELECT counter -> N + 2, returns hostname prefix-(N+2)
Environment
- Ralph version: current
main at bcf65b994ef29fb3fc2e10b660e6288723d5209e
- Operating system: not OS-specific
- Method of installation: not installation-specific
Impact
Concurrent asset creation can assign duplicate hostnames. If hostnames are used for inventory identity, DNS, automation, or access mapping, duplicate allocation can cause incorrect asset attribution or automation against the wrong host.
Suggested fix
Serialize hostname allocation by locking the AssetLastHostname row inside a transaction, for example with select_for_update(), or by using a single database operation that atomically increments and returns the allocated value. A regression test should exercise concurrent allocations and assert that every returned hostname is unique.
Steps to reproduce
AssetLastHostname.increment_hostname(prefix)is used when assets are created.AssetLastHostnamerow wherecounter = N.increment_hostname(prefix)concurrently from two workers.F("counter") + 1update first, then let worker B perform its update before worker A performs the follow-upget(pk=obj.pk)read.Expected behavior
Each concurrent allocation should receive a distinct hostname, for example
prefix-N+1andprefix-N+2.Actual behavior
increment_hostname()performs an atomic increment and then a separate unlocked read of the row. If one worker reads after another worker has already committed a later increment, both workers can format and return the same final counter value.Relevant code:
src/ralph/assets/models/assets.py:280-291updatescounterand then reads the row again.TODOmentioningselect_for_update, which is the missing serialization primitive here.Minimal interleaving:
Environment
mainatbcf65b994ef29fb3fc2e10b660e6288723d5209eImpact
Concurrent asset creation can assign duplicate hostnames. If hostnames are used for inventory identity, DNS, automation, or access mapping, duplicate allocation can cause incorrect asset attribution or automation against the wrong host.
Suggested fix
Serialize hostname allocation by locking the
AssetLastHostnamerow inside a transaction, for example withselect_for_update(), or by using a single database operation that atomically increments and returns the allocated value. A regression test should exercise concurrent allocations and assert that every returned hostname is unique.