Skip to content

Conversation

shruthilayaj
Copy link
Member

We'd like to store a snapshot of the last valid transaction widget
while we do an in-place migration of widgets from transactions -> spans.
This allows us to recover the original transaction state of the
widget in case something goes horribly wrong.

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jul 10, 2025
Copy link
Contributor

github-actions bot commented Jul 10, 2025

This PR has a migration; here is the generated SQL for src/sentry/migrations/0946_add_dashboard_widget_snapshot_model.py

for 0946_add_dashboard_widget_snapshot_model in sentry

--
-- Create model DashboardWidgetSnapshot
--
CREATE TABLE "sentry_dashboardwidgetsnapshot" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "data" text NOT NULL, "widget_id" bigint NOT NULL);
ALTER TABLE "sentry_dashboardwidgetsnapshot" ADD CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da" FOREIGN KEY ("widget_id") REFERENCES "sentry_dashboardwidget" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_dashboardwidgetsnapshot" VALIDATE CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da";
CREATE INDEX CONCURRENTLY "sentry_dashboardwidgetsnapshot_widget_id_8a0347ce" ON "sentry_dashboardwidgetsnapshot" ("widget_id");

Copy link

codecov bot commented Jul 10, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #95236      +/-   ##
==========================================
- Coverage   87.73%   85.53%   -2.20%     
==========================================
  Files       10548    10520      -28     
  Lines      607769   606756    -1013     
  Branches    23817    23675     -142     
==========================================
- Hits       533215   519016   -14199     
- Misses      74258    87382   +13124     
- Partials      296      358      +62     

Copy link
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/0947_add_dashboard_widget_snapshot_model.py

for 0947_add_dashboard_widget_snapshot_model in sentry

--
-- Create model DashboardWidgetSnapshot
--
CREATE TABLE "sentry_dashboardwidgetsnapshot" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "data" text NOT NULL, "widget_id" bigint NOT NULL);
ALTER TABLE "sentry_dashboardwidgetsnapshot" ADD CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da" FOREIGN KEY ("widget_id") REFERENCES "sentry_dashboardwidget" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_dashboardwidgetsnapshot" VALIDATE CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da";
CREATE INDEX CONCURRENTLY "sentry_dashboardwidgetsnapshot_widget_id_8a0347ce" ON "sentry_dashboardwidgetsnapshot" ("widget_id");

@shruthilayaj shruthilayaj marked this pull request as ready for review July 14, 2025 15:08
@shruthilayaj shruthilayaj requested review from a team as code owners July 14, 2025 15:08
cursor[bot]

This comment was marked as outdated.

Copy link
Member

@narsaynorath narsaynorath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Shruthi and I talked previously about this storing the JSON data used to reconstruct a widget, and we could use the serializer to update the widgets if we need to backtrack. The model is aligned with that plan

Copy link
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/0948_add_dashboard_widget_snapshot_model.py

for 0948_add_dashboard_widget_snapshot_model in sentry

--
-- Create model DashboardWidgetSnapshot
--
CREATE TABLE "sentry_dashboardwidgetsnapshot" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "data" text NOT NULL, "widget_id" bigint NOT NULL);
ALTER TABLE "sentry_dashboardwidgetsnapshot" ADD CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da" FOREIGN KEY ("widget_id") REFERENCES "sentry_dashboardwidget" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_dashboardwidgetsnapshot" VALIDATE CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da";
CREATE INDEX CONCURRENTLY "sentry_dashboardwidgetsnapshot_widget_id_8a0347ce" ON "sentry_dashboardwidgetsnapshot" ("widget_id");

cursor[bot]

This comment was marked as outdated.

Copy link
Member

@wedamija wedamija left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Database changes lgtm.

One other option here could be to store the data in a temporary column on the existing table, but both methods seem about the same to me. Is the idea that you'll write a backup of the data when you're performing the migration?

Copy link
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/0949_add_dashboard_widget_snapshot_model.py

for 0949_add_dashboard_widget_snapshot_model in sentry

--
-- Create model DashboardWidgetSnapshot
--
CREATE TABLE "sentry_dashboardwidgetsnapshot" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "data" text NOT NULL, "widget_id" bigint NOT NULL);
ALTER TABLE "sentry_dashboardwidgetsnapshot" ADD CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da" FOREIGN KEY ("widget_id") REFERENCES "sentry_dashboardwidget" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_dashboardwidgetsnapshot" VALIDATE CONSTRAINT "sentry_dashboardwidg_widget_id_8a0347ce_fk_sentry_da";
CREATE INDEX CONCURRENTLY "sentry_dashboardwidgetsnapshot_widget_id_8a0347ce" ON "sentry_dashboardwidgetsnapshot" ("widget_id");

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Model Meta and Field Default Mismatches

The DashboardWidgetSnapshot model, introduced in the diff, has two issues:

  • It is missing a Meta class with app_label="sentry" and db_table definitions, which is inconsistent with other models in the same file (e.g., DashboardWidget). This can lead to inconsistent table naming and ORM behavior.
  • The data JSONField's model definition omits default=dict, despite the corresponding migration specifying it. This inconsistency can cause the Django ORM to require the field or behave unexpectedly when creating instances, as it won't recognize the database-level default.

src/sentry/models/dashboard_widget.py#L260-L268

@region_silo_model
class DashboardWidgetSnapshot(Model):
__relocation_scope__ = RelocationScope.Organization
widget = FlexibleForeignKey("sentry.DashboardWidget")
data: models.Field[dict[str, Any], dict[str, Any]] = JSONField()

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

@shruthilayaj
Copy link
Member Author

Database changes lgtm.

One other option here could be to store the data in a temporary column on the existing table, but both methods seem about the same to me. Is the idea that you'll write a backup of the data when you're performing the migration?

yup, that's right!

@shruthilayaj shruthilayaj merged commit fa0ebe5 into master Jul 15, 2025
66 checks passed
@shruthilayaj shruthilayaj deleted the shruthi/feat/add-dashboard-widget-snapshot-model branch July 15, 2025 15:40
andrewshie-sentry pushed a commit that referenced this pull request Jul 21, 2025
We'd like to store a snapshot of the last valid transaction widget
while we do an in-place migration of widgets from transactions -> spans.
This allows us to recover the original transaction state of the
widget in case something goes horribly wrong.
@github-actions github-actions bot locked and limited conversation to collaborators Jul 31, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Scope: Backend Automatically applied to PRs that change backend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants