Skip to content
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

RSDK-8663: Optimize away gostream concurrency complexity for camera clients. #4363

Merged
merged 2 commits into from
Sep 18, 2024

Conversation

dgottlieb
Copy link
Member

No description provided.

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Sep 13, 2024
@hexbabe
Copy link
Member

hexbabe commented Sep 17, 2024

@dgottlieb looks like CI is failing FYI

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Sep 17, 2024
@dgottlieb
Copy link
Member Author

Thanks -- did you otherwise review this?

Copy link
Member

@hexbabe hexbabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we pinpoint the reason why two GetImage calls are sometimes made? Is it possible to prevent that instead

test.That(t, err, test.ShouldBeNil)

if _, isReadImager := cameraClient.(camera.ReadImager); isReadImager {
// Success
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious syntax

Wondering why you prefer this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How this code got this way is a bit of a long story -- it wasn't due to preference. I was trying to use the "test.ShouldImplement" assertion, that I think requires reflection and additional error handling. But ultimately failed at that. So the structure of this if-statement was already there and I just had to use a type-assertion instead and do nothing in the "then" clause.

That said, I do think writing conditionals in the affirmative (i.e: avoid double negatives) reads better. And in this case, a real writer is going to write the if-statement that way -- just with a non-empty "then".

But normally I wouldn't take that to the "extreme" of having an empty "then" clause. I just left it this way because I was fed up with writing an assertion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha the test lib use of reflection can be super funky

Got it

@@ -118,6 +118,8 @@ func getExtra(ctx context.Context) (*structpb.Struct, error) {
return ext, nil
}

// RSDK-8663: This method signature is depended on by the `camera.serviceServer` optimization that
// avoids using an image stream just to get a single image.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see since the client Read func is guaranteed to only call GetImage once in its body

Copy link
Member Author

@dgottlieb dgottlieb Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! That's exactly how this patch works. Instead of spinning up:

  • stream = camera.Stream()
  • stream.Next()
  • stream.Close

We first ask "Hey camera, do you have a "Read" method?" Great -- we'll just call that one time and avoid this stream non-sense.

@dgottlieb
Copy link
Member Author

Did we pinpoint the reason why two GetImage calls are sometimes made?

Yes. The ticket/comments try to explain what happens. But it is hard to follow how that relates to the exact code because of the gostream layers involved.

Is it possible to prevent that instead

Given this patch does prevent that with camera clients, I assume you mean in the more general case? Yes and no. Right now, the API exposed by all underlying gostream cameras are:

  • Images
  • Stream

Beyond those two, cameras may not have any* other methods[1]. So for those that do, we can try and guess/use them. But even for the Read method that camera clients have, I'm not seeing many other cameras implement that. For example, the h264 camera becomes a "sourceBasedCamera" which only has the Images/Stream APIs + the rtp passthrough apis.

Point being, avoiding "double get image" from a bigger set of cameras requires more typing across the codebase. And the camera client was 90% of our problem today.

[1] As far as I can tell -- it's not correct to blindly use [Get]Images(req)[0] as a substitute for [Get]Image, e.g:

func (s *Server) GetImage(req *ImageRequest) {
  return camera.Images(...)[0]
}

@hexbabe
Copy link
Member

hexbabe commented Sep 18, 2024

Thanks for that info

From the writeup on the ticket:

The issue is, the background thread in camera client's Stream would have already called the c.Read() again, so the second request will be invoked and almost immediately cancelled.

I guess my question was more pointed towards this. There isn't a clear path forward preventing the double call originating from this background thread is there?

@dgottlieb
Copy link
Member Author

dgottlieb commented Sep 18, 2024

I guess my question was more pointed towards this. There isn't a clear path forward preventing the double call originating from this background thread is there?

I explored an avenue where ReadImage/ReadMedia would do:

  • stream = camera.Stream()
  • image = stream.NextAndClose()

Which I think is what you're asking? Where we can "atomically" get a single image and communicate to the "producer" to not (eagerly) produce the next image.

I didn't find that to be possible without significantly mucking with the producerConsumer code. Which would then require a much more thorough proof of correctness/review.

@hexbabe
Copy link
Member

hexbabe commented Sep 18, 2024

I guess my question was more pointed towards this. There isn't a clear path forward preventing the double call originating from this background thread is there?

I explored an avenue where ReadImage/ReadMedia would do:

  • stream = camera.Stream()
  • image = stream.NextAndClose()

Which I think is what you're asking? Where we can "atomically" get a single image and communicate to the "producer" to not (eagerly) produce the next image.

I didn't find that to be possible without significantly mucking with the producerConsumer code. Which would then require a much more thorough proof of correctness/review.

Makes sense. That sounds very involved

@dgottlieb dgottlieb merged commit 7f07706 into viamrobotics:main Sep 18, 2024
19 checks passed
@dgottlieb dgottlieb deleted the RSDK-8663 branch September 18, 2024 17:46
maximpertsov pushed a commit to maximpertsov/rdk that referenced this pull request Sep 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants