88
99import zstandard
1010from django .core .cache import cache
11- from django .core .exceptions import ObjectDoesNotExist
1211from django .db import models
1312from django .utils import timezone
1413
@@ -42,7 +41,6 @@ class PutfileResult:
4241 content_type : str
4342 size : int
4443 sha1 : str
45- file_id : int | None = None
4644 blob_path : str | None = None
4745
4846
@@ -61,8 +59,7 @@ class EventAttachment(Model):
6159 """Attachment Metadata and Storage
6260
6361 The actual attachment data can be saved in different backing stores:
64- - Using the :class:`File` model using the `file_id` field.
65- This stores attachments chunked and deduplicated.
62+ - When the attachment is empty (0-size), `blob_path is None`.
6663 - When the `blob_path` field has a `:` prefix:
6764 It is saved inline in `blob_path` following the `:` prefix.
6865 This happens for "small" and ASCII-only (see `can_store_inline`) attachments.
@@ -88,8 +85,7 @@ class EventAttachment(Model):
8885
8986 date_added = models .DateTimeField (default = timezone .now , db_index = True )
9087
91- # the backing blob, either going through the `File` model,
92- # or directly to a backing blob store
88+ # The `file_id` will be removed in a multi-part migration soon.
9389 file_id = BoundedBigIntegerField (null = True , db_index = True )
9490 blob_path = models .TextField (null = True )
9591
@@ -114,51 +110,29 @@ def delete(self, *args: Any, **kwargs: Any) -> tuple[int, dict[str, int]]:
114110
115111 if self .blob_path :
116112 if self .blob_path .startswith (":" ):
117- return rv
113+ pass # nothing to do for inline-stored attachments
118114 elif self .blob_path .startswith ("eventattachments/v1/" ):
119115 storage = get_storage ()
116+ storage .delete (self .blob_path )
120117 else :
121118 raise NotImplementedError ()
122119
123- storage .delete (self .blob_path )
124- return rv
125-
126- try :
127- from sentry .models .files .file import File
128-
129- file = File .objects .get (id = self .file_id )
130- except ObjectDoesNotExist :
131- # It's possible that the File itself was deleted
132- # before we were deleted when the object is in memory
133- # This seems to be a case that happens during deletion
134- # code.
135- pass
136- else :
137- file .delete ()
138-
139120 return rv
140121
141122 def getfile (self ) -> IO [bytes ]:
142- if self .size == 0 :
123+ if not self .blob_path :
143124 return BytesIO (b"" )
144125
145- if self .blob_path :
146- if self .blob_path .startswith (":" ):
147- return BytesIO (self .blob_path [1 :].encode ())
148-
149- elif self .blob_path .startswith ("eventattachments/v1/" ):
150- storage = get_storage ()
151- compressed_blob = storage .open (self .blob_path )
152- dctx = zstandard .ZstdDecompressor ()
153- return dctx .stream_reader (compressed_blob , read_across_frames = True )
126+ if self .blob_path .startswith (":" ):
127+ return BytesIO (self .blob_path [1 :].encode ())
154128
155- else :
156- raise NotImplementedError ()
157-
158- from sentry .models .files .file import File
129+ elif self .blob_path .startswith ("eventattachments/v1/" ):
130+ storage = get_storage ()
131+ compressed_blob = storage .open (self .blob_path )
132+ dctx = zstandard .ZstdDecompressor ()
133+ return dctx .stream_reader (compressed_blob , read_across_frames = True )
159134
160- file = File .objects .get (id = self .file_id )
161- return file .getfile ()
135+ raise NotImplementedError ()
162136
163137 @classmethod
164138 def putfile (cls , project_id : int , attachment : CachedAttachment ) -> PutfileResult :
@@ -171,7 +145,6 @@ def putfile(cls, project_id: int, attachment: CachedAttachment) -> PutfileResult
171145 return PutfileResult (content_type = content_type , size = 0 , sha1 = sha1 ().hexdigest ())
172146
173147 blob = BytesIO (data )
174-
175148 size , checksum = get_size_and_checksum (blob )
176149
177150 if can_store_inline (data ):
0 commit comments