feat(audience): HttpTransport — background POST with gzip + retry (SDK-141)#691
feat(audience): HttpTransport — background POST with gzip + retry (SDK-141)#691ImmutableJeffrey wants to merge 3 commits intomainfrom
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f66dff6. Configure here.
f66dff6 to
b9faf5a
Compare
Gzip: compresses batch payloads using GZipStream (System.IO.Compression,
available in Unity 2021+ .NET Standard 2.1). Pure C#, all platforms.
HttpTransport: reads batches from DiskStore, wraps in {"batch":[...]},
gzips, POSTs to /v1/audience/messages with x-immutable-publishable-key
header. Derives sandbox vs production URL from key prefix.
Retry policy:
- 200: delete batch from disk
- 4xx: delete batch (validation error, won't succeed on retry)
- 5xx: keep on disk, exponential backoff (5s → 10s → 20s → 40s → 60s cap)
- Network error: same as 5xx
- Backoff resets after success
Testable via injected HttpMessageHandler — 14 tests, no network calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Plan specifies: 5s → 10s → 20s → 60s cap. Implementation had: 5s → 10s → 20s → 40s → 60s cap. Replace the bitshift formula with an explicit switch expression so the schedule is readable and matches the plan exactly. Update the test to verify the jump from 20s directly to 60s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Match the pattern used elsewhere in the codebase (Session.cs uses 60_000 for heartbeat interval). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b9faf5a to
52381a4
Compare
| } | ||
| } | ||
|
|
||
| if (count == 0) return null; |
There was a problem hiding this comment.
Should the Audience package also make it clear what's nullable like Passport? Didn't think about it until this as it can return null but it's private static string BuildPayload(IReadOnlyList<string> paths)
| /// threads via <see cref="HttpClient"/> — no main thread involvement. | ||
| /// | ||
| /// <para>Retry policy: 5xx and network errors keep events on disk with | ||
| /// exponential backoff (5s → 10s → 20s → 40s → 60s cap). 4xx and 200 |
There was a problem hiding this comment.
The class doc says '5s → 10s → 20s → 40s → 60s cap' but there is no 40s step. After 3 failures (20s) the switch jumps straight to 60s. The BackoffMs method comment is correct. Update the class summary to match: '5s → 10s → 20s → 60s cap'.
| sb.Append(json); | ||
| count++; | ||
| } | ||
| catch (Exception) |
There was a problem hiding this comment.
This swallows everything, not just the file-disappeared race. UnauthorizedAccessException means a permissions problem. DirectoryNotFoundException means the store path is wrong. Those should not be silently skipped. Narrow to IOException, which covers all the file system races without hiding anything else.

Summary
Adds
HttpTransportandGziputility for sending queued events to the audience backend.Gzip.CompressusesSystem.IO.Compression.GZipStream(pure C#, available in Unity 2021+).HttpTransportreads batches fromDiskStore, wraps them in{"batch":[...]}, gzips, and POSTs to/v1/audience/messageswith thex-immutable-publishable-keyheader. Runs on background threads viaHttpClient.pk_imapik-test-*routes to sandbox,pk_imapik-*routes to production.OnErrorcallback withAudienceErrorCodevalues (ValidationRejected,FlushFailed,NetworkError).HttpMessageHandleris injectable for testing; all transport tests use a mock handler.Linear: SDK-141
Note
Medium Risk
Introduces new background HTTP flushing logic (gzip payload construction, error handling, retry/backoff) that directly affects event delivery reliability and data loss behavior on 4xx responses.
Overview
Adds
HttpTransportto read event files fromDiskStore, wrap them into a{"batch":[...]}JSON payload, gzip-compress, and POST toConstants.MessagesPathwith thex-immutable-publishable-keyheader, selecting sandbox vs prod base URL from the key prefix.Implements outcome handling and backoff state: 2xx/4xx delete the batch (no retry), while 5xx and network errors keep events on disk and increase an exponential backoff (
BackoffMs,IsBackingOff), with an optionalonErrorcallback that is swallowed if it throws.Adds a
Gziputility plus new NUnit tests covering gzip round-trips andHttpTransportbehavior for success, empty queue, header/body correctness, environment URL selection, 4xx drop vs 5xx/network retry, backoff progression/reset, and error-callback safety.Reviewed by Cursor Bugbot for commit b9faf5a. Bugbot is set up for automated code reviews on this repo. Configure here.