fix(transform): return an error on non-2xx in send_http_post#3
fix(transform): return an error on non-2xx in send_http_post#3Steven Patterson (stevepatterson5) wants to merge 4 commits into
Conversation
send_http_post discarded the HTTP response and never inspected the status code, so any response the retry client does not retry (every 4xx except 429) came back with a nil error and the batch was silently dropped. In the seceng LogScale pipelines this silently discarded ~429k events over 7 days across 10 sources, with no error, no 5xx metric, and no dead-letter message; vault-audit alone accounted for ~306k. The cause of the upstream 400s is still unknown precisely because the response body was thrown away. Read a bounded 512-byte prefix of the body before discarding the rest so the failure can explain itself, then return an error for any non-2xx status. The limit keeps server-echoed payload data out of logs. The existing debug log is unchanged and still emitted before the return. Adds a test covering the statuses the client does not retry. 429 and 5xx are excluded deliberately: exercising them would incur the client's full backoff and exceed the 30s test timeout used by CI and the image build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CODEOWNERS still named @brexhq/substation, a team that does not exist in this org. GitHub ignores unresolvable owner entries, so the branch protection setting require_code_owner_reviews had no owner to enforce and the rule was inert. Note: @LiveRamp/seceng needs write access to this repository for the entry to resolve. Without that grant this rule stays inert for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🔗 Linked repositories identifiedCodeRabbit considers these linked repositories for cross-repo context during reviews:
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughChangesThe HTTP transformer now reports non-2xx responses with bounded response-body context and includes status tests. Repository ownership and lint directives are also updated. HTTP response handling
Repository maintenance
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@transform/send_http_post.go`:
- Around line 217-219: Update the non-2xx error construction in the HTTP POST
flow to avoid interpolating the sensitive url value into the returned error.
Preserve the transform ID, status code, and trimmed response body, while
omitting the URL or using an established redaction helper if one exists.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f1530483-eb67-4c6c-aae4-7dcdf0cde5ae
📒 Files selected for processing (3)
CODEOWNERStransform/send_http_post.gotransform/send_http_post_test.go
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
liveramp/big_data_core(manual)
The URL is produced by secrets.Interpolate, so it may carry credentials or sensitive query parameters. Returning it verbatim made those values part of the error contract, and errors propagate further than the debug log above (CWE-532). The transform ID, status code, and trimmed body are retained. Also clears the three golangci-lint failures blocking the go check: - drops an unused //nolint:errcheck directive on the bounded body read (the error is already explicitly discarded, so nolintlint flagged it) - rewrites pubsub.go's malformed `// nolint: gocognit` as a compliant `//nolint:gocyclo,gocognit`, which resolves both the leading-space nolintlint error and the pre-existing gocyclo complexity failure The pubsub.go directive is pre-existing debt unrelated to this change, but golangci-lint fails the whole run, so the check cannot pass without it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.golangci.yml enables three complexity linters: cyclop (max-complexity 30), gocyclo (default 30), and gocognit (min-complexity raised to 45, so it does not fire here). pubSubHandler measures 34 under gocyclo and 36 under cyclop, so both need suppressing; only gocyclo was listed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Description
Two changes:
send_http_postnow returns an error on any non-2xx response. It previously discarded the response and never inspected the status code. A bounded 512-byte prefix of the body is retained so the failure can explain itself; the limit keeps server-echoed payload data out of logs and errors. The existing debug log is unchanged and still emitted before the return.@LiveRamp/secenginstead of@brexhq/substation, a team that does not exist in this org.Motivation and Context
The HTTP client is
retryablehttp.NewClient()with stock defaults. ItsbaseRetryPolicyretries transport errors, 429, and 5xx (except 501) — everything else returnsfalse, nil. So every 4xx except 429 came back witherr == nil, and because the status code was never checked, the batch was silently discarded: no error, no 5xx metric, no dead-letter message.This is not hypothetical. In the seceng LogScale pipelines it silently dropped 429,030 events over 7 days across 10 sources,
vault-auditalone accounting for ~306k (~994 MB). Every one was an HTTP 400. The pipelines reported perfect health throughout.That figure is a floor, not a total — the only trace was a Debug-level log line, so sources running with
substation_debug = false(including the largest, at 733 TB) produced no evidence at all.The cause of the upstream 400s is still unknown, precisely because the response body was thrown away. It is not payload size: identical batch sizes both succeed and fail, and 253 of 400 sampled successful sends were >10 MB. The 400s return in ~80 ms versus ~17 s for a successful ingest, which points at request-level validation rather than capacity. This change is what will surface the reason.
On CODEOWNERS: GitHub silently ignores unresolvable owner entries, so the
require_code_owner_reviewsbranch protection setting had no owner to enforce and the rule was inert.@LiveRamp/secenghas since been granted access to this repository, so the entry now resolves.How Has This Been Tested?
gofmt,go build ./...,go vet ./transform/all clean.TZ=UTC go test -timeout 30s ./...— 12 packages. WithoutTZ=UTC,TestTimeToStringandTestTransform/time_to_stringfail on any host not in UTC. Pre-existing and unrelated to this change; CI and the container build both run UTC.transform/send_http_post_test.gocovering 200, 201, 400, 401, 404. Verified it actually catches the bug: reverting only the source change while keeping the test yieldsexpected an error for status 400, got nilfor all three error cases.seceng-containers/containers/substation/Dockerfile.Types of changes
Checklist:
Deployment note
Merging this does not change production. The image is built by
LiveRamp/seceng-containers, which pinsARG SUBSTATION_COMMIT(currentlyd1a0320); that needs bumping to this commit to trigger a Jenkins build, thenlocal.substation_shabumped in the infrastructure repo.When it does reach production, the silent drops become loud failures — 500 → Pub/Sub retry → DLQ. That is the intent, but it will generate DLQ traffic on
vault-auditimmediately, so it should land when someone can watch it.🤖 Generated with Claude Code
Summary by CodeRabbit