Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,9 @@ That last case previously produced **no report whatsoever** — the SDK shut dow
2. `POST /v2/issues?app_token=…` — body = `app_token + access_token + request.json` → `result.endpoint` (presigned URL).
3. `PUT <presigned>` — raw ZIP bytes, `Content-Length`, no auth header. Success → delete bundle + cached URL.

**Server semantics:** `12003` SimilarCrashExists → drop silently; `12004` TooManySimilar → blacklist those `signatures` locally + drop; `401`/invalid → re-register session + retry; `429`/`503`/network → retry.
**Server semantics:** the "similar crash exists" verdict → drop silently; the "too many similar" verdict → blacklist those `signatures` locally + drop; `401`/invalid → re-register session + retry; `429`/`503`/network → retry.

**Offline queue + cross-launch retry:** bundles in `<data>/queue/`; sync uploader thread (default; optional async `reqwest`) with backoff `min(30s·2^n, 300s)`, caps export 15 / upload 60. Pending-report list + retry counters persist → next launch re-drives. Crash reports flush synchronously first. A presigned endpoint obtained from `create_issue` is persisted (a `.endpoint` sidecar) when its PUT fails transiently, so a retry **resumes at the PUT** rather than re-creating the issue (which would mint a duplicate or trip server dedup `12003` and drop the bundle un-uploaded). The retry counter (and the abandon-at-cap deletion) advances **only** on backoff-paced attempts; a forced `flush()` re-attempts a queued report but never consumes its durable budget, so an offline flush can't delete a still-deliverable crash bundle.
**Offline queue + cross-launch retry:** bundles in `<data>/queue/`; sync uploader thread (default; optional async `reqwest`) with backoff `min(30s·2^n, 300s)`, caps export 15 / upload 60. Pending-report list + retry counters persist → next launch re-drives. Crash reports flush synchronously first. A presigned endpoint obtained from `create_issue` is persisted (a `.endpoint` sidecar) when its PUT fails transiently, so a retry **resumes at the PUT** rather than re-creating the issue (which would mint a duplicate or trip server dedup and drop the bundle un-uploaded). The retry counter (and the abandon-at-cap deletion) advances **only** on backoff-paced attempts; a forced `flush()` re-attempts a queued report but never consumes its durable budget, so an offline flush can't delete a still-deliverable crash bundle.

**APM:** separate lighter path `POST /v2/performance/transactions` (one bare transaction, batched ~30 s or realtime).

Expand Down
6 changes: 3 additions & 3 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ were pre-verified by inspection against `crash-handler` 0.6.3 + `libc`).
60-retry cap while offline — forced attempts are decoupled from the counter, so
a still-deliverable crash bundle can't be deleted in ~1.3 s. Retry now **resumes
at the presigned PUT** via a persisted `.endpoint` sidecar instead of re-POSTing
`create_issue` (no duplicate issues / no `12003`-drop of an un-uploaded bundle).
`create_issue` (no duplicate issues / no duplicate-verdict drop of an un-uploaded bundle).
- **PII (high):** failed-request network entries scrub the URL out of reqwest's
error `Display` (was leaking full credential-bearing URLs); header redaction is
unified onto the normalized param matcher (`signature`/`csrf`/`api_key` no
Expand Down Expand Up @@ -262,8 +262,8 @@ The defects, in the order they blocked things:
application errors with `ok:false` and **HTTP 200**, but the transport only
inspected the envelope for 4xx/5xx. The error degraded to "no endpoint in
response", the report was dropped, and the server's explanation was thrown
away. The known codes `12003`/`12004` were reachable only on a 4xx too — so
`12004` skipped the signature blacklist entirely.
away. The known duplicate and too-many-similar verdicts were reachable only on a 4xx too — so
the latter skipped the signature blacklist entirely.
3. **Nothing could observe a dropped report** (`1b2a456`). `flush()` returns
"the queue is empty", and the queue drains on success *and* on permanent
failure alike — so it returned `true` while the server accepted nothing.
Expand Down
4 changes: 2 additions & 2 deletions crates/bugsee-core/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ pub fn next_attempt(report: &QueuedReport) -> i64 {
/// The cached presigned upload endpoint for a report, if a prior attempt created
/// the issue but the PUT failed transiently. A retry resumes at the PUT (step 3)
/// instead of re-POSTing `create_issue` (which would mint a duplicate issue, or
/// trip server dedup `12003` and drop the bundle without ever uploading it).
/// trip server dedup and drop the bundle without ever uploading it).
pub fn endpoint(report: &QueuedReport) -> Option<String> {
std::fs::read_to_string(report.endpoint_path())
.ok()
Expand Down Expand Up @@ -262,7 +262,7 @@ fn load_blacklist(data_dir: &Path) -> BTreeSet<String> {
.unwrap_or_default()
}

/// Persist crash signatures the server told us to stop sending (`12004`).
/// Persist crash signatures the server told us to stop sending (the too-many-similar verdict).
///
/// Appends only the not-yet-present signatures, each on its own line, under
/// `O_APPEND`. Appending (rather than read-modify-rewrite of the whole set) is
Expand Down
2 changes: 1 addition & 1 deletion crates/bugsee-core/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2019,7 +2019,7 @@ fn deliver_queued(
request_json,
};
// Resume at the PUT if a prior attempt created the issue but the PUT failed
// (F14): re-POSTing create_issue mints a duplicate issue, or trips 12003 and
// (F14): re-POSTing create_issue mints a duplicate issue, or trips the server's dedup and
// deletes the bundle unuploaded.
let cached_endpoint = queue::endpoint(report);
let mut endpoint_out: Option<String> = None;
Expand Down
4 changes: 2 additions & 2 deletions crates/bugsee-core/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub trait Transport: Send + Sync {
///
/// `cached_endpoint` is a presigned upload URL a prior attempt obtained but
/// failed to PUT to; when set, delivery resumes at the PUT instead of re-creating
/// the issue (avoiding a duplicate issue / a `12003` drop of an un-uploaded
/// the issue (avoiding a duplicate issue / a duplicate-verdict drop of an un-uploaded
/// bundle). On a *transient* PUT failure the endpoint that should be persisted
/// for the next retry is written to `endpoint_out`; on any other outcome
/// `endpoint_out` is left `None` (the caller then clears any stale cache).
Expand Down Expand Up @@ -106,7 +106,7 @@ pub fn deliver(
return match transport.upload_bundle(&endpoint, &report.zip) {
Ok(()) => Ok(()),
// Persist the endpoint so the retry resumes at the PUT rather
// than re-POSTing create_issue (duplicate issue / 12003 drop).
// than re-POSTing create_issue (duplicate issue / duplicate-verdict drop).
Err(TransportError::Transient(e)) => {
*endpoint_out = Some(endpoint);
Err(TransportError::Transient(e))
Expand Down
Loading