Skip to content

Commit 9868777

Browse files
jpnurmiclaude
andcommitted
refactor(tus): cache external attachments by type, choose upload method at transport time
Replace sentry__is_large_attachment(path, size) with sentry__attachment_is_external(att) which returns true for MINIDUMP type or size >= SENTRY_LARGE_ATTACHMENT_SIZE. Minidumps are always cached as external files; other large attachments continue to be cached by size. Rename sentry__cache_large_attachments to sentry__cache_external_attachments. At transport time, resolve_and_send_external_attachments checks each cached attachment: large files with TUS support go via TUS upload, smaller files are inlined into the envelope. For raw envelopes (read from disk on restart), sentry__envelope_append_raw_attachment builds the item bytes and appends them directly to the raw payload buffer. External attachment cleanup is deferred until after successful send so files survive for retry on network failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0e2679d commit 9868777

10 files changed

Lines changed: 228 additions & 71 deletions

src/backends/sentry_backend_breakpad.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor,
186186
tmp.path = dump_path;
187187
tmp.type = MINIDUMP;
188188
tmp.next = nullptr;
189-
sentry__cache_large_attachments(
189+
sentry__cache_external_attachments(
190190
options->run->cache_path, &event_id, &tmp, nullptr);
191191
}
192192

src/backends/sentry_backend_crashpad.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ report_to_envelope(const crashpad::CrashReportDatabase::Report &report,
531531
sentry__envelope_add_attachments(envelope, attachments);
532532
sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope);
533533
if (options->run) {
534-
sentry__cache_large_attachments(options->run->cache_path,
534+
sentry__cache_external_attachments(options->run->cache_path,
535535
&event_id, attachments, options->run->run_path);
536536
}
537537
} else {

src/sentry_attachment.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@
77

88
#define SENTRY_LARGE_ATTACHMENT_SIZE (100 * 1024 * 1024) // 100 MB
99

10-
static inline bool
11-
sentry__is_large_attachment(const sentry_path_t *path, size_t file_size)
12-
{
13-
// TODO: for temporarily testing with <1 MB minidumps
14-
// return file_size < 1024 * 1024 && sentry__path_ends_with(path, ".dmp");
15-
(void)path;
16-
return file_size >= SENTRY_LARGE_ATTACHMENT_SIZE;
17-
}
18-
1910
/**
2011
* The attachment_type.
2112
*/
@@ -51,6 +42,24 @@ struct sentry_attachment_s {
5142
sentry_attachment_t *next; // Linked list pointer
5243
};
5344

45+
/**
46+
* Returns the size in bytes of the attachment's data (buffer length or file
47+
* size).
48+
*/
49+
size_t sentry__attachment_get_size(const sentry_attachment_t *attachment);
50+
51+
/**
52+
* Returns true if the attachment should be cached as an external file.
53+
* Minidumps are always external; other attachments are external when they
54+
* exceed the large attachment size threshold.
55+
*/
56+
static inline bool
57+
sentry__attachment_is_external(const sentry_attachment_t *att)
58+
{
59+
return att->type == MINIDUMP
60+
|| sentry__attachment_get_size(att) >= SENTRY_LARGE_ATTACHMENT_SIZE;
61+
}
62+
5463
/**
5564
* Creates a new file attachment. Takes ownership of `path`.
5665
*/
@@ -100,12 +109,6 @@ void sentry__attachments_remove(
100109
void sentry__attachments_extend(
101110
sentry_attachment_t **attachments_ptr, sentry_attachment_t *attachments);
102111

103-
/**
104-
* Returns the size in bytes of the attachment's data (buffer length or file
105-
* size).
106-
*/
107-
size_t sentry__attachment_get_size(const sentry_attachment_t *attachment);
108-
109112
/**
110113
* Returns the filename string for the attachment (basename of `filename` if
111114
* set, otherwise basename of `path`).

src/sentry_core.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -731,8 +731,8 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event,
731731
= all_attachments ? all_attachments : scope->attachments;
732732
sentry__envelope_add_attachments(envelope, atts);
733733
if (options->run) {
734-
sentry__cache_large_attachments(options->run->cache_path, event_id,
735-
atts, options->run->run_path);
734+
sentry__cache_external_attachments(options->run->cache_path,
735+
event_id, atts, options->run->run_path);
736736
}
737737
}
738738

@@ -822,7 +822,7 @@ prepare_user_feedback(sentry_value_t user_feedback, sentry_hint_t *hint)
822822
sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope);
823823
SENTRY_WITH_OPTIONS (options) {
824824
if (options->run) {
825-
sentry__cache_large_attachments(options->run->cache_path,
825+
sentry__cache_external_attachments(options->run->cache_path,
826826
&event_id, hint->attachments, options->run->run_path);
827827
}
828828
}
@@ -1761,7 +1761,7 @@ sentry_capture_minidump_n(const char *path, size_t path_len)
17611761
// the minidump is added as an attachment, with the type
17621762
// `event.minidump`
17631763
size_t dump_size = sentry__path_get_size(dump_path);
1764-
bool is_large = sentry__is_large_attachment(dump_path, dump_size);
1764+
bool is_large = dump_size >= SENTRY_LARGE_ATTACHMENT_SIZE;
17651765
sentry_envelope_item_t *item = sentry__envelope_add_from_path(
17661766
envelope, dump_path, "attachment");
17671767

@@ -1776,7 +1776,7 @@ sentry_capture_minidump_n(const char *path, size_t path_len)
17761776
tmp.path = dump_path;
17771777
tmp.type = MINIDUMP;
17781778
tmp.next = NULL;
1779-
sentry__cache_large_attachments(
1779+
sentry__cache_external_attachments(
17801780
options->run->cache_path, &event_id, &tmp, NULL);
17811781
} else {
17821782
sentry_envelope_free(envelope);

src/sentry_database.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ write_envelope(const sentry_path_t *path, const sentry_envelope_t *envelope,
166166
}
167167

168168
void
169-
sentry__cache_large_attachments(const sentry_path_t *cache_path,
169+
sentry__cache_external_attachments(const sentry_path_t *cache_path,
170170
const sentry_uuid_t *event_id, const sentry_attachment_t *attachments,
171171
const sentry_path_t *run_path)
172172
{
@@ -187,7 +187,7 @@ sentry__cache_large_attachments(const sentry_path_t *cache_path,
187187

188188
for (const sentry_attachment_t *att = attachments; att; att = att->next) {
189189
size_t file_size = sentry__attachment_get_size(att);
190-
if (!sentry__is_large_attachment(att->path, file_size)) {
190+
if (!sentry__attachment_is_external(att)) {
191191
continue;
192192
}
193193

src/sentry_database.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,13 @@ bool sentry__has_crash_marker(const sentry_options_t *options);
140140
bool sentry__clear_crash_marker(const sentry_options_t *options);
141141

142142
/**
143-
* Cache large attachments (>= SENTRY_LARGE_ATTACHMENT_SIZE) to
143+
* Cache external attachments (e.g. minidumps) to
144144
* `<cache_path>/<event-uuid>/` and write `__sentry-attachments.json` metadata.
145145
*
146146
* When `run_path` is non-NULL and a file attachment's parent directory
147147
* matches it, the file is renamed instead of copied.
148148
*/
149-
void sentry__cache_large_attachments(const sentry_path_t *cache_path,
149+
void sentry__cache_external_attachments(const sentry_path_t *cache_path,
150150
const sentry_uuid_t *event_id, const sentry_attachment_t *attachments,
151151
const sentry_path_t *run_path);
152152

src/sentry_envelope.c

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,7 @@ sentry__envelope_add_attachment(
663663
return NULL;
664664
}
665665

666-
size_t file_size = sentry__attachment_get_size(attachment);
667-
668-
if (sentry__is_large_attachment(attachment->path, file_size)) {
666+
if (sentry__attachment_is_external(attachment)) {
669667
return NULL;
670668
}
671669

@@ -728,7 +726,7 @@ sentry__envelope_add_from_path(
728726
return NULL;
729727
}
730728
size_t file_size = sentry__path_get_size(path);
731-
if (sentry__is_large_attachment(path, file_size)) {
729+
if (file_size >= SENTRY_LARGE_ATTACHMENT_SIZE) {
732730
return NULL;
733731
}
734732
size_t buf_len;
@@ -741,6 +739,79 @@ sentry__envelope_add_from_path(
741739
return envelope_add_from_owned_buffer(envelope, buf, buf_len, type);
742740
}
743741

742+
bool
743+
sentry__envelope_append_raw_attachment(sentry_envelope_t *envelope,
744+
const sentry_path_t *path, const char *filename,
745+
const char *attachment_type, const char *content_type)
746+
{
747+
if (!envelope || !envelope->is_raw || !path) {
748+
return false;
749+
}
750+
751+
size_t file_len;
752+
char *file_buf = sentry__path_read_to_buffer(path, &file_len);
753+
if (!file_buf) {
754+
return false;
755+
}
756+
757+
sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL);
758+
if (!jw) {
759+
sentry_free(file_buf);
760+
return false;
761+
}
762+
sentry_value_t headers = sentry_value_new_object();
763+
sentry_value_set_by_key(
764+
headers, "type", sentry_value_new_string("attachment"));
765+
sentry_value_set_by_key(
766+
headers, "length", sentry_value_new_int32((int32_t)file_len));
767+
if (filename) {
768+
sentry_value_set_by_key(
769+
headers, "filename", sentry_value_new_string(filename));
770+
}
771+
if (attachment_type) {
772+
sentry_value_set_by_key(headers, "attachment_type",
773+
sentry_value_new_string(attachment_type));
774+
}
775+
if (content_type) {
776+
sentry_value_set_by_key(
777+
headers, "content_type", sentry_value_new_string(content_type));
778+
}
779+
sentry__jsonwriter_write_value(jw, headers);
780+
sentry_value_decref(headers);
781+
size_t header_len = 0;
782+
char *header_buf = sentry__jsonwriter_into_string(jw, &header_len);
783+
if (!header_buf) {
784+
sentry_free(file_buf);
785+
return false;
786+
}
787+
788+
size_t old_len = envelope->contents.raw.payload_len;
789+
size_t new_len = old_len + 1 + header_len + 1 + file_len;
790+
char *new_payload = sentry_malloc(new_len);
791+
if (!new_payload) {
792+
sentry_free(header_buf);
793+
sentry_free(file_buf);
794+
return false;
795+
}
796+
797+
char *p = new_payload;
798+
memcpy(p, envelope->contents.raw.payload, old_len);
799+
p += old_len;
800+
*p++ = '\n';
801+
memcpy(p, header_buf, header_len);
802+
p += header_len;
803+
*p++ = '\n';
804+
memcpy(p, file_buf, file_len);
805+
806+
sentry_free(envelope->contents.raw.payload);
807+
envelope->contents.raw.payload = new_payload;
808+
envelope->contents.raw.payload_len = new_len;
809+
810+
sentry_free(header_buf);
811+
sentry_free(file_buf);
812+
return true;
813+
}
814+
744815
static void
745816
sentry__envelope_serialize_headers_into_stringbuilder(
746817
const sentry_envelope_t *envelope, sentry_stringbuilder_t *sb)

src/sentry_envelope.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ void sentry__envelope_add_attachments(
121121
sentry_envelope_item_t *sentry__envelope_add_from_path(
122122
sentry_envelope_t *envelope, const sentry_path_t *path, const char *type);
123123

124+
/**
125+
* Append an attachment item to a raw envelope by reading `path` and
126+
* building the item header/payload bytes directly into the raw buffer.
127+
*/
128+
bool sentry__envelope_append_raw_attachment(sentry_envelope_t *envelope,
129+
const sentry_path_t *path, const char *filename,
130+
const char *attachment_type, const char *content_type);
131+
124132
/**
125133
* This will add the given buffer as a new envelope item of type `type`.
126134
*/

0 commit comments

Comments
 (0)