Skip to content

Commit f124c76

Browse files
khvn26emyller
andauthored
feat: Add explicit ordering for segment rule conditions (#5671)
Co-authored-by: Evandro Myller <22429+emyller@users.noreply.github.com>
1 parent 4b54c64 commit f124c76

File tree

5 files changed

+32
-7
lines changed

5 files changed

+32
-7
lines changed

api/app/settings/common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,10 @@
13571357

13581358
SEGMENT_RULES_CONDITIONS_LIMIT = env.int("SEGMENT_RULES_CONDITIONS_LIMIT", 100)
13591359

1360+
SEGMENT_RULES_CONDITIONS_EXPLICIT_ORDERING_ENABLED = env.bool(
1361+
"SEGMENT_RULES_CONDITIONS_EXPLICIT_ORDERING_ENABLED", default=False
1362+
)
1363+
13601364
WEBHOOK_BACKOFF_BASE = env.int("WEBHOOK_BACKOFF_BASE", default=2)
13611365
WEBHOOK_BACKOFF_RETRIES = env.int("WEBHOOK_BACKOFF_RETRIES", default=3)
13621366

api/core/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ class SoftDeleteExportableManager(UUIDNaturalKeyManagerMixin, SoftDeleteManager)
6666

6767

6868
class SoftDeleteExportableModel(SoftDeleteObject, AbstractBaseExportableModel): # type: ignore[misc]
69-
objects = SoftDeleteExportableManager() # type: ignore[misc]
69+
objects: typing.ClassVar[SoftDeleteExportableManager] = (
70+
SoftDeleteExportableManager()
71+
)
7072

7173
class Meta:
7274
abstract = True

api/segments/models.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323
from audit.related_object_type import RelatedObjectType
2424
from core.models import (
25+
SoftDeleteExportableManager,
2526
SoftDeleteExportableModel,
2627
abstract_base_auditable_model_factory,
2728
)
@@ -300,6 +301,21 @@ def deep_clone(self, cloned_segment: Segment) -> "SegmentRule":
300301
return cloned_rule
301302

302303

304+
class ConditionManager(SoftDeleteExportableManager):
305+
def get_queryset(
306+
self,
307+
) -> models.QuerySet["Condition"]:
308+
# Effectively `Condition.Meta.ordering = ("id",) if ... else ()`,
309+
# but avoid the weirdness of a setting-dependant migration
310+
# and having to reload everything in tests
311+
qs: models.QuerySet["Condition"]
312+
if settings.SEGMENT_RULES_CONDITIONS_EXPLICIT_ORDERING_ENABLED:
313+
qs = super().get_queryset().order_by("id")
314+
else:
315+
qs = super().get_queryset()
316+
return qs
317+
318+
303319
class Condition(
304320
SoftDeleteExportableModel,
305321
abstract_base_auditable_model_factory(["uuid"]), # type: ignore[misc]
@@ -343,6 +359,8 @@ class Condition(
343359
created_at = models.DateTimeField(null=True, auto_now_add=True)
344360
updated_at = models.DateTimeField(null=True, auto_now=True)
345361

362+
objects: typing.ClassVar[ConditionManager] = ConditionManager()
363+
346364
def __str__(self): # type: ignore[no-untyped-def]
347365
return "Condition for %s: %s %s %s" % (
348366
str(self.rule),

api/tests/unit/segments/test_unit_segments_views.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -648,8 +648,11 @@ def test_update_segment_add_new_condition(
648648
admin_client_new: APIClient,
649649
segment: Segment,
650650
segment_rule: SegmentRule,
651+
settings: SettingsWrapper,
651652
) -> None:
652653
# Given
654+
settings.SEGMENT_RULES_CONDITIONS_EXPLICIT_ORDERING_ENABLED = True
655+
653656
url = reverse(
654657
"api-v1:projects:project-segments-detail", args=[project.id, segment.id]
655658
)
@@ -705,11 +708,8 @@ def test_update_segment_add_new_condition(
705708
assert response.status_code == status.HTTP_200_OK
706709

707710
assert nested_rule.conditions.count() == 2
708-
assert (
709-
nested_rule.conditions.order_by("-id").first().property
710-
== new_condition_property
711-
)
712-
assert nested_rule.conditions.order_by("-id").first().value == new_condition_value
711+
assert nested_rule.conditions.last().property == new_condition_property
712+
assert nested_rule.conditions.last().value == new_condition_value
713713

714714

715715
def test_update_mismatched_rule_and_segment(

docs/docs/deployment/hosting/locally-api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ the below variables will be ignored.
222222
and hence should not be modified for already running instances of flagsmith. It should only be used for new
223223
installations, and should not be modified. WARNING: setting this to a higher limit may prevent imports to our SaaS
224224
platform if required in the future.
225+
- `SEGMENT_RULES_CONDITIONS_EXPLICIT_ORDERING_ENABLED`: Forces segment rule condition ordering by primary key, ascending. Default is `False` (no guaranteed order). **Warning**: changing this setting might affect evaluation for existing segments.
225226
- `ENABLE_API_USAGE_TRACKING`: Enable tracking of all API requests in Postgres / Influx. Default is True. Setting to
226227
False will mean that the Usage tab in the Organisation Settings will not show any data. Useful when using Postgres for
227228
analytics in high traffic environments to limit the size of database.
@@ -451,7 +452,7 @@ increasing the performance of your task processor.
451452

452453

453454
| Environment Variable | Description | Example value | Default |
454-
|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|-----------------------------------------------|
455+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | --------------------------------------------- |
455456
| `CACHE_ENVIRONMENT_DOCUMENT_MODE` | The caching mode. One of `PERSISTENT` or `EXPIRING`. Note that although the default is `EXPIRING` there is no caching by default due to the default value of `CACHE_ENVIRONMENT_DOCUMENT_SECONDS` | `PERSISTENT` | `EXPIRING` |
456457
| `CACHE_ENVIRONMENT_DOCUMENT_SECONDS` | Number of seconds to cache the environment for (only relevant when `CACHE_ENVIRONMENT_DOCUMENT_MODE=EXPIRING`) | `60` | `0` ( = don't cache) |
457458
| `CACHE_ENVIRONMENT_DOCUMENT_BACKEND` | Python path to the django cache backend chosen. See documentation [here](https://docs.djangoproject.com/en/4.2/topics/cache/). | `django.core.cache.backends.memcached.PyMemcacheCache` | `django.core.cache.backends.db.DatabaseCache` |

0 commit comments

Comments
 (0)