Flagsmith / Flagsmith/flagsmith
Race condition in FeatureSegment priority assignment allows duplicate priorities
- Dominant language
- Python
- Stars
- 6.6k
- Forks
- 567
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 121
Description
Concurrent `POST /environments/{env_key}/features/{feature_id}/create-segment-override/` requests for the same feature+environment can produce `FeatureSegment` rows with duplicate `priority` values.
## Root cause
`FeatureSegment` inherits from `ordered_model.OrderedModelBase`. On `save()`, if `priority is None`, it calls `get_next_order()` which does `max(priority) + 1` scoped to `(feature, environment, environment_feature_version)`.
This read-then-write is not atomic — when two requests arrive concurrently, both can read the same `max(priority)` and assign the same value.
## Impact
Duplicate priorities break the contract that segment override priority is a total order. Downstream consumers (e.g. the flag engine evaluator) use strict `<` to pick the winning segment override when priorities tie, making the result depend on iteration order rather than intentional priority.
## Reproduction
Call `create-segment-override` concurrently for 3 different segments on the same feature+environment (e.g. with 32 parallel workers). Observe that two or more `FeatureSegment` rows end up with `priority=0`.
## Suggested fix
Either:
1. Use `select_for_update()` / row-level locking around the `get_next_order()` + `save()` sequence, or
2. Add a unique constraint on `(feature, environment, environment_feature_version, priority)` and retry on conflict.
Contributor guide
Assessment
This issue has not been assessed yet.