Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions api/segment_membership/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ def refresh_project_segment_counts(project_id: int) -> None:
now = timezone.now()
for m in membership_counts:
m.last_synced_at = now

new_pairs = {(m.segment_id, m.environment_id) for m in membership_counts}
stale_ids = [
pk
for pk, segment_id, environment_id in (
SegmentMembershipCount.objects.filter(
segment__project=project
).values_list("id", "segment_id", "environment_id")
)
if (segment_id, environment_id) not in new_pairs
]
stale_deleted, _ = SegmentMembershipCount.objects.filter(
id__in=stale_ids,
).delete()

SegmentMembershipCount.objects.bulk_create(
membership_counts,
update_conflicts=True,
Expand All @@ -171,4 +186,5 @@ def refresh_project_segment_counts(project_id: int) -> None:
"refresh.project.completed",
project__id=project_id,
membership_counts__count=len(membership_counts),
stale_counts__count=stale_deleted,
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from unittest.mock import MagicMock

from django.utils import timezone
from pytest_django.fixtures import SettingsWrapper
from pytest_mock import MockerFixture
from pytest_structlog import StructuredLogCapture
Expand Down Expand Up @@ -295,3 +296,63 @@ def test_refresh_project_segment_counts__counts_returned__upserts_per_env_rows(
f":project_{project.id}"
)
)


def test_refresh_project_segment_counts__previously_matching_pair_drops_to_zero__row_deleted(
mocker: MockerFixture,
settings: SettingsWrapper,
project: Project,
environment: Environment,
segment: Segment,
enable_features: EnableFeaturesFixture,
) -> None:
# Given a prior refresh that landed a non-zero count for (segment, env)
enable_features("segment_membership_inspection")
settings.CLICKHOUSE_ENABLED = True
SegmentMembershipCount.objects.create(
segment=segment,
environment=environment,
count=15,
last_synced_at=timezone.now(),
)
cursor = MagicMock()
open_cursor = mocker.patch.object(tasks, "open_clickhouse_cursor")
open_cursor.return_value.__enter__.return_value = cursor
# ... and a new compute that returns no matches for the same pair (the
# rule was edited, the identity set drifted, etc.).
mocker.patch.object(tasks, "compute_segment_counts_for_project", return_value=[])

# When
refresh_project_segment_counts(project.id)

# Then the stale row is gone -- pairs that no longer match drop out of
# the table entirely rather than lingering at the previous count.
assert not SegmentMembershipCount.objects.filter(
segment=segment, environment=environment
).exists()


def test_refresh_project_segment_counts__never_matched_pair__no_row_written(
mocker: MockerFixture,
settings: SettingsWrapper,
project: Project,
environment: Environment,
segment: Segment,
enable_features: EnableFeaturesFixture,
) -> None:
# Given a project with no prior membership rows
enable_features("segment_membership_inspection")
settings.CLICKHOUSE_ENABLED = True
cursor = MagicMock()
open_cursor = mocker.patch.object(tasks, "open_clickhouse_cursor")
open_cursor.return_value.__enter__.return_value = cursor
mocker.patch.object(tasks, "compute_segment_counts_for_project", return_value=[])

# When
refresh_project_segment_counts(project.id)

# Then no row is written: refresh upserts matches, drops misses, and
# leaves never-matched pairs untouched.
assert not SegmentMembershipCount.objects.filter(
segment=segment, environment=environment
).exists()
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,12 @@ Attributes:
### `segment_membership.refresh.project.completed`

Logged at `info` from:
- `api/segment_membership/tasks.py:170`
- `api/segment_membership/tasks.py:185`

Attributes:
- `membership_counts.count`
- `project.id`
- `stale_counts.count`

### `segment_membership.refresh.project.failed`

Expand Down
Loading