-
Notifications
You must be signed in to change notification settings - Fork 604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More iobuf cross-shard checks, better doc #23263
Closed
travisdowns
wants to merge
5
commits into
redpanda-data:dev
from
travisdowns:td-CORE-7061-more-checks-in-iobuf
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
037a64f
iobuf: more doc around sharing constraints
travisdowns e6531cf
iobuf: move function pre-checks into method
travisdowns ccb96c9
iobuf: add checks to more methods
travisdowns c820224
WIP: reenable backtrace decoding
travisdowns 13d4580
WIP: decode on startup failures
travisdowns File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem like a safe interface to me?
It means the
submit_to
caller always has to wait until thesubmit_to
future resolves to do anything with its iobuf and hope thesubmit_to
side is not keeping the original iobuf around somehow? It seems as unsafe to me as the more idiomatic way of just doing copy/share first and then moving the result to the other shard. It further pessimizes the cases where no copy is needed and the iobuf is simply EOL on the source shard.I think the oncore stuff is just broken and doesn't really work as is. I tried to extend it in the past as well.
This really needs language support, maybe there is some thread_local hackery that might work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good feedback, thanks.
Right, though waiting until the future resolves is almost always the existing use case for submit_to and related calls?
That said, I wrote this as a codification of the oncore existing restrictions of the class, and the existing behavior of the share/copy/move ctor methods: the thing that creates the new iobuf (here,
copy()
needs to happen on the target shard since it grabs its origin shard ID implicitly from the current shard.As far as I can tell, iobuf does not currently allow any reasonable way to move an iobuf from one shard to another: the origin ID is preserved on move, so you can never get a moved to iobuf on another shard with the right origin ID. It may only happen to work in some cases because the verify check wasn't uniformly added everywhere so if you avoid the methods where it does exist you at least won't get an assert (but you may crash).
I don't think it's a slam dunk though that
copy()
-on-source is definitely better at least if we have any concept of origin shard: consider the case where you do aninvoke_on
(all shards), you really want to create N copies (at east with the current restriction), one for each shard. In that case copy() on the target just works: makes the right number of copies of the iobuf with right shard ID.Yes, but as above this is "already the case" and don't think there's any way to move safely currently.
Well the restriction is "too strict", but we already have it so I'm trying to lean on that for safety at the cost of re-enforcing the existing pessimization. So it is at least partly workable in the sense that it has been like this since iobuf introduction.
That said, totally open to better approaches here too, either for the doc or the implementation. Just relying on 100% adherence to complex and mostly undocumented lifetime runs isn't going to cut it, I think: especially because the one of the main failure mode occurs due to a non-atomic increment race which is going to be very hard to pick up with testing and likely to slip through to production.
Ah, I wish I had seen that. Yeah it's the same vein. There's also more discussion in https://redpandadata.atlassian.net/browse/CORE-7061 if you hadn't seen it.
Sure, we should be open to anything here. Maybe like @ballard26 said it's easier just to make the refcounting atomic, which would make the semantics more sane and hence easier to document :). That does look like a potentially invasive change performance-wise though since I think it means temporary_buffer needs use atomic refcounting everywhere in seastar (i.e., not opt-in e.g., via template parameter) since we buffers created and consumed in seastar to be like that from the start: plus the work of combing through all the
deleter
implementations to see if they are truly compatible with cross-shard use beyond the refcounting, since they can do arbitrary things at deletion time.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right yes, I guess I should have clarified that I was thinking more about the release build usecase or with the oncore check removed as I think it doesn't make much sense in its current form as described otherwise. I do very much suspect it wasn't added to all methods because it would break some common current usage patterns that are fine from a thread safety perspective.
In that example, is a copy even needed at all? If it's safe to copy/read from N other threads in parallel then it would also be safe to just read directly without copying for the duration of the
invoke_on_all
call? It would require const == threadsafe, not sure whether that is given at least today.I do think that is the best way forward. Back when I was looking into this because of the same bug/misuse in a different part of the code this was my conclusion as well. Note I think this could be a performance gain as it would mean that we can drop the extra copies on the produce and fetch path which are likely a lot more expensive.
For the checks, I am wondering whether we can add a method along the signature of:
That invalidates the current object and creates a new one with the source shard id adapted. Then we could possibly add the checks in more places like
share
(outside of the constructors which are probably still needed implicitly as seastar might move the lambda under the hood). Haven't really thought this through.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But no,
move_to_shard
is never really safe because at least in its current form the iobuf doesn't know whether it uniquely owns the temporary_buffer (in #13447 (comment) the issue was definitely that we could have already gotten ashared
buffer out ofss::input_stream::read_up_to
).So I guess this really already pessimizes this case:
as it can never be correct unfortunately. And in that sense
share
with oncore check failing anywhere is a bug. As are the failures in this PR? Well maybe not if you can guarantee that thetemporary_buf
is notshare
d possibly because you constructed it yourself somewhere.So possibly
copy_to_shard(shard_id target_shard_id)
might be a good interface but in practice just doing thecopy
in thesubmit_to
callback is just easier then.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well I feel like we shouldn't do be doing anything in the release build that would assert in the debug build, right? Both "in principle" but also actually it should be unlikely since we run (some subset of) our tests in debug and those don't assert. So I would say that the methods that have the oncore check are off-limits for cross-origin calls regardless of the build mode.
That aside, I basically agree with you that the oncore check seems like it's not really doing the right thing or at least pessimizes things unnecessarily. So I'll consider this on hold for now.
Edit: To add this was responding to your comment 2 up, I hadn't see the one immediately above so I'll have to consider that further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Though some correct cases can be detected, conservatively, though it would require more tracking. E.g., every operation either potentially causes shared buffers to be added to the iobuf, or not. E.g., the constructors that just pass char*/size which is copied into the iobuf don't result in sharing, and something like the move-ctor should inherit the sharing state.
clear()
removes all sharing, etc. So you track this boolean state "not shared vs maybe shared" and only assert if it's in the maybe shared state or some other kind of logic. E.g., moving across shards is safe for not shared stuff.I did look at two cases and they both looked safe in the sense above: on one shard an iobuf is created "unshared" (e.g., as the result of parsing some json), then it is moved to another shard (produce flow in this case).