Skip to content

Commit f9a36ed

Browse files
jpnurmiclaude
andcommitted
feat(cache): write consent-revoked envelopes in retry format
Consent-revoked envelopes were cached as `<uuid>.envelope` which `sentry__retry_send()` skips (filename ≤45 chars). Write them in retry format (`<ts>-<count>-<uuid>.envelope`) so they're picked up when consent is later given. - Add `retry_count` param to `write_envelope()`, `sentry__run_write_cache()`, and `sentry__run_move_cache()`: >= 0 produces retry format, -1 plain format - Add lock-free consent check to `sentry__retry_send()` using stored `user_consent` pointer (same pattern as the batcher) - Make `sentry__retry_write_envelope` static; remove from public header - Skip `sentry__capture_envelope()` in `process_old_runs` when consent missing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7595ef9 commit f9a36ed

5 files changed

Lines changed: 101 additions & 19 deletions

File tree

src/sentry_database.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,9 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash)
369369
continue;
370370
}
371371

372+
bool consent_missing = sentry__should_skip_upload();
372373
bool can_cache = options->cache_keep
373-
&& (sentry__should_skip_upload() || !options->http_retry
374+
&& (consent_missing || !options->http_retry
374375
|| !sentry__transport_can_retry(options->transport));
375376

376377
sentry_pathiter_t *run_iter = sentry__path_iter_directory(run_dir);
@@ -415,11 +416,14 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash)
415416
}
416417
}
417418
} else if (sentry__path_ends_with(file, ".envelope")) {
418-
sentry_envelope_t *envelope = sentry__envelope_from_path(file);
419-
sentry__capture_envelope(options->transport, envelope);
420-
419+
if (!consent_missing) {
420+
sentry_envelope_t *envelope
421+
= sentry__envelope_from_path(file);
422+
sentry__capture_envelope(options->transport, envelope);
423+
}
421424
if (can_cache
422-
&& sentry__run_move_cache(options->run, file, -1)) {
425+
&& sentry__run_move_cache(
426+
options->run, file, consent_missing ? 0 : -1)) {
423427
continue;
424428
}
425429
}

src/sentry_retry.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ typedef enum {
2828
struct sentry_retry_s {
2929
sentry_run_t *run;
3030
bool cache_keep;
31+
long *user_consent;
3132
uint64_t startup_time;
3233
volatile long state;
3334
volatile long scheduled;
@@ -49,6 +50,8 @@ sentry__retry_new(const sentry_options_t *options)
4950
sentry__mutex_init(&retry->sealed_lock);
5051
retry->run = sentry__run_incref(options->run);
5152
retry->cache_keep = options->cache_keep;
53+
retry->user_consent
54+
= options->require_user_consent ? (long *)&options->user_consent : NULL;
5255
retry->startup_time = sentry__usec_time() / 1000;
5356
return retry;
5457
}
@@ -141,6 +144,12 @@ size_t
141144
sentry__retry_send(sentry_retry_t *retry, uint64_t before,
142145
sentry_retry_send_func_t send_cb, void *data)
143146
{
147+
if (retry->user_consent
148+
&& sentry__atomic_fetch(retry->user_consent)
149+
!= SENTRY_USER_CONSENT_GIVEN) {
150+
return 0;
151+
}
152+
144153
sentry_pathiter_t *piter
145154
= sentry__path_iter_directory(retry->run->cache_path);
146155
if (!piter) {

tests/unit/test_cache.c

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "sentry_envelope.h"
44
#include "sentry_options.h"
55
#include "sentry_path.h"
6+
#include "sentry_retry.h"
67
#include "sentry_testsupport.h"
78
#include "sentry_uuid.h"
89
#include "sentry_value.h"
@@ -73,20 +74,24 @@ SENTRY_TEST(cache_keep)
7374
sentry_envelope_write_to_path(envelope, old_envelope_path) == 0);
7475
sentry_envelope_free(envelope);
7576

76-
sentry_path_t *cached_envelope_path
77-
= sentry__path_join_str(cache_path, envelope_filename);
78-
TEST_ASSERT(!!cached_envelope_path);
79-
8077
TEST_ASSERT(sentry__path_is_file(old_envelope_path));
81-
TEST_ASSERT(!sentry__path_is_file(cached_envelope_path));
8278

8379
sentry__process_old_runs(options, 0);
8480

8581
TEST_ASSERT(!sentry__path_is_file(old_envelope_path));
86-
TEST_ASSERT(sentry__path_is_file(cached_envelope_path));
82+
83+
int count = 0;
84+
sentry_pathiter_t *iter = sentry__path_iter_directory(cache_path);
85+
const sentry_path_t *entry;
86+
while (iter && (entry = sentry__pathiter_next(iter)) != NULL) {
87+
if (sentry__path_ends_with(entry, ".envelope")) {
88+
count++;
89+
}
90+
}
91+
sentry__pathiter_free(iter);
92+
TEST_CHECK_INT_EQUAL(count, 1);
8793

8894
sentry__path_free(old_envelope_path);
89-
sentry__path_free(cached_envelope_path);
9095
sentry__path_free(old_run_path);
9196
sentry__path_free(cache_path);
9297
sentry_free(envelope_filename);
@@ -328,15 +333,22 @@ SENTRY_TEST(cache_consent_revoked)
328333
sentry_value_new_message_event(SENTRY_LEVEL_INFO, "test", "revoked"));
329334

330335
int count = 0;
336+
bool is_retry_format = false;
331337
sentry_pathiter_t *iter = sentry__path_iter_directory(cache_path);
332338
const sentry_path_t *entry;
333339
while (iter && (entry = sentry__pathiter_next(iter)) != NULL) {
334340
if (sentry__path_ends_with(entry, ".envelope")) {
335341
count++;
342+
uint64_t ts;
343+
int attempt;
344+
const char *uuid;
345+
is_retry_format = sentry__parse_cache_filename(
346+
sentry__path_filename(entry), &ts, &attempt, &uuid);
336347
}
337348
}
338349
sentry__pathiter_free(iter);
339350
TEST_CHECK_INT_EQUAL(count, 1);
351+
TEST_CHECK(is_retry_format);
340352

341353
sentry__path_free(cache_path);
342354
sentry_close();
@@ -413,21 +425,32 @@ SENTRY_TEST(cache_consent_revoked_old_run)
413425
sentry_envelope_write_to_path(envelope, old_envelope_path) == 0);
414426
sentry_envelope_free(envelope);
415427

416-
sentry_path_t *cached_envelope_path
417-
= sentry__path_join_str(cache_path, envelope_filename);
418-
TEST_ASSERT(!!cached_envelope_path);
419-
420428
TEST_ASSERT(sentry__path_is_file(old_envelope_path));
421-
TEST_ASSERT(!sentry__path_is_file(cached_envelope_path));
422429

423430
sentry__process_old_runs(options, 0);
424431

425432
TEST_ASSERT(!sentry__path_is_file(old_envelope_path));
426-
TEST_ASSERT(sentry__path_is_file(cached_envelope_path));
427433
TEST_ASSERT(!sentry__path_is_dir(old_run_path));
428434

435+
int count = 0;
436+
bool is_retry_format = false;
437+
sentry_pathiter_t *iter = sentry__path_iter_directory(cache_path);
438+
const sentry_path_t *entry;
439+
while (iter && (entry = sentry__pathiter_next(iter)) != NULL) {
440+
if (sentry__path_ends_with(entry, ".envelope")) {
441+
count++;
442+
uint64_t ts;
443+
int attempt;
444+
const char *uuid;
445+
is_retry_format = sentry__parse_cache_filename(
446+
sentry__path_filename(entry), &ts, &attempt, &uuid);
447+
}
448+
}
449+
sentry__pathiter_free(iter);
450+
TEST_CHECK_INT_EQUAL(count, 1);
451+
TEST_CHECK(is_retry_format);
452+
429453
sentry__path_free(old_envelope_path);
430-
sentry__path_free(cached_envelope_path);
431454
sentry__path_free(old_run_path);
432455
sentry__path_free(cache_path);
433456
sentry_free(envelope_filename);

tests/unit/test_retry.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,48 @@ SENTRY_TEST(retry_trigger)
460460
sentry__retry_free(retry);
461461
sentry_close();
462462
}
463+
464+
SENTRY_TEST(retry_consent)
465+
{
466+
#if defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS)
467+
SKIP_TEST();
468+
#endif
469+
SENTRY_TEST_OPTIONS_NEW(options);
470+
sentry_options_set_dsn(options, "https://foo@sentry.invalid/42");
471+
sentry_options_set_http_retry(options, true);
472+
sentry_options_set_require_user_consent(options, true);
473+
sentry_init(options);
474+
sentry_user_consent_revoke();
475+
476+
sentry_retry_t *retry = sentry__retry_new(options);
477+
TEST_ASSERT(!!retry);
478+
479+
const sentry_path_t *cache_path = options->run->cache_path;
480+
sentry__path_remove_all(cache_path);
481+
sentry__path_create_dir_all(cache_path);
482+
483+
uint64_t old_ts
484+
= sentry__usec_time() / 1000 - 10 * sentry__retry_backoff(0);
485+
sentry_uuid_t event_id = sentry_uuid_new_v4();
486+
write_retry_file(options->run, old_ts, 0, &event_id);
487+
488+
TEST_CHECK_INT_EQUAL(count_envelope_files(cache_path), 1);
489+
490+
// consent revoked: retry_send returns 0 without calling send_cb
491+
retry_test_ctx_t ctx = { 200, 0 };
492+
size_t remaining = sentry__retry_send(retry, 0, test_send_cb, &ctx);
493+
TEST_CHECK_INT_EQUAL(ctx.count, 0);
494+
TEST_CHECK_INT_EQUAL(remaining, 0);
495+
TEST_CHECK_INT_EQUAL(count_envelope_files(cache_path), 1);
496+
497+
// give consent: retry_send sends and removes the file
498+
sentry_user_consent_give();
499+
ctx = (retry_test_ctx_t) { 200, 0 };
500+
remaining = sentry__retry_send(retry, UINT64_MAX, test_send_cb, &ctx);
501+
TEST_CHECK_INT_EQUAL(ctx.count, 1);
502+
TEST_CHECK_INT_EQUAL(remaining, 0);
503+
TEST_CHECK_INT_EQUAL(count_envelope_files(cache_path), 0);
504+
505+
sentry__retry_free(retry);
506+
sentry_close();
507+
}

tests/unit/tests.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ XX(read_write_envelope_to_invalid_path)
173173
XX(recursive_paths)
174174
XX(retry_backoff)
175175
XX(retry_cache)
176+
XX(retry_consent)
176177
XX(retry_filename)
177178
XX(retry_result)
178179
XX(retry_session)

0 commit comments

Comments
 (0)