feat: add metrics for gcs payload cleanup on failed db transactions - #2528
feat: add metrics for gcs payload cleanup on failed db transactions#2528taddes wants to merge 9 commits into
Conversation
| warn!("gcs payload cleanup failed for {gs_url}: {e}"); | ||
| metrics.incr_with_tags(CLEANUP_METRIC, cleanup_failure_tags(handler, "gcs_error")); | ||
| } | ||
| } |
There was a problem hiding this comment.
You don't need to break up the chain for the side effect. You can do
.inspect(|_| metrics.incr_with_tags(CLEANUP_METRIC, cleanup_tags(handler, "success")))
.inspect_err(|e| {
warn!("gcs payload cleanup failed for {gs_url}: {e}");
metrics.incr_with_tags(CLEANUP_METRIC, cleanup_failure_tags(handler, "gcs_error"));
})There was a problem hiding this comment.
ok cool, good suggestion
| metrics.incr_with_tags(CLEANUP_METRIC, cleanup_failure_tags(handler, "invalid_url")); | ||
| return Err(e); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Since you are returning the Result as is (parsed -> parsed, Err(e) -> Err(e)) you can use inspect_err for the side effect.
| client: &StorageControl, | ||
| gs_url: &str, | ||
| metrics: &Metrics, | ||
| handler: &str, |
There was a problem hiding this comment.
Maybe make an enum instead of allowing any string.
dafe56c to
f21a62f
Compare
| # The Spanner emulator aborts a test transaction with a Conflict (503) when it | ||
| # hits lock contention on the shared test rows. That is transient, so retry in | ||
| # CI rather than failing the whole job; local runs keep the strict retries = 0. | ||
| retries = 2 |
There was a problem hiding this comment.
This is a bit out of scope. But also, line 6 above is test-threads = 1 so I'm not sure the lock contention is even possible?
There was a problem hiding this comment.
It was a bug I was running into locally and that was the suggested fix 🤷 I can look a bit more closely at that part
| Err(_) => { | ||
| metrics.incr_with_tags( | ||
| CLEANUP_METRIC, | ||
| cleanup_tags(CleanupHandler::PutBso, CleanupResult::Skipped), | ||
| ); | ||
| } |
There was a problem hiding this comment.
I'd argue we don't need these Err blocks and their metrics at all because we should never have anything offloaded in the first place if there's no gcs_control_client (there should never be a lack of gcs_control_client if there is a gcs_client).
Or at least we could do this more simpler by just propagating the error (gcs_control_client()?) -- similarly to the above state.gcs_client()? -- if we're set to offload and there's no gcs clients, just fail with an error.
|
|
||
| /// Sink that keeps every statsd line so tests can assert on emissions. | ||
| #[derive(Debug)] | ||
| struct RecordingSink(Arc<Mutex<Vec<String>>>); |
There was a problem hiding this comment.
FYI cadence ships a similar sink for testing -- pretty similar but it gives you a https://docs.rs/crossbeam-channel/0.5.15/crossbeam_channel/struct.Receiver.html you can iterate over (here's an e.g. of it used in contile)
3a54ffd to
b48e323
Compare
Description
STOR-623 added a best-effort GCS delete for payloads that were offloaded before a
write's database transaction failed, but it was fire and forget, so there's no way
to tell from prod whether those deletes actually land. This adds a counter around
them.
The metric is
storage.gcs.payload.cleanup, tagged with:handler:put_bsoorpost_collectionresult:successorfailurereasonon failures:gcs_errorif GCS rejected the delete,invalid_urlif thegs://URL didn't parse (a code bug rather than an infra problem)Both are emitted inside
delete_payload, so any future call site gets them for free.Tests use cadence's
SpyMetricSinkover the existingStorageControlstubs, one perbranch. Commits are split so each addition is reviewable on its own.
Note on local verification:
cargo fmt --checkis clean, clippy is clean forsyncserver, and theweb::payload_offloadunit tests pass. The fullmake clippy_mysqlcan't run on arm64 here becausegrpcio-sysfails to build, andit also trips two pre-existing
needless_as_byteserrors intokenserver-auththatare already on master, so CI is the real check for those.
Review round
Rebased onto master, so the merge commit is gone and the history is linear.
skippedresult entirely. Both GCS clients are built together atstartup when a payload bucket is configured, so there is no state where the control
client is missing but the upload succeeded. The counter could only ever read zero.
Back to
if let Ok(client)with a comment recording why it holds.SpyMetricSink, which neededcrossbeam-channelas a dev-dependency to name the receiver. It was already in thelock via cadence.
.config/nextest.tomlretry commit as out of scope. Worth noting forwhenever it gets picked up separately: nextest profiles inherit from
[profile.default], sotest-threads = 1does apply toprofile.ci, which meansparallel tests contending on shared rows can't be the cause of that abort.
I did not take the
gcs_control_client()?variant. At the cleanup site the?wouldreturn the GCS client error in place of the database error that actually caused the
rollback, so the real cause would be lost from both the response and Sentry.
Testing
No behaviour change beyond the metric.
cargo test -p syncserver --lib web::payload_offloadcovers success, GCS failure, and unparseable URL.
Issue(s)
Closes STOR-653.