Add index on RewardCampaign for ends_at and starts_at fields; update tests for index verification
This commit is contained in:
parent
47d4f5341f
commit
61946f8155
3 changed files with 115 additions and 2 deletions
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 6.0.4 on 2026-04-11 22:41
|
||||
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"Add an index on the RewardCampaign model for the ends_at and starts_at fields, to optimize queries that filter by these fields."
|
||||
|
||||
dependencies = [
|
||||
("twitch", "0019_dropcampaign_campaign_list_indexes"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name="rewardcampaign",
|
||||
index=models.Index(
|
||||
fields=["ends_at", "-starts_at"],
|
||||
name="tw_reward_ends_starts_idx",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
@ -595,6 +595,7 @@ class DropCampaign(auto_prefetch.Model):
|
|||
"game",
|
||||
"game__twitch_id",
|
||||
"game__display_name",
|
||||
"game__name",
|
||||
"game__slug",
|
||||
"game__box_art",
|
||||
"game__box_art_file",
|
||||
|
|
@ -606,6 +607,7 @@ class DropCampaign(auto_prefetch.Model):
|
|||
models.Prefetch(
|
||||
"game__owners",
|
||||
queryset=Organization.objects.only("twitch_id", "name"),
|
||||
to_attr="owners_for_dashboard",
|
||||
),
|
||||
models.Prefetch(
|
||||
"allow_channels",
|
||||
|
|
@ -640,14 +642,14 @@ class DropCampaign(auto_prefetch.Model):
|
|||
for campaign in campaigns:
|
||||
game: Game = campaign.game
|
||||
game_id: str = game.twitch_id
|
||||
game_display_name: str = game.display_name
|
||||
game_display_name: str = game.get_game_name
|
||||
|
||||
game_bucket: dict[str, Any] = campaigns_by_game.setdefault(
|
||||
game_id,
|
||||
{
|
||||
"name": game_display_name,
|
||||
"box_art": game.box_art_best_url,
|
||||
"owners": list(game.owners.all()),
|
||||
"owners": list(getattr(game, "owners_for_dashboard", [])),
|
||||
"campaigns": [],
|
||||
},
|
||||
)
|
||||
|
|
@ -1190,6 +1192,10 @@ class RewardCampaign(auto_prefetch.Model):
|
|||
fields=["starts_at", "ends_at"],
|
||||
name="tw_reward_starts_ends_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["ends_at", "-starts_at"],
|
||||
name="tw_reward_ends_starts_idx",
|
||||
),
|
||||
models.Index(fields=["status", "-starts_at"]),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -710,6 +710,7 @@ class TestChannelListView:
|
|||
expected_reward_indexes: set[str] = {
|
||||
"tw_reward_starts_desc_idx",
|
||||
"tw_reward_starts_ends_idx",
|
||||
"tw_reward_ends_starts_idx",
|
||||
}
|
||||
|
||||
drop_index_names: set[str] = _index_names(DropCampaign._meta.db_table)
|
||||
|
|
@ -849,6 +850,90 @@ class TestChannelListView:
|
|||
f"baseline={baseline_select_count}, scaled={scaled_select_count}"
|
||||
)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_dashboard_field_access_after_prefetch_has_no_extra_selects(self) -> None:
|
||||
"""Dashboard-accessed fields should not trigger deferred model SELECT queries."""
|
||||
now: datetime.datetime = timezone.now()
|
||||
|
||||
org: Organization = Organization.objects.create(
|
||||
twitch_id="org_dashboard_field_access",
|
||||
name="Org Dashboard Field Access",
|
||||
)
|
||||
game: Game = Game.objects.create(
|
||||
twitch_id="game_dashboard_field_access",
|
||||
name="Game Dashboard Field Access",
|
||||
display_name="Game Dashboard Field Access",
|
||||
slug="game-dashboard-field-access",
|
||||
box_art="https://example.com/game-box-art.jpg",
|
||||
)
|
||||
game.owners.add(org)
|
||||
|
||||
campaign: DropCampaign = DropCampaign.objects.create(
|
||||
twitch_id="campaign_dashboard_field_access",
|
||||
name="Campaign Dashboard Field Access",
|
||||
game=game,
|
||||
operation_names=["DropCampaignDetails"],
|
||||
image_url="https://example.com/campaign.jpg",
|
||||
start_at=now - timedelta(hours=1),
|
||||
end_at=now + timedelta(hours=1),
|
||||
)
|
||||
channel: Channel = Channel.objects.create(
|
||||
twitch_id="channel_dashboard_field_access",
|
||||
name="channeldashboardfieldaccess",
|
||||
display_name="Channel Dashboard Field Access",
|
||||
)
|
||||
campaign.allow_channels.add(channel)
|
||||
|
||||
RewardCampaign.objects.create(
|
||||
twitch_id="reward_dashboard_field_access",
|
||||
name="Reward Dashboard Field Access",
|
||||
brand="Brand",
|
||||
summary="Reward summary",
|
||||
is_sitewide=False,
|
||||
game=game,
|
||||
starts_at=now - timedelta(hours=1),
|
||||
ends_at=now + timedelta(hours=1),
|
||||
)
|
||||
|
||||
dashboard_rewards_qs: QuerySet[RewardCampaign] = (
|
||||
RewardCampaign.active_for_dashboard(now)
|
||||
)
|
||||
dashboard_campaigns_qs: QuerySet[DropCampaign] = (
|
||||
DropCampaign.active_for_dashboard(now)
|
||||
)
|
||||
|
||||
rewards_list: list[RewardCampaign] = list(dashboard_rewards_qs)
|
||||
list(dashboard_campaigns_qs)
|
||||
|
||||
with CaptureQueriesContext(connection) as queries:
|
||||
# Use pre-evaluated queryset to avoid capturing initial SELECT queries
|
||||
grouped = DropCampaign.grouped_by_game(dashboard_campaigns_qs)
|
||||
|
||||
for reward in rewards_list:
|
||||
_ = reward.twitch_id
|
||||
_ = reward.name
|
||||
_ = reward.brand
|
||||
_ = reward.summary
|
||||
_ = reward.starts_at
|
||||
_ = reward.ends_at
|
||||
_ = reward.is_sitewide
|
||||
_ = reward.is_active
|
||||
if reward.game:
|
||||
_ = reward.game.twitch_id
|
||||
_ = reward.game.display_name
|
||||
|
||||
assert game.twitch_id in grouped
|
||||
|
||||
deferred_selects: list[str] = [
|
||||
query_info["sql"]
|
||||
for query_info in queries.captured_queries
|
||||
if query_info["sql"].lstrip().upper().startswith("SELECT")
|
||||
]
|
||||
assert not deferred_selects, (
|
||||
"Dashboard model field access triggered unexpected deferred SELECT queries. "
|
||||
f"Queries: {deferred_selects}"
|
||||
)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_dashboard_grouping_reuses_selected_game_relation(self) -> None:
|
||||
"""Dashboard grouping should not issue extra standalone Game queries."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue