fix: DailySunTimesAdmin field, commute_tick + map viewport N+1s; document expand/contract for DB column drops - #788
Merged
Merged
Conversation
development → staging
development → staging
development → staging
The hour/minute duration inputs shared a .row class with the completed-date/time row. The small-screen media query flipped all .row elements to flex-direction: column, stacking the duration fields instead of keeping them side by side. Scope the column layout to the date/time row only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Publishing an Announcement now pushes a WebSocket "announcement_published" event to online players, which invalidates the announcements/unread-count queries so the Navbar badge appears immediately instead of only on refresh or remount. - Announcement.save() broadcasts on the unpublished->published transition - publish_selected_announcements admin action saves rows individually (not queryset.update()) so the broadcast actually fires - frontend wires the new action into handleGlobalWebSocketEvent / WebSocketContext to invalidate the relevant react-query keys Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…realtime-badge feat: broadcast announcement badge updates in real time
Deploy: staging → main
Root-cause the 2026-08-14 production UndefinedColumn errors on Character.can_link: web, celery, and celery-beat deploy independently on Render, and only web runs migrate via preDeployCommand. The migration dropping can_link landed in the same deploy as the code change that stopped using it, so celery/celery-beat kept erroring on the old column reference until their own deploys caught up. The current can_link code (derived property, is_reserved field) is already correct - this documents the two-deploy process needed to avoid the same stale-worker window on future column removals.
DailySunTimesAdmin.fields referenced "name", a field that doesn't exist on DailySunTimes (it has date, world, sunrise, sunset, dawn, dusk) - list_display/list_filter already correctly use "date". Every GET to /admin/gameworld/dailysuntimes/add/ raised FieldError before rendering the form.
target_role_for (via work_hours_for) queried each character's WORK CharacterLocation individually, and sync_character_location redundantly called target_role_for again per character even when the caller already knew the role - both run once per idle character every commute_tick, reported by Sentry as an N+1 (67 extra queries in one tick, PROGRESS-BACKEND-PRODUCTION-EJ). Introduced in e12199f when target_role_for started resolving hours from the character's work building instead of fixed constants. Batch-fetch every character's primary (home + work) CharacterLocations in commute_tick with one query, and thread work_location/target_role through to work_hours_for/target_role_for/sync_character_location so they use the pre-fetched values instead of querying per character. Other callers (behaviour_services.generate_day, existing tests) are unaffected - the new parameters default to the previous per-character lookup behaviour.
PathFeatureSerializer.get_geometry reads obj.geom, but both
MapViewportView and PopulationCentreMapView fetched paths with
.only("id", "from_node__location", "to_node__location"), omitting
geom. Every access triggered a separate per-object deferred-field
query - Sentry reported 364 extra queries (75% of transaction time)
on a single /api/v1/map/viewport/ request (PROGRESS-BACKEND-PRODUCTION-EH).
Add "geom" to both .only() calls.
Resolves a conflict in Announcement.save(): development's version (d37f377) auto-sets published_at but was branched before the WebSocket broadcast-on-publish behavior (c2e8a7c, already live on main/staging) landed, so its rewrite of save() dropped that behavior. Combined both - auto-set published_at, then broadcast on the draft-to-published transition - rather than silently losing the already-shipped broadcast feature.
gaidheal1
marked this pull request as ready for review
August 16, 2026 09:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Continuation of the previous branch (
claude/prod-redis-query-errors-c0dzdg,closed as #787) renamed to
task/fix-sentry-issues, fixing four Sentryissues from
progress-backend-production(two of them the same underlyingincident, see below).
can_linkUndefinedColumn): root-caused the productionerrors from 2026-08-14 to a deploy-ordering race between the
independently-auto-deploying
web,celery, andcelery-beatRenderservices (only
webrunsmigrate). Application code was alreadycorrect at
HEAD. Documented the expand/contract process for dropping DBcolumns in
docs/operations/deployment-runbook.md.PROGRESS-BACKEND-PRODUCTION-ED):DailySunTimesAdmin.fieldsreferenced
"name", a field that doesn't exist onDailySunTimes.Changed to
"date".PROGRESS-BACKEND-PRODUCTION-EJand-EK, samecommute_tickrun, same root cause):
target_role_for/work_hours_forqueried eachidle character's WORK
CharacterLocationindividually, andsync_character_locationredundantly re-queried it again per charactereven when the caller already knew the role - two separate per-character
query sites in the same tick, which is why Sentry grouped them as two
issues (each keyed off a different preceding span in the trace).
Batch-fetched every character's primary (home + work)
CharacterLocations in one query incommute_tickand threaded thepre-fetched
work_location/target_rolethroughwork_hours_for/target_role_for/sync_character_location.PROGRESS-BACKEND-PRODUCTION-EH):PathFeatureSerializer.get_geometryreads
obj.geom, but bothMapViewportViewandPopulationCentreMapViewfetched
Paths via.only("id", "from_node__location", "to_node__location"),omitting
geom- every access triggered a separate deferred-field query(364 extra queries, 75% of transaction time, on one
/api/v1/map/viewport/request). Added
"geom"to both.only()calls.Also noted, not fixed here (already resolved, explained to the user in
conversation rather than in a diff): a
ValueError: Unable to configure handler 'file_debug'issue from Aug 2 turned out to be stale - thefile_debughandler has been commented out ofsettings/dev.pysinceDecember 2025, and the traceback shows it running
manage.py testfrom/testbedon Python 3.14, consistent with a sandboxed test run rather thanreal production traffic.
Test plan
/admin/gameworld/dailysuntimes/add/and confirm the add formrenders without a
FieldErrorlocations/tests/test_schedule.pyandlocations/tests/test_map_viewport.py- not run locally in thissession (no Docker/GDAL available in this sandbox), so CI should be
the first real run