Skip to content

Provide an internal-only API to report connectivity state changes on the ClientConn #6036

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

Closed
wants to merge 17 commits into from

Conversation

my4-dev
Copy link
Contributor

@my4-dev my4-dev commented Feb 19, 2023

RELEASE NOTES:

  • Provide an internal-only API to report connectivity state changes on the ClientConn

Motivation:

Please refer to #5818

Modifications:

  • Add internal/clientutil/connectivity_state_pubsub.go
  • Add publisher to connectivityStateManager in order to publish the connectivity changes on ClientConn to subscribers.

Result:

  • We can get lossless updates of connectivity state changes on the ClientConn and use a callback.
type ClientController struct {
    cc *ClientConn
    ...
}

type Callback struct {
    c chan connectivity.State
}

func (callback *Callback) GetStateChannel() chan connectivity.State {
    return callback.c
}

func (callback *Callback)  ClientStateChangeListenOnChannel(state connectivity.State) {
    // process...
}

clientController.cc.csMgr.publisher.Register(Callback{...})

RELEASE NOTES: none

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Feb 19, 2023

CLA Signed

The committers listed above are authorized under a signed CLA.

@zasweq zasweq self-requested a review February 21, 2023 19:50
@zasweq zasweq self-assigned this Feb 21, 2023
@easwars easwars added the Type: Feature New features or improvements in behavior label Mar 2, 2023
@easwars easwars added this to the 1.54 Release milestone Mar 2, 2023
@easwars
Copy link
Contributor

easwars commented Mar 2, 2023

@my4-dev : Thanks for taking a stab at this one. I feel that the changes in this PR don't quite add up to what I had imagined in #5818. Let me give an overview of what I had in mind.

I'm not too opposed to your package internal/clientutil, but I would prefer something like internal/connectivitystate. This package would look something like this:

package connectivitystate

// Watcher is the interface to be implemented by components which wish to receive
// connectivity state updates from the Tracker.
type Watcher interface {
  // Implementations must not block.
  OnStateChange(state connectivity.State)
}

// Tracker is equivalent to the Publisher type that you have. But it does more.
type Tracker struct {
  mu sync.Mutex // Access to the underlying fields needs to be guarded by this mutex.
  state connectivity.State // Caches the most recent state update
  watchers map[Watchers]bool // Set of registered watchers
}

func NewTracker() *Tracker { ... }

func (t *Tracker) AddWatcher(watcher Watcher) func () {
  // Adds the provided watcher to the set of watchers in Tracker.
  // Schedules a callback on the provided watcher with current state.
  // Returns a function to remove the provided watcher from the set of watchers. The caller
  // of this method is responsible for invoking this function when it no longer needs to
  // monitor the connectivity state changes on the channel.
}

func (t *Tracker) SetState(state connectivity.State) {
  // Update the cached state
  // Invoke callbacks on all registered watchers.
}

I would assume that you would also need the CallbackSerializer type defined at https://github.com/grpc/grpc-go/blob/master/xds/internal/xdsclient/callback_serializer.go. You would need to move it out of xds/internal to the top-level internal package (and update existing users).

Then, the ClientConn would need to have a field for the Tracker. This should be similar to what you currently have.

type ClientConn struct {
  ...
  connectivityStateTracker *tracker
  ...
}

The one thing I would want to add is an unexported method on the ClientConn, and a corresponding exported method internal package.

package internal

var AddConnectivityStateWatcher interface{} // The actual type for this would be `func(*grpc.ClientConn, connectivitystate.Watcher)

See examples in the internal package for how this is done for other cases to get an idea.

package grpc

func init() {
  internal.AddConnectivityStateWatcher = func(cc *grpc.ClientConn, w connectivitystate.Watcher) {
    if cc.connectivityStateTracker == nil {
      cc.connectivityStateTracker == connectivitystate.NewTracker()
    }
    cc.connectivityStateTracker.AddWatcher(w)
  }
}

We need to ensure that when there are no watchers, we don't do any wasted work.

@dfawley : What do you think about the above approach?

@my4-dev
Copy link
Contributor Author

my4-dev commented Mar 5, 2023

@easwars : Thank you for some comments! I have reflected your advice.

I couldn't understand why Testing piplelines failed.
The error logs shows that undefined: newCallbackSerializer, but I don't define such a function and couldn't find a path displayed in logs.

Could you please tell me how to solve this?

@easwars easwars self-assigned this Mar 6, 2023
@github-actions
Copy link

This PR is labeled as requiring an update from the reporter, and no update has been received after 6 days. If no update is provided in the next 7 days, this issue will be automatically closed.

@github-actions github-actions bot added the stale label Mar 14, 2023
@my4-dev
Copy link
Contributor Author

