Skip to content

test: wall-clock assert on a 64KB payload in mask_test.go is one refactor from a -race timeout #1209

Description

@Dumbris

Summary

TestMaskTextStaysCheapOnAPayloadFullOfSecrets (internal/security/mask_test.go:254) asserts a hard wall-clock bound on a 64KB payload:

for i := 0; b.Len() < 64*1024; i++ { ... }   // 65,579 bytes
...
if elapsed > 2*time.Second {
    t.Fatalf("masking a %d-byte payload took %s", len(text), elapsed)
}

It passes comfortably today and is not currently flaky. Filing it because it is the same shape that just cost us a red main, and it is one refactor away from converting into that failure.

Why now

#1174 added TestDetectionScansUntruncatedResponse with a ~75KB payload fed to security.Detector.Scan, bounded by waitForDetectionMetadata's 5s poll deadline. It passed locally and failed in both CI workflows, because:

  • Detector.Scan costs ~5.5 µs/byte, and the race detector multiplies that by ~24x (measured 114–137 µs/byte across 1K–75K payloads)
  • CI runs -race in unit-tests.yml and e2e-tests.yml; a plain local go test does not
  • so the scan took ~9.5s against a 5s deadline

Fixed in #1207 by sizing the payload down. mask_test.go is the only other place in the repo where a multi-KB payload meets a hard deadline.

Current headroom (measured, -race, Apple M-series)

go test ./internal/security/ -race -run TestMaskTextStaysCheapOnAPayloadFullOfSecrets -count=3
masked 65579 bytes in 250.714041ms
masked 65579 bytes in 233.734208ms
masked 65579 bytes in 238.472417ms

3.8 µs/byte under -race — about 36x cheaper per byte than Scan, because MaskText/Redact is a single replacement sweep rather than patterns + file paths + high-entropy windows. That leaves roughly 8x headroom against the 2s assert, so a 2–3x slower GitHub runner is fine.

The risk

The margin depends entirely on masking never touching the Scan path. If anyone routes MaskText through Scan, or adds a per-value Scan to the mask sweep, the per-byte cost jumps ~36x and this test fails immediately on CI while still passing locally — with a wall-clock message that reads like a performance regression rather than a test-sizing problem.

A bare absolute wall-clock assertion on a shared CI runner is also inherently the fragile shape: it measures the runner as much as the code.

Suggested fix

Any of these, in rough order of preference:

  1. Shrink the payload (4–8KB) and keep the bound — the test is guarding against accidental O(n²), which a smaller payload still catches.
  2. Assert on shape rather than wall-clock: time two sizes and check the ratio is roughly linear, which is race- and runner-independent.
  3. Keep the payload but raise the bound and document the -race multiplier, so a future reader knows what the number is protecting against.

Option 2 is the only one that actually survives someone changing the underlying cost, and it tests the real property ("masking is linear") instead of a proxy.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions