Skip to content

feat(difs): Add Objectstore columns to ProjectDebugFile#117500

Open
lcian wants to merge 2 commits into
masterfrom
lcian/feat/objectstore-debug-files-migration
Open

feat(difs): Add Objectstore columns to ProjectDebugFile#117500
lcian wants to merge 2 commits into
masterfrom
lcian/feat/objectstore-debug-files-migration

Conversation

@lcian

@lcian lcian commented Jun 12, 2026

Copy link
Copy Markdown
Member

Adds new columns to house Objectstore-backed difs.
These will lose the file FK and gain the storage_path field.
In addition, we also need to add content_type, file_size and date_created, as those were previously fields of File.
Naturally, during the migration period, both of these need to be nullable, so we need to assert the invariants we know to be true at runtime.

@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jun 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

for 1115_projectdebugfile_add_objectstore_columns in sentry

--
-- Alter field file on projectdebugfile
--
SET CONSTRAINTS "sentry_projectdsymfile_file_id_bdf41a41_fk_sentry_file_id" IMMEDIATE; ALTER TABLE "sentry_projectdsymfile" DROP CONSTRAINT "sentry_projectdsymfile_file_id_bdf41a41_fk_sentry_file_id";
ALTER TABLE "sentry_projectdsymfile" ALTER COLUMN "file_id" DROP NOT NULL;
ALTER TABLE "sentry_projectdsymfile" ADD CONSTRAINT "sentry_projectdsymfile_file_id_bdf41a41_fk_sentry_file_id" FOREIGN KEY ("file_id") REFERENCES "sentry_file" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_projectdsymfile" VALIDATE CONSTRAINT "sentry_projectdsymfile_file_id_bdf41a41_fk_sentry_file_id";
--
-- Add field storage_path to projectdebugfile
--
ALTER TABLE "sentry_projectdsymfile" ADD COLUMN "storage_path" text NULL;
--
-- Add field content_type to projectdebugfile
--
ALTER TABLE "sentry_projectdsymfile" ADD COLUMN "content_type" text NULL;
--
-- Add field file_size to projectdebugfile
--
ALTER TABLE "sentry_projectdsymfile" ADD COLUMN "file_size" bigint NULL;
--
-- Add field date_created to projectdebugfile
--
ALTER TABLE "sentry_projectdsymfile" ADD COLUMN "date_created" timestamp with time zone NULL;

@lcian lcian force-pushed the lcian/feat/objectstore-debug-files-migration branch 2 times, most recently from 7c7b0ee to 0b95ff7 Compare June 12, 2026 09:28
lcian added 2 commits June 12, 2026 13:18
Add new nullable columns (storage_path, content_type, file_size,
date_created) and make the File FK nullable in preparation for
migrating debug files to Objectstore.
@lcian lcian force-pushed the lcian/feat/objectstore-debug-files-migration branch from 0b95ff7 to 4fe3b25 Compare June 12, 2026 11:18
@lcian lcian marked this pull request as ready for review June 12, 2026 12:50
@lcian lcian requested review from a team as code owners June 12, 2026 12:50
@lcian lcian requested review from a team June 12, 2026 12:50
Comment on lines 156 to 162

@property
def file_format(self) -> str:
assert self.file is not None
ct = self.file.headers.get("Content-Type", "unknown").lower()
return KNOWN_DIF_FORMATS.get(ct, "unknown")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: Serializing a DebugFile with a None file attribute (for Objectstore-backed DIFs) raises an AssertionError or AttributeError because the serializer does not handle the null case.
Severity: CRITICAL

Suggested Fix

Update DebugFileSerializer to handle cases where obj.file is None. Use obj.content_type and other direct attributes from the DebugFile model for Objectstore-backed DIFs instead of accessing them through the now-nullable file relation. Remove the assert self.file is not None from the file_format property and add logic to handle the None case.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/sentry/models/debugfile.py#L156-L162

Potential issue: The `DebugFileSerializer` attempts to serialize debug information files
(DIFs) by accessing attributes of `obj.file`, such as `obj.file.headers` and
`obj.file.size`. It also calls the `obj.file_format` property. However, for DIFs backed
by Objectstore, the `file` attribute is `None`. The `file_format` property contains an
`assert self.file is not None`, which will raise an `AssertionError`. Direct access to
`obj.file` attributes will raise an `AttributeError`. This causes any API endpoint that
serializes these DIFs, such as the `DebugFilesEndpoint`, to crash.

Also affects:

  • src/sentry/models/debugfile.py:130~136

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines 237 to 243
uuidmap_seen = False
il2cpp_seen = False
for i, dif in enumerate(difs):
assert dif.file is not None
mime_type = dif.file.headers.get("Content-Type")
if mime_type == DIF_MIMETYPES["bcsymbolmap"]:
if not bcsymbolmap_seen:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The clean_redundant_difs function unconditionally asserts dif.file is not None, which will fail and crash uploads if any existing DIF for the same debug_id is Objectstore-backed.
Severity: HIGH

Suggested Fix

Modify clean_redundant_difs to correctly handle ProjectDebugFile instances where file is None. Instead of asserting dif.file is not None to get the content type, use dif.content_type which is available on the model directly and supports both file-backed and Objectstore-backed DIFs.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/sentry/models/debugfile.py#L237-L243

Potential issue: The function `clean_redundant_difs` is called after a new debug
information file (DIF) is uploaded. It queries for all existing `ProjectDebugFile`
records with the same `debug_id` and iterates through them. Inside the loop, it
unconditionally runs `assert dif.file is not None`. If a pre-existing DIF for that
`debug_id` is backed by Objectstore, its `file` attribute will be `None`, causing the
assertion to fail. This will crash the upload process for any new DIF that shares a
`debug_id` with an Objectstore-backed DIF.

Did we get this right? 👍 / 👎 to inform future reviews.

@loewenheim

Copy link
Copy Markdown
Contributor

Is the idea behind making the file field nullable in the model and adding assertions that this way we save ourselves a migration? In principle, it would work just as well to only add the new columns and do the nullability separately, right?

@lcian

lcian commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

Yeah @loewenheim, we could very well split this into 2 separate migrations, would it be preferable to do it that way?
It's my first time creating a migration, so I don't really know what factors should be considered.

The intention was not really to spare a migration, but to make the File nullable while at the same time introducing the alternative, as if this were an enum you add a variant to.
A ProjectDebugFile with a NULL File doesn't make too much sense to me, unless an alternative way to point to a file exists.

@loewenheim

Copy link
Copy Markdown
Contributor

If having two separate migrations isn't a concern, I would prefer it if the file field was made nullable at the point where it's actually possible for a debug file to have null there.

@lcian

lcian commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

Yeah that makes sense, I'll change it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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