Add an env var to allow disabling debug backtrace recording in the DB pool claim code - #11232
Add an env var to allow disabling debug backtrace recording in the DB pool claim code#11232nickelization wants to merge 10 commits into
Conversation
I ran into some issues running simulated Omicron on macOS because the cost of generating a backtrace was high for the debug build I was using; the whole startup process slowed down enough that we hit the 10 minute timeout and failed on the `start_nexus_external` step. I didn't manage to get numbers on exactly how slow each individual backtrace was, but sampling the call stacks showed lots of threads stuck in this function. Since the docs say this can be expensive in certain circumstances (and especially since backtrace generation is guarded by a global mutex) it's not entirely surprising that this could clog things up. My understanding is this has been measured on Illumos and found to be cheap there, so I don't expect this is something that would manifest in production, but a macOS debug build obviously has plenty of differences from an Illumos release build, so it's not super surprising on its own that we might stumble across a slow path somewhere in there. The odd thing is that although I was able to fix it by locally disabling the call to `Backtrace::force_capture`, I wasn't able to reproduce the problem anymore after I reenabled it. I'm not sure what could have possibly changed, though I did notice that the build size dropped from 1.9GB to 1.2GB, so something weird is definitely going on with my dev setup and I can't for the life of me figure out what happened. All that said...@davepacheco mentioned it might be reasonable to have a switch for this code anyway, and since I already wrote the flag I figured I'd whip up a PR for this while it's fresh on my mind.
davepacheco
left a comment
There was a problem hiding this comment.
Sorry about the trouble. I should have heeded the warning about this being expensive on some platforms!
I do think it's reasonable to have a switch to turn this off. I don't love sticking a getenv call right in this hot path. Thinking out loud: in an ideal world it could be an argument to omicron-dev, though that wouldn't work for the test suite. What about having the ControlPlaneTestContext setup (which is used for both omicron-dev run-all and all the tests) check for an env var like this and plumb some configuration through accordingly? It would probably be an annoying amount of plumbing, though it would ensure that this couldn't be set outside of those contexts. Alternatively, the Pool could do this at construction time, but it doesn't seem right for some arbitrary code like this to look at the environment instead of passing configuration in.
I'd also suggest checking the specific value for the variable (like 1). (I hate having to unset a variable rather than just setting it to 0 to turn it off. It also makes it harder to programmatically clear.)
|
Ah no worries! Seems to be some kind of an edge case on top of the already-edge-case of running on macOS, and I probably would've done the same. Easy change either way. I guess I hadn't thought of I thought about making it check for a specific value, particularly since I tend to dislike "negative"/"inverse" config flags like this where you enable the flag to disable a feature...but then I worry about it feeling arbitrary that you have to know the magic value to set it to (i.e. |
Note this is hardcoded to `true` right now, but will soon be plumbed through to use the new `Nexus` flag added in previous commits.
This actually shouldn't be needed since we create the DB pool from `ServerContext::new`, where we still have access to our `NexusConfig` value. This reverts commit 7220160.
|
@davepacheco Well, not sure if this is exactly what you pictured - I didn't see an obvious way to plumb it through |
Looks like this got missed on my local machine since it's gated to only compile on Illumos.
|
Alright, sorry in advance! I realize this is kind of complex and maybe not the best documented. Thinking out loud:
From first principles: it seems confusing to me to add this to the configuration file and set it in the in-memory config (after loading it from disk) based on an environment variable because the consumers that want to set this (a human running Stepping back further: I think something else you could do is not have this in the config at all, but keep it as one of the few other parameters that's passed around separately from the Nexus config. (Philosophically, this would be saying: this isn't a runtime property of Nexus, it's something about the environment that consumers of Nexus get to decide.) In that case, you could have a CLI argument to To summarize, I see 3 options here:
There are really two separable things here:
For example, there's a variant of what you have here where you still use the environment variable in the same spot you're checking it now, but instead of munging config, it affects the argument that gets passed to Sorry again -- I realize all of this is way more involved than what we were trying to do here. I'm just trying to avoid adding more ad hoc complexity to an already complex set of code paths. Hopefully this doesn't feel like a total waste of time? |
|
@davepacheco not a waste of time at all, thanks so much for taking the time to write all that! Even if it seems a little silly to go this deep on a tiny test flag that I meant to add in passing, this kind of nuts and bolts stuff is helpful for me in getting to know Omicron better, and I definitely don't want to introduce any more complexity than necessary either. I hadn't actually learned much about the toml config files yet. If it's common for folks to edit the local config anyway, then simply adding this as a new parameter and relying on local config edits as a way to set this seems like a much simpler and cleaner solution. For context, I previously wasn't considering the option of editing local config files, and I had actually applied But anyway, yes, I think we can make this simpler by adding the field without the Anyway, thanks again for the detailed review! Should have a new version pushed up later today. |
davepacheco
left a comment
There was a problem hiding this comment.
Ugh, sorry, I had these comments queued up with my other one but used the wrong GitHub button and they didn't get posted. Sorry.
| /// Since we don't expect to encounter this in production, it can only currently be disabled for | ||
| /// debug and testing use cases. For this reason, we skip it when serializing and deseralizing | ||
| /// and default it to `true`. | ||
| #[serde(skip, default = "default_record_db_claim_backtraces")] |
There was a problem hiding this comment.
We've been a little inconsistent on whether to provide defaults here, but if we're going to keep this in the config file, I would consider updating all the files in this repo (you could probably find them by searching for one of these other properties). Some of these (like nexus/examples/config.toml) include comments about stuff like this explaining why you'd change these and showing how you'd do that.
There was a problem hiding this comment.
Yeah, good call, I can update existing config files to add this.
| interface: Some("opte0".to_string()), | ||
| treat_loopback_as_external: TreatLoopbackAsExternal::YesForTestPurposesOnly, | ||
| }, | ||
| // Just a debug/test flag, and set via an env var, so no need to check this: |
There was a problem hiding this comment.
I'm not sure why we wouldn't test this. I think you do want to add it to the toml input above and make sure it has the right value here, right?
There was a problem hiding this comment.
Right, so the issue before was that it deliberately wasn't being set via the config file (via serde(skip, ...)), and I didn't want to hardcode this to check for true or false since then the test would fail if the env var changed it. Since we've now decided to actually make it a normal entry in the config file though, I agree, this should be changed to actually include it in the input toml and test it that way.
| pub fn new( | ||
| log: &Logger, | ||
| resolver: &QorbResolver, | ||
| record_db_claim_backtraces: bool, |
There was a problem hiding this comment.
Could you make this an enum (see this section of RFD 643))?
Same in the other constructor. I think the config file could stay a boolean.
There was a problem hiding this comment.
Definitely. I almost used that technique in the initial implementation, but thought others might see it as overkill (I've seen folks have mixed opinions about this kind of thing in past jobs). I didn't know about RFD 643 though, and I'm happy to do it that way 👍
See latest comments on the PR for the reasoning behind this change.
|
@davepacheco sorry for the prolonged back and forth, but I do have one more question: do you know what the story is behind this attribute line that's present on various fields of I understand this keeps the field out of the generated JSON schema, but I'm having trouble pinning down whether/why we would need to do this in this particular case (rather than just updating the schema to include the new field). It seems like other more recently added fields have copied this attribute, so I'm guessing maybe I should too (and so I did that in my latest commit) but I don't really want to just blindly copy that without understanding the bigger picture here. I did trace this stuff back to originating with #3715, but didn't find enough context there to answer this. I imagine it may have something to do with maintaining compatibility across versions, but we don't use the |
I ran into some issues running simulated Omicron on macOS because the cost of generating a backtrace was high for the debug build I was using; the whole startup process slowed down enough that we hit the 10 minute timeout and failed on the
start_nexus_externalstep.I didn't manage to get numbers on exactly how slow each individual backtrace was, but sampling the call stacks with
sampleshowed lots of instances of threads stuck in this function. Since the docs say this can be expensive in certain circumstances (and especially since backtrace generation is guarded by a global mutex) it's not entirely surprising that this could clog things up, even though we haven't run into this before on other platforms. My understanding is this has been measured on Illumos and found to be cheap there, so I don't expect this is something that would manifest in production, but a macOS debug build obviously has plenty of differences from an Illumos release build, so it makes sense to me that I could've stumbled across a slow path somewhere in there.The odd thing is that although I was able to fix it by locally disabling the call to
Backtrace::force_capture, I wasn't able to reproduce the problem anymore after I re-enabled it. I'm not sure what could have possibly changed to make this slow path suddenly go fast on the same code, though I did notice that the build size dropped from 1.9GB to 1.2GB, so something weird is definitely going on with my dev setup and I can't for the life of me figure out what happened.All that said...@davepacheco mentioned it might be reasonable to have a switch for this code anyway, and since I already wrote the code to add a flag I figured I'd whip up a PR for this while it's fresh on my mind.