github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/pubsub/watch.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package pubsub
    19  
    20  import (
    21  	"fmt"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/hxx258456/ccgo/grpc/internal/pretty"
    26  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource"
    27  )
    28  
    29  type watchInfoState int
    30  
    31  const (
    32  	watchInfoStateStarted watchInfoState = iota
    33  	watchInfoStateRespReceived
    34  	watchInfoStateTimeout
    35  	watchInfoStateCanceled
    36  )
    37  
    38  // watchInfo holds all the information from a watch() call.
    39  type watchInfo struct {
    40  	c      *Pubsub
    41  	rType  xdsresource.ResourceType
    42  	target string
    43  
    44  	ldsCallback func(xdsresource.ListenerUpdate, error)
    45  	rdsCallback func(xdsresource.RouteConfigUpdate, error)
    46  	cdsCallback func(xdsresource.ClusterUpdate, error)
    47  	edsCallback func(xdsresource.EndpointsUpdate, error)
    48  
    49  	expiryTimer *time.Timer
    50  
    51  	// mu protects state, and c.scheduleCallback().
    52  	// - No callback should be scheduled after watchInfo is canceled.
    53  	// - No timeout error should be scheduled after watchInfo is resp received.
    54  	mu    sync.Mutex
    55  	state watchInfoState
    56  }
    57  
    58  func (wi *watchInfo) newUpdate(update interface{}) {
    59  	wi.mu.Lock()
    60  	defer wi.mu.Unlock()
    61  	if wi.state == watchInfoStateCanceled {
    62  		return
    63  	}
    64  	wi.state = watchInfoStateRespReceived
    65  	wi.expiryTimer.Stop()
    66  	wi.c.scheduleCallback(wi, update, nil)
    67  }
    68  
    69  func (wi *watchInfo) newError(err error) {
    70  	wi.mu.Lock()
    71  	defer wi.mu.Unlock()
    72  	if wi.state == watchInfoStateCanceled {
    73  		return
    74  	}
    75  	wi.state = watchInfoStateRespReceived
    76  	wi.expiryTimer.Stop()
    77  	wi.sendErrorLocked(err)
    78  }
    79  
    80  func (wi *watchInfo) resourceNotFound() {
    81  	wi.mu.Lock()
    82  	defer wi.mu.Unlock()
    83  	if wi.state == watchInfoStateCanceled {
    84  		return
    85  	}
    86  	wi.state = watchInfoStateRespReceived
    87  	wi.expiryTimer.Stop()
    88  	wi.sendErrorLocked(xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "xds: %v target %s not found in received response", wi.rType, wi.target))
    89  }
    90  
    91  func (wi *watchInfo) timeout() {
    92  	wi.mu.Lock()
    93  	defer wi.mu.Unlock()
    94  	if wi.state == watchInfoStateCanceled || wi.state == watchInfoStateRespReceived {
    95  		return
    96  	}
    97  	wi.state = watchInfoStateTimeout
    98  	wi.sendErrorLocked(fmt.Errorf("xds: %v target %s not found, watcher timeout", wi.rType, wi.target))
    99  }
   100  
   101  // Caller must hold wi.mu.
   102  func (wi *watchInfo) sendErrorLocked(err error) {
   103  	var (
   104  		u interface{}
   105  	)
   106  	switch wi.rType {
   107  	case xdsresource.ListenerResource:
   108  		u = xdsresource.ListenerUpdate{}
   109  	case xdsresource.RouteConfigResource:
   110  		u = xdsresource.RouteConfigUpdate{}
   111  	case xdsresource.ClusterResource:
   112  		u = xdsresource.ClusterUpdate{}
   113  	case xdsresource.EndpointsResource:
   114  		u = xdsresource.EndpointsUpdate{}
   115  	}
   116  	wi.c.scheduleCallback(wi, u, err)
   117  }
   118  
   119  func (wi *watchInfo) cancel() {
   120  	wi.mu.Lock()
   121  	defer wi.mu.Unlock()
   122  	if wi.state == watchInfoStateCanceled {
   123  		return
   124  	}
   125  	wi.expiryTimer.Stop()
   126  	wi.state = watchInfoStateCanceled
   127  }
   128  
   129  func (pb *Pubsub) watch(wi *watchInfo) (first bool, cancel func() bool) {
   130  	pb.mu.Lock()
   131  	defer pb.mu.Unlock()
   132  	pb.logger.Debugf("new watch for type %v, resource name %v", wi.rType, wi.target)
   133  	var (
   134  		watchers map[string]map[*watchInfo]bool
   135  		mds      map[string]xdsresource.UpdateMetadata
   136  	)
   137  	switch wi.rType {
   138  	case xdsresource.ListenerResource:
   139  		watchers = pb.ldsWatchers
   140  		mds = pb.ldsMD
   141  	case xdsresource.RouteConfigResource:
   142  		watchers = pb.rdsWatchers
   143  		mds = pb.rdsMD
   144  	case xdsresource.ClusterResource:
   145  		watchers = pb.cdsWatchers
   146  		mds = pb.cdsMD
   147  	case xdsresource.EndpointsResource:
   148  		watchers = pb.edsWatchers
   149  		mds = pb.edsMD
   150  	default:
   151  		pb.logger.Errorf("unknown watch type: %v", wi.rType)
   152  		return false, nil
   153  	}
   154  
   155  	var firstWatcher bool
   156  	resourceName := wi.target
   157  	s, ok := watchers[wi.target]
   158  	if !ok {
   159  		// If this is a new watcher, will ask lower level to send a new request
   160  		// with the resource name.
   161  		//
   162  		// If this (type+name) is already being watched, will not notify the
   163  		// underlying versioned apiClient.
   164  		pb.logger.Debugf("first watch for type %v, resource name %v, will send a new xDS request", wi.rType, wi.target)
   165  		s = make(map[*watchInfo]bool)
   166  		watchers[resourceName] = s
   167  		mds[resourceName] = xdsresource.UpdateMetadata{Status: xdsresource.ServiceStatusRequested}
   168  		firstWatcher = true
   169  	}
   170  	// No matter what, add the new watcher to the set, so it's callback will be
   171  	// call for new responses.
   172  	s[wi] = true
   173  
   174  	// If the resource is in cache, call the callback with the value.
   175  	switch wi.rType {
   176  	case xdsresource.ListenerResource:
   177  		if v, ok := pb.ldsCache[resourceName]; ok {
   178  			pb.logger.Debugf("LDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v))
   179  			wi.newUpdate(v)
   180  		}
   181  	case xdsresource.RouteConfigResource:
   182  		if v, ok := pb.rdsCache[resourceName]; ok {
   183  			pb.logger.Debugf("RDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v))
   184  			wi.newUpdate(v)
   185  		}
   186  	case xdsresource.ClusterResource:
   187  		if v, ok := pb.cdsCache[resourceName]; ok {
   188  			pb.logger.Debugf("CDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v))
   189  			wi.newUpdate(v)
   190  		}
   191  	case xdsresource.EndpointsResource:
   192  		if v, ok := pb.edsCache[resourceName]; ok {
   193  			pb.logger.Debugf("EDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v))
   194  			wi.newUpdate(v)
   195  		}
   196  	}
   197  
   198  	return firstWatcher, func() bool {
   199  		pb.logger.Debugf("watch for type %v, resource name %v canceled", wi.rType, wi.target)
   200  		wi.cancel()
   201  		pb.mu.Lock()
   202  		defer pb.mu.Unlock()
   203  		var lastWatcher bool
   204  		if s := watchers[resourceName]; s != nil {
   205  			// Remove this watcher, so it's callback will not be called in the
   206  			// future.
   207  			delete(s, wi)
   208  			if len(s) == 0 {
   209  				pb.logger.Debugf("last watch for type %v, resource name %v canceled, will send a new xDS request", wi.rType, wi.target)
   210  				// If this was the last watcher, also tell xdsv2Client to stop
   211  				// watching this resource.
   212  				delete(watchers, resourceName)
   213  				delete(mds, resourceName)
   214  				lastWatcher = true
   215  				// Remove the resource from cache. When a watch for this
   216  				// resource is added later, it will trigger a xDS request with
   217  				// resource names, and client will receive new xDS responses.
   218  				switch wi.rType {
   219  				case xdsresource.ListenerResource:
   220  					delete(pb.ldsCache, resourceName)
   221  				case xdsresource.RouteConfigResource:
   222  					delete(pb.rdsCache, resourceName)
   223  				case xdsresource.ClusterResource:
   224  					delete(pb.cdsCache, resourceName)
   225  				case xdsresource.EndpointsResource:
   226  					delete(pb.edsCache, resourceName)
   227  				}
   228  			}
   229  		}
   230  		return lastWatcher
   231  	}
   232  }