my4-dev commented Mar 14, 2023

Hi @easwars!
I have corrected based on your advice.
Please take a look at my modification.

@easwars
Copy link
Contributor

easwars commented Mar 14, 2023

The tests are failing to with the following errors:

# google.golang.org/grpc/xds/internal/xdsclient [google.golang.org/grpc/xds/internal/xdsclient.test]
Error: xds/internal/xdsclient/authority_test.go:75:23: undefined: newCallbackSerializer

@my4-dev
Copy link
Contributor Author

my4-dev commented Mar 25, 2023

@easwars : I've request PR #6153 . Please take a look at this!

@easwars
Copy link
Contributor

easwars commented Mar 27, 2023

I don't mind doing this. I will.

Thank you very much.

t.cs.Schedule(func(context.Context) {
t.mu.Lock()
defer t.mu.Unlock()
watcher.OnStateChange(t.state)
Copy link
Contributor Author

@my4-dev my4-dev Mar 28, 2023

Choose a reason for hiding this comment

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

While implementing test cases, I noticed that watcher.OnStateChange could be called with an incorrect argument because of characteristics of closure if t.state would be changed very quickly.

e.g.

  1. SetState is called with connectivity.Idle.
  2. watcher.OnStateChange is scheduled.
  3. SetState is called with connectivity.Connecting immediately after step 2.
  4. watcher.OnStateChange is scheduled.
  5. Scheduled watcher.OnStateChange is called with t.state( = connectivity.Connecting) twice incorrectly.

In order to resolve this problem, I'm going to change t.state to state.
We would need to fix watcher.OnStateChange(t.state) in AddWatcher func as well.

Do you have any other opinions?
If so, I'll adopt yours.

t.cs.Schedule(func(context.Context) {
t.mu.Lock()
defer t.mu.Unlock()
watcher.OnStateChange(state)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've fixed it from watcher.OnStateChange(t.state) to watcher.OnStateChange(state) for the same reason of SetState.

cc.csMgr.connectivityStateTracker = nil
}
cc.csMgr.mu.Unlock()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've moved this code block here, added Lock() and changed to release resources of cc.csMgr.connectivityStateTracker.
It would be better to invoke this code block before cc.csMgr.updateState(connectivity.Shutdown) on Close method because it wasn't stable whether connectivity shutdown state change would be reported to Watcher.

@easwars
Copy link
Contributor

easwars commented Mar 29, 2023

I discussed this PR with @dfawley and we are leaning towards the following approach:

  • Address the one outstanding comment in internal/grpcsync: move CallbackSerializer from xdsclient/internal to here #6153 and merge it
  • Make a separate PR with only the changes currently in internal/connectivitystate/connectivitystate.go. We also want to make this a generic pubsub type instead of a specific pubsub type only for connectivity state changes. The actual code changes to make that happen will be minimal. Instead of the state field being of type connectivity.State, it will be of type interface {}. Similar changes would be required for OnStateChange, NewTracker and SetState.
    • Since we want to make it a generic pub-sub type, we would also have to bikeshed a little on the actual type and method names.
    • Also bikeshed a little on the actual package name and package path.
  • Once we merge the generic pubsub type, we can make changes to the ClientConn.

We are already very close on most of the above, just need some minor massaging.

Please let me know what you think about this approach.

@my4-dev
Copy link
Contributor Author

my4-dev commented Mar 30, 2023

I agree with you. Let's go with this idea.
It would be better to open new Merge Request for each approachs based on this MR.

I want to go ahead the first approach.
I've replied to your comment. Please take a look at this.

@easwars
Copy link
Contributor

easwars commented Mar 30, 2023

I agree with you. Let's go with this idea. It would be better to open new Merge Request for each approachs based on this MR.

Thanks @my4-dev. I had one comment on #6153 that I forgot to hit the send button on :(
I've sent it now. Hopefully, we can merge that soon and move with a separate PR for pubsub type.

@easwars
Copy link
Contributor

easwars commented Mar 31, 2023

Now that #6153 is merged, we can move forward with the next PR where we define the generic pub-sub type. Thanks.

@my4-dev
Copy link
Contributor Author

my4-dev commented Apr 1, 2023

I've submitted the next PR #6167 .

@dfawley
Copy link
Member

dfawley commented Jul 5, 2023

IIUC this is unblocked now?

@my4-dev
Copy link
Contributor Author

my4-dev commented Jul 6, 2023

Now that #6167 is merged, we can move forward with next PR where we make changes to the ClientConn.
It would be better to make changes like this in new PR.
So this PR can be closed.

@dfawley dfawley closed this Jul 6, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Status: Requires Reporter Clarification Type: Feature New features or improvements in behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants