github.com/altipla-consulting/ravendb-go-client@v0.1.3/request_executor.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math"
     9  	"net/http"
    10  	"strings"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  )
    15  
    16  var (
    17  	// Note: unlike Java GetStatisticsOperation.GetCommand() is not thread safe
    18  	// requestExecutorFailureCheckOperation *GetStatisticsOperation = NewGetStatisticsOperation("failure=check")
    19  
    20  	// HTTPClientPostProcessor allows to tweak http client after it has been created
    21  	// this allows replacing Transport with a custom transport that does logging,
    22  	// proxying or tweaks each http request
    23  	HTTPClientPostProcessor func(*http.Client)
    24  
    25  	// if true, adds lots of logging to track bugs in request executor
    26  	DebugLogRequestExecutor bool = false
    27  	DebugTopology           bool = false
    28  )
    29  
    30  const (
    31  	goClientVersion = "4.0.0"
    32  )
    33  
    34  func redbg(format string, args ...interface{}) {
    35  	if DebugLogRequestExecutor {
    36  		fmt.Printf(format, args...)
    37  	}
    38  }
    39  
    40  func tdbg(format string, args ...interface{}) {
    41  	if DebugTopology {
    42  		fmt.Printf(format, args...)
    43  	}
    44  }
    45  
    46  // Note: for simplicity ClusterRequestExecutor logic is implemented in RequestExecutor
    47  // because Go doesn't support inheritance
    48  type ClusterRequestExecutor = RequestExecutor
    49  
    50  // RequestExecutor describes executor of HTTP requests
    51  type RequestExecutor struct {
    52  	updateDatabaseTopologySemaphore    *Semaphore
    53  	updateClientConfigurationSemaphore *Semaphore
    54  
    55  	failedNodesTimers sync.Map // *ServerNode => *NodeStatus
    56  
    57  	Certificate          *tls.Certificate
    58  	TrustStore           *x509.Certificate
    59  	databaseName         string
    60  	lastReturnedResponse atomic.Value // atomic to avoid data races
    61  
    62  	updateTopologyTimer *time.Timer
    63  	nodeSelector        atomic.Value // atomic to avoid data races
    64  
    65  	NumberOfServerRequests  atomicInteger
    66  	TopologyEtag            int64
    67  	ClientConfigurationEtag int64
    68  	conventions             *DocumentConventions
    69  
    70  	disableTopologyUpdates            bool
    71  	disableClientConfigurationUpdates bool
    72  
    73  	firstTopologyUpdateFuture *completableFuture
    74  
    75  	readBalanceBehavior ReadBalanceBehavior
    76  	// TODO: mulit-threaded access, protect
    77  	Cache                 *httpCache
    78  	httpClient            *http.Client
    79  	topologyTakenFromNode *ServerNode
    80  
    81  	lastKnownUrls []string
    82  
    83  	mu sync.Mutex
    84  
    85  	disposed int32 // atomic
    86  
    87  	// those are needed to implement ClusterRequestExecutor logic
    88  	isCluster                bool
    89  	clusterTopologySemaphore *Semaphore
    90  
    91  	/// Note: in Java this is thread local but Go doesn't have equivalent
    92  	// of thread local data
    93  	aggressiveCaching *AggressiveCacheOptions
    94  }
    95  
    96  func (re *RequestExecutor) getFailedNodeTimer(n *ServerNode) *NodeStatus {
    97  	v, ok := re.failedNodesTimers.Load(n)
    98  	if !ok {
    99  		return nil
   100  	}
   101  	return v.(*NodeStatus)
   102  }
   103  
   104  func (re *RequestExecutor) getNodeSelector() *NodeSelector {
   105  	return re.nodeSelector.Load().(*NodeSelector)
   106  }
   107  
   108  func (re *RequestExecutor) setNodeSelector(s *NodeSelector) {
   109  	re.nodeSelector.Store(s)
   110  }
   111  
   112  func (re *RequestExecutor) markDisposed() {
   113  	atomic.StoreInt32(&re.disposed, 1)
   114  }
   115  
   116  func (re *RequestExecutor) isDisposed() bool {
   117  	v := atomic.LoadInt32(&re.disposed)
   118  	return v > 0
   119  }
   120  
   121  func (re *RequestExecutor) GetTopology() *Topology {
   122  	nodeSelector := re.getNodeSelector()
   123  	if nodeSelector != nil {
   124  		return nodeSelector.getTopology()
   125  	}
   126  	return nil
   127  }
   128  
   129  // GetTopologyNodes returns a copy of topology nodes
   130  func (re *RequestExecutor) GetTopologyNodes() []*ServerNode {
   131  	t := re.GetTopology()
   132  	if t == nil || len(t.Nodes) == 0 {
   133  		return nil
   134  	}
   135  	return append([]*ServerNode{}, t.Nodes...)
   136  }
   137  
   138  // GetURL returns an URL
   139  func (re *RequestExecutor) GetURL() (string, error) {
   140  	if re.getNodeSelector() == nil {
   141  		return "", nil
   142  	}
   143  
   144  	preferredNode, err := re.getPreferredNode()
   145  	if err != nil {
   146  		return "", err
   147  	}
   148  	if preferredNode != nil {
   149  		return preferredNode.currentNode.URL, nil
   150  	}
   151  	return "", nil
   152  }
   153  
   154  func (re *RequestExecutor) GetConventions() *DocumentConventions {
   155  	return re.conventions
   156  }
   157  
   158  // NewRequestExecutor creates a new executor
   159  func NewRequestExecutor(databaseName string, certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions, initialUrls []string) *RequestExecutor {
   160  	if conventions == nil {
   161  		conventions = NewDocumentConventions()
   162  	}
   163  	redbg("NewRequestExecutor: db: %s, urls: %v, read balance: %s\n", databaseName, initialUrls, conventions.ReadBalanceBehavior)
   164  	res := &RequestExecutor{
   165  		updateDatabaseTopologySemaphore:    NewSemaphore(1),
   166  		updateClientConfigurationSemaphore: NewSemaphore(1),
   167  
   168  		Cache:               newHttpCache(conventions.getMaxHttpCacheSize()),
   169  		readBalanceBehavior: conventions.ReadBalanceBehavior,
   170  		databaseName:        databaseName,
   171  		Certificate:         certificate,
   172  		TrustStore:          trustStore,
   173  
   174  		conventions: conventions.Clone(),
   175  	}
   176  	res.lastReturnedResponse.Store(time.Now())
   177  	res.setNodeSelector(nil)
   178  	// TODO: handle an error
   179  	// TODO: java globally caches http clients
   180  	res.httpClient, _ = res.createClient()
   181  	return res
   182  }
   183  
   184  // GetHTTPClient returns http client for sending the requests
   185  func (re *RequestExecutor) GetHTTPClient() (*http.Client, error) {
   186  	if re.httpClient != nil {
   187  		return re.httpClient, nil
   188  	}
   189  	c, err := re.createClient()
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	re.httpClient = c
   194  	return re.httpClient, nil
   195  }
   196  func NewClusterRequestExecutor(certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions, initialUrls []string) *RequestExecutor {
   197  	res := NewRequestExecutor("", certificate, trustStore, conventions, initialUrls)
   198  	res.MakeCluster()
   199  
   200  	return res
   201  }
   202  
   203  // TODO: only used for http cache?
   204  //private string extractThumbprintFromCertificate(KeyStore certificate) {
   205  
   206  func RequestExecutorCreate(initialUrls []string, databaseName string, certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions) *RequestExecutor {
   207  	re := NewRequestExecutor(databaseName, certificate, trustStore, conventions, initialUrls)
   208  	re.mu.Lock()
   209  	re.firstTopologyUpdateFuture = re.firstTopologyUpdate(initialUrls)
   210  	re.mu.Unlock()
   211  	return re
   212  }
   213  
   214  func RequestExecutorCreateForSingleNodeWithConfigurationUpdates(url string, databaseName string, certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions) *RequestExecutor {
   215  	executor := RequestExecutorCreateForSingleNodeWithoutConfigurationUpdates(url, databaseName, certificate, trustStore, conventions)
   216  	executor.disableClientConfigurationUpdates = false
   217  	return executor
   218  }
   219  
   220  func RequestExecutorCreateForSingleNodeWithoutConfigurationUpdates(url string, databaseName string, certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions) *RequestExecutor {
   221  	initialUrls := requestExecutorValidateUrls([]string{url}, certificate)
   222  	executor := NewRequestExecutor(databaseName, certificate, trustStore, conventions, initialUrls)
   223  
   224  	topology := &Topology{
   225  		Etag: -1,
   226  	}
   227  
   228  	serverNode := NewServerNode()
   229  	serverNode.Database = databaseName
   230  	serverNode.URL = initialUrls[0]
   231  	// TODO: is Collections.singletonList in Java code subtly significant?
   232  	topology.Nodes = []*ServerNode{serverNode}
   233  
   234  	executor.setNodeSelector(NewNodeSelector(topology))
   235  	executor.TopologyEtag = -2
   236  	executor.disableTopologyUpdates = true
   237  	executor.disableClientConfigurationUpdates = true
   238  
   239  	return executor
   240  }
   241  
   242  func ClusterRequestExecutorCreateForSingleNode(url string, certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions) *RequestExecutor {
   243  
   244  	initialUrls := []string{url}
   245  	url = requestExecutorValidateUrls(initialUrls, certificate)[0]
   246  
   247  	if conventions == nil {
   248  		conventions = getDefaultConventions()
   249  	}
   250  	executor := NewClusterRequestExecutor(certificate, trustStore, conventions, initialUrls)
   251  	executor.MakeCluster()
   252  
   253  	serverNode := NewServerNode()
   254  	serverNode.URL = url
   255  
   256  	topology := &Topology{
   257  		Etag:  -1,
   258  		Nodes: []*ServerNode{serverNode},
   259  	}
   260  
   261  	nodeSelector := NewNodeSelector(topology)
   262  
   263  	executor.setNodeSelector(nodeSelector)
   264  	executor.TopologyEtag = -2
   265  	executor.disableClientConfigurationUpdates = true
   266  	executor.disableTopologyUpdates = true
   267  
   268  	return executor
   269  }
   270  
   271  func (re *RequestExecutor) MakeCluster() {
   272  	re.isCluster = true
   273  	re.clusterTopologySemaphore = NewSemaphore(1)
   274  }
   275  
   276  func ClusterRequestExecutorCreate(initialUrls []string, certificate *tls.Certificate, trustStore *x509.Certificate, conventions *DocumentConventions) *RequestExecutor {
   277  	if conventions == nil {
   278  		conventions = getDefaultConventions()
   279  	}
   280  	executor := NewClusterRequestExecutor(certificate, trustStore, conventions, initialUrls)
   281  	executor.MakeCluster()
   282  
   283  	executor.disableClientConfigurationUpdates = true
   284  	executor.mu.Lock()
   285  	executor.firstTopologyUpdateFuture = executor.firstTopologyUpdate(initialUrls)
   286  	executor.mu.Unlock()
   287  
   288  	return executor
   289  }
   290  
   291  func (re *RequestExecutor) clusterUpdateClientConfigurationAsync() *completableFuture {
   292  	panicIf(!re.isCluster, "clusterUpdateClientConfigurationAsync() called on non-cluster RequestExecutor")
   293  	return newCompletableFutureAlreadyCompleted(nil)
   294  }
   295  
   296  func (re *RequestExecutor) updateClientConfigurationAsync() *completableFuture {
   297  	// Note: in Java this is done via virtual functions
   298  	if re.isCluster {
   299  		return re.clusterUpdateClientConfigurationAsync()
   300  	}
   301  
   302  	if re.isDisposed() {
   303  		return newCompletableFutureAlreadyCompleted(nil)
   304  	}
   305  
   306  	future := newCompletableFuture()
   307  	f := func() {
   308  		var err error
   309  
   310  		defer func() {
   311  			if err != nil {
   312  				future.completeWithError(err)
   313  			} else {
   314  				future.complete(nil)
   315  			}
   316  		}()
   317  
   318  		re.updateClientConfigurationSemaphore.acquire()
   319  		defer re.updateClientConfigurationSemaphore.release()
   320  
   321  		oldDisableClientConfigurationUpdates := re.disableClientConfigurationUpdates
   322  		re.disableClientConfigurationUpdates = true
   323  
   324  		defer func() {
   325  			re.disableClientConfigurationUpdates = oldDisableClientConfigurationUpdates
   326  		}()
   327  
   328  		command := NewGetClientConfigurationCommand()
   329  		currentIndexAndNode, err := re.chooseNodeForRequest(command, nil)
   330  		if err != nil {
   331  			return
   332  		}
   333  		err = re.Execute(currentIndexAndNode.currentNode, currentIndexAndNode.currentIndex, command, false, nil)
   334  		if err != nil {
   335  			return
   336  		}
   337  
   338  		result := command.Result
   339  		if result == nil {
   340  			return
   341  		}
   342  
   343  		re.conventions.UpdateFrom(result.Configuration)
   344  		re.ClientConfigurationEtag = result.Etag
   345  
   346  		if re.isDisposed() {
   347  			return
   348  		}
   349  	}
   350  
   351  	go f()
   352  	return future
   353  }
   354  
   355  func (re *RequestExecutor) UpdateTopologyAsync(node *ServerNode, timeout int) chan *clusterUpdateAsyncResult {
   356  	return re.updateTopologyAsyncWithForceUpdate(node, timeout, false)
   357  }
   358  
   359  type clusterUpdateAsyncResult struct {
   360  	Ok  bool
   361  	Err error
   362  }
   363  
   364  func dbgPrintTopology(t *Topology) {
   365  	tdbg("Topology nodes: %d, etag: %d\n", len(t.Nodes), t.Etag)
   366  	for _, node := range t.Nodes {
   367  		tdbg("  tag: %s, db: %s, role: %s, url: %s\n", node.ClusterTag, node.Database, node.ServerRole, node.URL)
   368  	}
   369  }
   370  
   371  func dbgPrintClusterTopologyMap(name string, m map[string]string) {
   372  	if len(m) == 0 {
   373  		return
   374  	}
   375  	tdbg("  %s %d:\n", name, len(m))
   376  	for k, v := range m {
   377  		tdbg("    %s: %s\n", k, v)
   378  	}
   379  
   380  }
   381  
   382  func dbgPrintClusterTopology(t *ClusterTopologyResponse) {
   383  	tdbg("ClusterTopology: leader: %s, nodetag: %s\n", t.Leader, t.NodeTag)
   384  	ct := t.Topology
   385  	tdbg("  lastnodeid: %s, topologyid: %s\n", ct.LastNodeID, ct.TopologyID)
   386  	dbgPrintClusterTopologyMap("members", ct.Members)
   387  	dbgPrintClusterTopologyMap("promotables", ct.Promotables)
   388  	dbgPrintClusterTopologyMap("watchers", ct.Watchers)
   389  }
   390  
   391  func (re *RequestExecutor) clusterUpdateTopologyAsyncWithForceUpdate(node *ServerNode, timeout int, forceUpdate bool) chan *clusterUpdateAsyncResult {
   392  	panicIf(!re.isCluster, "clusterUpdateTopologyAsyncWithForceUpdate() called on non-cluster RequestExecutor")
   393  
   394  	future := make(chan *clusterUpdateAsyncResult, 1)
   395  	if re.isDisposed() {
   396  		future <- &clusterUpdateAsyncResult{Ok: false}
   397  		close(future)
   398  		return future
   399  	}
   400  
   401  	f := func() {
   402  		var err error
   403  		var res bool
   404  		defer func() {
   405  			if err != nil && !re.isDisposed() {
   406  				err = nil
   407  			}
   408  
   409  			if err != nil {
   410  				future <- &clusterUpdateAsyncResult{Err: err}
   411  			} else {
   412  				future <- &clusterUpdateAsyncResult{Ok: res}
   413  			}
   414  			close(future)
   415  			re.clusterTopologySemaphore.release()
   416  		}()
   417  
   418  		re.clusterTopologySemaphore.acquire()
   419  		if re.isDisposed() {
   420  			res = false
   421  			return
   422  		}
   423  
   424  		command := NewGetClusterTopologyCommand()
   425  		err = re.Execute(node, -1, command, false, nil)
   426  		if err != nil {
   427  			return
   428  		}
   429  		results := command.Result
   430  		dbgPrintClusterTopology(results)
   431  		members := results.Topology.Members
   432  		var nodes []*ServerNode
   433  		for key, value := range members {
   434  			serverNode := NewServerNode()
   435  			serverNode.URL = value
   436  			serverNode.ClusterTag = key
   437  			nodes = append(nodes, serverNode)
   438  		}
   439  		newTopology := &Topology{
   440  			Nodes: nodes,
   441  		}
   442  
   443  		nodeSelector := re.getNodeSelector()
   444  		if nodeSelector == nil {
   445  			nodeSelector = NewNodeSelector(newTopology)
   446  			re.setNodeSelector(nodeSelector)
   447  
   448  			if re.readBalanceBehavior == ReadBalanceBehaviorFastestNode {
   449  				nodeSelector.scheduleSpeedTest()
   450  			}
   451  		} else if nodeSelector.onUpdateTopology(newTopology, forceUpdate) {
   452  			re.disposeAllFailedNodesTimers()
   453  
   454  			if re.readBalanceBehavior == ReadBalanceBehaviorFastestNode {
   455  				nodeSelector.scheduleSpeedTest()
   456  			}
   457  		}
   458  	}
   459  
   460  	go f()
   461  	return future
   462  }
   463  
   464  func (re *RequestExecutor) updateTopologyAsyncWithForceUpdate(node *ServerNode, timeout int, forceUpdate bool) chan *clusterUpdateAsyncResult {
   465  	// Note: in Java this is done via virtual functions
   466  	if re.isCluster {
   467  		return re.clusterUpdateTopologyAsyncWithForceUpdate(node, timeout, forceUpdate)
   468  	}
   469  	future := make(chan *clusterUpdateAsyncResult, 1)
   470  	f := func() {
   471  		var err error
   472  		var res bool
   473  		defer func() {
   474  			result := &clusterUpdateAsyncResult{
   475  				Ok:  res,
   476  				Err: err,
   477  			}
   478  			future <- result
   479  			close(future)
   480  		}()
   481  
   482  		if re.isDisposed() {
   483  			res = false
   484  			return
   485  		}
   486  		re.updateDatabaseTopologySemaphore.acquire()
   487  		defer re.updateDatabaseTopologySemaphore.release()
   488  		command := NewGetDatabaseTopologyCommand()
   489  		err = re.Execute(node, 0, command, false, nil)
   490  		if err != nil {
   491  			return
   492  		}
   493  		result := command.Result
   494  		dbgPrintTopology(result)
   495  		nodeSelector := re.getNodeSelector()
   496  		if nodeSelector == nil {
   497  			nodeSelector = NewNodeSelector(result)
   498  			re.setNodeSelector(nodeSelector)
   499  			if re.readBalanceBehavior == ReadBalanceBehaviorFastestNode {
   500  				nodeSelector.scheduleSpeedTest()
   501  			}
   502  		} else if nodeSelector.onUpdateTopology(result, forceUpdate) {
   503  			re.disposeAllFailedNodesTimers()
   504  			if re.readBalanceBehavior == ReadBalanceBehaviorFastestNode {
   505  				nodeSelector.scheduleSpeedTest()
   506  			}
   507  		}
   508  		re.TopologyEtag = nodeSelector.getTopology().Etag
   509  		res = true
   510  	}
   511  
   512  	go f()
   513  	return future
   514  }
   515  
   516  func (re *RequestExecutor) disposeAllFailedNodesTimers() {
   517  	f := func(key, val interface{}) bool {
   518  		status := val.(*NodeStatus)
   519  		status.Close()
   520  		return true
   521  	}
   522  	re.failedNodesTimers.Range(f)
   523  	re.failedNodesTimers = sync.Map{}
   524  }
   525  
   526  // sessionInfo can be nil
   527  func (re *RequestExecutor) ExecuteCommand(command RavenCommand, sessionInfo *SessionInfo) error {
   528  	redbg("RequestExector.ExecuteCommand: %T\n", command)
   529  	if re.isDisposed() {
   530  		// can happen if e.g. we create BulkInsertOperation, close the store and then call Close() on BulkInsertOperation
   531  		return newIllegalStateError("RequestExecutor has been disposed")
   532  	}
   533  	topologyUpdate := re.firstTopologyUpdateFuture
   534  	isDone := topologyUpdate != nil && topologyUpdate.IsDone() && !topologyUpdate.IsCompletedExceptionally() && !topologyUpdate.isCancelled()
   535  	if isDone || re.disableTopologyUpdates {
   536  		currentIndexAndNode, err := re.chooseNodeForRequest(command, sessionInfo)
   537  		if err != nil {
   538  			return err
   539  		}
   540  		return re.Execute(currentIndexAndNode.currentNode, currentIndexAndNode.currentIndex, command, true, sessionInfo)
   541  	} else {
   542  		return re.unlikelyExecute(command, topologyUpdate, sessionInfo)
   543  	}
   544  }
   545  
   546  func (re *RequestExecutor) chooseNodeForRequest(cmd RavenCommand, sessionInfo *SessionInfo) (*CurrentIndexAndNode, error) {
   547  	if !cmd.getBase().IsReadRequest {
   548  		return re.getPreferredNode()
   549  	}
   550  
   551  	switch re.readBalanceBehavior {
   552  	case ReadBalanceBehaviorNone:
   553  		return re.getPreferredNode()
   554  	case ReadBalanceBehaviorRoundRobin:
   555  		sessionID := 0
   556  		if sessionInfo != nil {
   557  			sessionID = sessionInfo.SessionID
   558  		}
   559  		return re.getNodeBySessionID(sessionID)
   560  	case ReadBalanceBehaviorFastestNode:
   561  		return re.getFastestNode()
   562  	default:
   563  		panicIf(true, "Unknown re.ReadBalanceBehavior: '%s'", re.readBalanceBehavior)
   564  	}
   565  	return nil, nil
   566  }
   567  
   568  func (re *RequestExecutor) unlikelyExecuteInner(command RavenCommand, topologyUpdate *completableFuture, sessionInfo *SessionInfo) (*completableFuture, error) {
   569  
   570  	if topologyUpdate == nil {
   571  		re.mu.Lock()
   572  		if re.firstTopologyUpdateFuture == nil {
   573  			if len(re.lastKnownUrls) == 0 {
   574  				re.mu.Unlock()
   575  				return nil, newIllegalStateError("No known topology and no previously known one, cannot proceed, likely a bug")
   576  			}
   577  
   578  			re.firstTopologyUpdateFuture = re.firstTopologyUpdate(re.lastKnownUrls)
   579  		}
   580  		topologyUpdate = re.firstTopologyUpdateFuture
   581  		re.mu.Unlock()
   582  	}
   583  
   584  	_, err := topologyUpdate.Get()
   585  	return topologyUpdate, err
   586  }
   587  
   588  func (re *RequestExecutor) unlikelyExecute(command RavenCommand, topologyUpdate *completableFuture, sessionInfo *SessionInfo) error {
   589  	var err error
   590  	topologyUpdate, err = re.unlikelyExecuteInner(command, topologyUpdate, sessionInfo)
   591  	if err != nil {
   592  		re.mu.Lock()
   593  		if re.firstTopologyUpdateFuture == topologyUpdate {
   594  			re.firstTopologyUpdateFuture = nil // next request will raise it
   595  		}
   596  		re.mu.Unlock()
   597  		return err
   598  	}
   599  
   600  	currentIndexAndNode, err := re.chooseNodeForRequest(command, sessionInfo)
   601  	if err != nil {
   602  		return err
   603  	}
   604  	err = re.Execute(currentIndexAndNode.currentNode, currentIndexAndNode.currentIndex, command, true, sessionInfo)
   605  	return err
   606  }
   607  
   608  func (re *RequestExecutor) updateTopologyCallback() {
   609  	last := re.lastReturnedResponse.Load().(time.Time)
   610  	dur := time.Since(last)
   611  	if dur < time.Minute {
   612  		return
   613  	}
   614  
   615  	var serverNode *ServerNode
   616  
   617  	selector := re.getNodeSelector()
   618  	if selector == nil {
   619  		return
   620  	}
   621  	preferredNode, err := re.getPreferredNode()
   622  	if err != nil {
   623  		return
   624  	}
   625  	serverNode = preferredNode.currentNode
   626  
   627  	re.UpdateTopologyAsync(serverNode, 0)
   628  }
   629  
   630  type tupleStringError struct {
   631  	S   string
   632  	Err error
   633  }
   634  
   635  func (re *RequestExecutor) firstTopologyUpdate(inputUrls []string) *completableFuture {
   636  	initialUrls := requestExecutorValidateUrls(inputUrls, re.Certificate)
   637  
   638  	future := newCompletableFuture()
   639  	var list []*tupleStringError
   640  	f := func() {
   641  		var err error
   642  		defer func() {
   643  			if err != nil {
   644  				future.completeWithError(err)
   645  			} else {
   646  				future.complete(nil)
   647  			}
   648  		}()
   649  
   650  		for _, url := range initialUrls {
   651  			{
   652  				serverNode := NewServerNode()
   653  				serverNode.URL = url
   654  				serverNode.Database = re.databaseName
   655  
   656  				chRes := re.UpdateTopologyAsync(serverNode, math.MaxInt32)
   657  				res := <-chRes
   658  				err = res.Err
   659  				if err == nil {
   660  					re.initializeUpdateTopologyTimer()
   661  					re.topologyTakenFromNode = serverNode
   662  					return
   663  				}
   664  			}
   665  
   666  			if _, ok := (err).(*DatabaseDoesNotExistError); ok {
   667  				// Will happen on all node in the cluster,
   668  				// so errors immediately
   669  				re.lastKnownUrls = initialUrls
   670  				return
   671  			}
   672  
   673  			if len(initialUrls) == 0 {
   674  				re.lastKnownUrls = initialUrls
   675  				err = newIllegalStateError("Cannot get topology from server: %s", url)
   676  				return
   677  			}
   678  			list = append(list, &tupleStringError{url, err})
   679  		}
   680  		topology := &Topology{
   681  			Etag: re.TopologyEtag,
   682  		}
   683  		topologyNodes := re.GetTopologyNodes()
   684  		if len(topologyNodes) == 0 {
   685  			for _, uri := range initialUrls {
   686  				serverNode := NewServerNode()
   687  				serverNode.URL = uri
   688  				serverNode.Database = re.databaseName
   689  				serverNode.ClusterTag = "!"
   690  				topologyNodes = append(topologyNodes, serverNode)
   691  			}
   692  		}
   693  		topology.Nodes = topologyNodes
   694  		re.setNodeSelector(NewNodeSelector(topology))
   695  		if len(initialUrls) > 0 {
   696  			re.initializeUpdateTopologyTimer()
   697  			return
   698  		}
   699  		re.lastKnownUrls = initialUrls
   700  
   701  		var a []string
   702  		for _, el := range list {
   703  			first := el.S
   704  			second := el.Err
   705  			s := first + " -> " + second.Error()
   706  			a = append(a, s)
   707  		}
   708  		details := strings.Join(a, ", ")
   709  		err = re.throwError(details)
   710  	}
   711  	go f()
   712  	return future
   713  }
   714  
   715  func (re *RequestExecutor) throwError(details string) error {
   716  	err := newIllegalStateError("Failed to retrieve database topology from all known nodes \n" + details)
   717  	return err
   718  }
   719  
   720  func requestExecutorValidateUrls(initialUrls []string, certificate *tls.Certificate) []string {
   721  	// TODO: implement me
   722  	return initialUrls
   723  }
   724  
   725  func (re *RequestExecutor) initializeUpdateTopologyTimer() {
   726  	re.mu.Lock()
   727  	defer re.mu.Unlock()
   728  
   729  	if re.updateTopologyTimer != nil {
   730  		return
   731  	}
   732  	// TODO: make it into an infinite goroutine instead
   733  	f := func() {
   734  		re.updateTopologyCallback()
   735  		// Go doesn't have repeatable timer, so re-trigger ourselves
   736  		re.mu.Lock()
   737  		re.updateTopologyTimer = nil
   738  		re.mu.Unlock()
   739  		re.initializeUpdateTopologyTimer()
   740  	}
   741  	re.updateTopologyTimer = time.AfterFunc(time.Minute, f)
   742  }
   743  
   744  func isNetworkTimeoutError(err error) bool {
   745  	// TODO: implement me
   746  	// can test it by setting very low timeout in http.Client
   747  	return false
   748  }
   749  
   750  // Execute executes a command on a given node
   751  // If nodeIndex is -1, we don't know the index
   752  func (re *RequestExecutor) Execute(chosenNode *ServerNode, nodeIndex int, command RavenCommand, shouldRetry bool, sessionInfo *SessionInfo) error {
   753  	// nodeIndex -1 is equivalent to Java's null
   754  	request, err := re.createRequest(chosenNode, command)
   755  	if err != nil {
   756  		return err
   757  	}
   758  	urlRef := request.URL.String()
   759  
   760  	cachedItem, cachedChangeVector, cachedValue := re.getFromCache(command, urlRef)
   761  	defer cachedItem.close()
   762  
   763  	if cachedChangeVector != nil {
   764  		aggressiveCacheOptions := re.aggressiveCaching
   765  		if aggressiveCacheOptions != nil {
   766  			expired := cachedItem.getAge() > aggressiveCacheOptions.Duration
   767  			if !expired &&
   768  				!cachedItem.getMightHaveBeenModified() &&
   769  				command.getBase().CanCacheAggressively {
   770  				return command.setResponse(cachedValue, true)
   771  			}
   772  		}
   773  
   774  		request.Header.Set(headersIfNoneMatch, "\""+*cachedChangeVector+"\"")
   775  	}
   776  
   777  	if !re.disableClientConfigurationUpdates {
   778  		etag := `"` + i64toa(re.ClientConfigurationEtag) + `"`
   779  		request.Header.Set(headersClientConfigurationEtag, etag)
   780  	}
   781  
   782  	if !re.disableTopologyUpdates {
   783  		etag := `"` + i64toa(re.TopologyEtag) + `"`
   784  		request.Header.Set(headersTopologyEtag, etag)
   785  	}
   786  
   787  	//sp := time.Now()
   788  	var response *http.Response
   789  	re.NumberOfServerRequests.incrementAndGet()
   790  	if re.shouldExecuteOnAll(chosenNode, command) {
   791  		response, err = re.executeOnAllToFigureOutTheFastest(chosenNode, command)
   792  	} else {
   793  		response, err = command.send(re.httpClient, request)
   794  	}
   795  
   796  	if err != nil {
   797  		if !shouldRetry && isNetworkTimeoutError(err) {
   798  			return err
   799  		}
   800  		// Note: Java here re-throws if err is IOException and !shouldRetry
   801  		// but for us that propagates the wrong error to RequestExecutorTest_failsWhenServerIsOffline
   802  		urlRef = request.URL.String()
   803  		var ok bool
   804  		ok, err = re.handleServerDown(urlRef, chosenNode, nodeIndex, command, request, response, err, sessionInfo)
   805  		if err != nil {
   806  			return err
   807  		}
   808  		if !ok {
   809  			return re.throwFailedToContactAllNodes(command, request, err, nil)
   810  		}
   811  		return nil
   812  	}
   813  
   814  	command.getBase().StatusCode = response.StatusCode
   815  
   816  	refreshTopology := httpExtensionsGetBooleanHeader(response, headersRefreshTopology)
   817  	refreshClientConfiguration := httpExtensionsGetBooleanHeader(response, headersRefreshClientConfiguration)
   818  
   819  	if response.StatusCode == http.StatusNotModified {
   820  		cachedItem.notModified()
   821  
   822  		if command.getBase().ResponseType == RavenCommandResponseTypeObject {
   823  			err = command.setResponse(cachedValue, true)
   824  		}
   825  		return err
   826  	}
   827  
   828  	var ok bool
   829  	if response.StatusCode >= 400 {
   830  		ok, err = re.handleUnsuccessfulResponse(chosenNode, nodeIndex, command, request, response, urlRef, sessionInfo, shouldRetry)
   831  		if err != nil {
   832  			return err
   833  		}
   834  
   835  		if !ok {
   836  			dbMissingHeader := response.Header.Get("Database-Missing")
   837  			if dbMissingHeader != "" {
   838  				return newDatabaseDoesNotExistError(dbMissingHeader)
   839  			}
   840  
   841  			if len(command.getBase().FailedNodes) == 0 {
   842  				return newIllegalStateError("Received unsuccessful response and couldn't recover from it. Also, no record of exceptions per failed nodes. This is weird and should not happen.")
   843  			}
   844  
   845  			if len(command.getBase().FailedNodes) == 1 {
   846  				// return first error
   847  				failedNodes := command.getBase().FailedNodes
   848  				for _, err := range failedNodes {
   849  					panicIf(err == nil, "err is nil")
   850  					return err
   851  				}
   852  			}
   853  
   854  			return newAllTopologyNodesDownError("Received unsuccessful response from all servers and couldn't recover from it.")
   855  		}
   856  		return nil // we either handled this already in the unsuccessful response or we are throwing
   857  	}
   858  
   859  	var responseDispose responseDisposeHandling
   860  	responseDispose, err = ravenCommand_processResponse(command, re.Cache, response, urlRef)
   861  	re.lastReturnedResponse.Store(time.Now())
   862  	if err != nil {
   863  		return err
   864  	}
   865  
   866  	if responseDispose == responseDisposeHandlingAutomatic {
   867  		_ = response.Body.Close()
   868  	}
   869  
   870  	if refreshTopology || refreshClientConfiguration {
   871  
   872  		serverNode := NewServerNode()
   873  		serverNode.URL = chosenNode.URL
   874  		serverNode.Database = re.databaseName
   875  
   876  		var topologyTask chan *clusterUpdateAsyncResult
   877  		if refreshTopology {
   878  			topologyTask = re.UpdateTopologyAsync(serverNode, 0)
   879  		} else {
   880  			topologyTask = make(chan *clusterUpdateAsyncResult, 1)
   881  			topologyTask <- &clusterUpdateAsyncResult{Ok: false}
   882  			close(topologyTask)
   883  		}
   884  		var clientConfiguration *completableFuture
   885  		if refreshClientConfiguration {
   886  			clientConfiguration = re.updateClientConfigurationAsync()
   887  		} else {
   888  			clientConfiguration = newCompletableFutureAlreadyCompleted(nil)
   889  		}
   890  		result := <-topologyTask
   891  		err1 := result.Err
   892  		_, err2 := clientConfiguration.Get()
   893  		if err1 != nil {
   894  			return err1
   895  		}
   896  		if err2 != nil {
   897  			return err2
   898  		}
   899  	}
   900  	return nil
   901  }
   902  
   903  func (re *RequestExecutor) throwFailedToContactAllNodes(command RavenCommand, request *http.Request, e error, timeoutException error) error {
   904  	commandName := fmt.Sprintf("%T", command)
   905  	message := "Tried to send " + commandName + " request via " + request.Method + " " + request.URL.String() + " to all configured nodes in the topology, " +
   906  		"all of them seem to be down or not responding. I've tried to access the following nodes: "
   907  
   908  	var urls []string
   909  	nodeSelector := re.getNodeSelector()
   910  	if nodeSelector != nil {
   911  		for _, node := range nodeSelector.getTopology().Nodes {
   912  			url := node.URL
   913  			urls = append(urls, url)
   914  		}
   915  	}
   916  	message += strings.Join(urls, ", ")
   917  
   918  	if nodeSelector != nil && re.topologyTakenFromNode != nil {
   919  		nodes := nodeSelector.getTopology().Nodes
   920  		var a []string
   921  		for _, n := range nodes {
   922  			s := "( url: " + n.URL + ", clusterTag: " + n.ClusterTag + ", serverRole: " + n.ServerRole + ")"
   923  			a = append(a, s)
   924  		}
   925  		nodesStr := strings.Join(a, ", ")
   926  
   927  		message += "\nI was able to fetch " + re.topologyTakenFromNode.Database + " topology from " + re.topologyTakenFromNode.URL + ".\n" + "Fetched topology: " + nodesStr
   928  	}
   929  
   930  	return newAllTopologyNodesDownError("%s", message)
   931  }
   932  
   933  func (re *RequestExecutor) inSpeedTestPhase() bool {
   934  	nodeSelector := re.getNodeSelector()
   935  	return (nodeSelector != nil) && nodeSelector.inSpeedTestPhase()
   936  }
   937  
   938  func (re *RequestExecutor) shouldExecuteOnAll(chosenNode *ServerNode, command RavenCommand) bool {
   939  	nodeSelector := re.getNodeSelector()
   940  	multipleNodes := (nodeSelector != nil) && (len(nodeSelector.getTopology().Nodes) > 1)
   941  
   942  	cmd := command.getBase()
   943  	return re.readBalanceBehavior == ReadBalanceBehaviorFastestNode &&
   944  		nodeSelector != nil &&
   945  		nodeSelector.inSpeedTestPhase() &&
   946  		multipleNodes &&
   947  		cmd.IsReadRequest &&
   948  		cmd.ResponseType == RavenCommandResponseTypeObject &&
   949  		chosenNode != nil
   950  }
   951  
   952  type responseAndError struct {
   953  	response *http.Response
   954  	err      error
   955  }
   956  
   957  func (re *RequestExecutor) executeOnAllToFigureOutTheFastest(chosenNode *ServerNode, command RavenCommand) (*http.Response, error) {
   958  	// note: implementation is intentionally different than Java
   959  
   960  	var fastestWasRecorded int32 // atomic
   961  	chanPreferredResponse := make(chan *responseAndError, 1)
   962  
   963  	nPreferred := 0
   964  	nodes := re.getNodeSelector().getTopology().Nodes
   965  	for idx, node := range nodes {
   966  		re.NumberOfServerRequests.incrementAndGet()
   967  
   968  		isPreferred := node.ClusterTag == chosenNode.ClusterTag
   969  		if isPreferred {
   970  			nPreferred++
   971  			panicIf(nPreferred > 1, "nPreferred is %d, should not be > 1", nPreferred)
   972  		}
   973  
   974  		go func(nodeIndex int, node *ServerNode) {
   975  			var response *http.Response
   976  			request, err := re.createRequest(node, command)
   977  			if err == nil {
   978  				response, err = command.send(re.httpClient, request)
   979  				n := atomic.AddInt32(&fastestWasRecorded, 1)
   980  				if n == 1 {
   981  					// this is the first one, so record as fastest
   982  					re.getNodeSelector().recordFastest(nodeIndex, node)
   983  				}
   984  			}
   985  			// we return http response of the preferred node and close
   986  			// all others
   987  			if isPreferred {
   988  				chanPreferredResponse <- &responseAndError{
   989  					response: response,
   990  					err:      err,
   991  				}
   992  			} else {
   993  				if response != nil && err == nil {
   994  					_ = response.Body.Close()
   995  				}
   996  			}
   997  		}(idx, node)
   998  	}
   999  
  1000  	select {
  1001  	case ret := <-chanPreferredResponse:
  1002  		// note: can be nil if there was an error
  1003  		return ret.response, ret.err
  1004  	case <-time.After(time.Second * 15):
  1005  		return nil, fmt.Errorf("request timed out")
  1006  	}
  1007  }
  1008  
  1009  func (re *RequestExecutor) getFromCache(command RavenCommand, url string) (*releaseCacheItem, *string, []byte) {
  1010  	cmd := command.getBase()
  1011  	if cmd.CanCache && cmd.IsReadRequest && cmd.ResponseType == RavenCommandResponseTypeObject {
  1012  		return re.Cache.get(url)
  1013  	}
  1014  
  1015  	return newReleaseCacheItem(nil), nil, nil
  1016  }
  1017  
  1018  func (re *RequestExecutor) createRequest(node *ServerNode, command RavenCommand) (*http.Request, error) {
  1019  	request, err := command.createRequest(node)
  1020  	if err != nil {
  1021  		return nil, err
  1022  	}
  1023  	request.Header.Set(headersClientVersion, goClientVersion)
  1024  	return request, err
  1025  }
  1026  
  1027  func (re *RequestExecutor) handleUnsuccessfulResponse(chosenNode *ServerNode, nodeIndex int, command RavenCommand, request *http.Request, response *http.Response, url string, sessionInfo *SessionInfo, shouldRetry bool) (bool, error) {
  1028  	var err error
  1029  	switch response.StatusCode {
  1030  	case http.StatusNotFound:
  1031  		re.Cache.setNotFound(url)
  1032  		switch command.getBase().ResponseType {
  1033  		case RavenCommandResponseTypeEmpty:
  1034  			return true, nil
  1035  		case RavenCommandResponseTypeObject:
  1036  			// TODO: should I propagate the error?
  1037  			_ = command.setResponse(nil, false)
  1038  		default:
  1039  			// TODO: should I propagate the error?
  1040  			_ = command.setResponseRaw(response, nil)
  1041  		}
  1042  		return true, nil
  1043  	case http.StatusForbidden:
  1044  		err = newAuthorizationError("Forbidden access to " + chosenNode.Database + "@" + chosenNode.URL + ", " + request.Method + " " + request.URL.String())
  1045  	case http.StatusGone: // request not relevant for the chosen node - the database has been moved to a different one
  1046  		if !shouldRetry {
  1047  			return false, nil
  1048  		}
  1049  
  1050  		updateFuture := re.updateTopologyAsyncWithForceUpdate(chosenNode, int(math.MaxInt32), true)
  1051  		result := <-updateFuture
  1052  		if result.Err != nil {
  1053  			return false, result.Err
  1054  		}
  1055  
  1056  		var currentIndexAndNode *CurrentIndexAndNode
  1057  		currentIndexAndNode, err = re.chooseNodeForRequest(command, sessionInfo)
  1058  		if err != nil {
  1059  			return false, err
  1060  		}
  1061  		err = re.Execute(currentIndexAndNode.currentNode, currentIndexAndNode.currentIndex, command, false, sessionInfo)
  1062  		return false, err
  1063  	case http.StatusGatewayTimeout, http.StatusRequestTimeout,
  1064  		http.StatusBadGateway, http.StatusServiceUnavailable:
  1065  		ok, err := re.handleServerDown(url, chosenNode, nodeIndex, command, request, response, nil, sessionInfo)
  1066  		return ok, err
  1067  	case http.StatusConflict:
  1068  		err = requestExecutorHandleConflict(response)
  1069  	default:
  1070  		command.getBase().onResponseFailure(response)
  1071  		err = exceptionDispatcherThrowError(response)
  1072  	}
  1073  	return false, err
  1074  }
  1075  
  1076  func requestExecutorHandleConflict(response *http.Response) error {
  1077  	return exceptionDispatcherThrowError(response)
  1078  }
  1079  
  1080  func (re *RequestExecutor) handleServerDown(url string, chosenNode *ServerNode, nodeIndex int, command RavenCommand, request *http.Request, response *http.Response, e error, sessionInfo *SessionInfo) (bool, error) {
  1081  	if command.getBase().FailedNodes == nil {
  1082  		command.getBase().FailedNodes = map[*ServerNode]error{}
  1083  	}
  1084  
  1085  	re.addFailedResponseToCommand(chosenNode, command, request, response, e)
  1086  
  1087  	if nodeIndex < 0 {
  1088  		// We executed request over a node not in the topology. This means no failover...
  1089  		return false, nil
  1090  	}
  1091  
  1092  	re.spawnHealthChecks(chosenNode, nodeIndex)
  1093  
  1094  	nodeSelector := re.getNodeSelector()
  1095  	if nodeSelector == nil {
  1096  		return false, nil
  1097  	}
  1098  
  1099  	nodeSelector.onFailedRequest(nodeIndex)
  1100  
  1101  	currentIndexAndNode, err := re.getPreferredNode()
  1102  	if err != nil {
  1103  		return false, err
  1104  	}
  1105  
  1106  	if _, ok := command.getBase().FailedNodes[currentIndexAndNode.currentNode]; ok {
  1107  		//we tried all the nodes...nothing left to do
  1108  		return false, nil
  1109  	}
  1110  
  1111  	err = re.Execute(currentIndexAndNode.currentNode, currentIndexAndNode.currentIndex, command, false, sessionInfo)
  1112  	if err != nil {
  1113  		return false, err
  1114  	}
  1115  	return true, nil
  1116  }
  1117  
  1118  func (re *RequestExecutor) spawnHealthChecks(chosenNode *ServerNode, nodeIndex int) {
  1119  	nodeStatus := NewNodeStatus(re, nodeIndex, chosenNode)
  1120  
  1121  	_, loaded := re.failedNodesTimers.LoadOrStore(chosenNode, nodeStatus)
  1122  	if !loaded {
  1123  		nodeStatus.startTimer()
  1124  	}
  1125  }
  1126  
  1127  func (re *RequestExecutor) checkNodeStatusCallback(nodeStatus *NodeStatus) {
  1128  	nodesCopy := re.GetTopologyNodes()
  1129  
  1130  	idx := nodeStatus.nodeIndex
  1131  	// TODO: idx < 0 probably shouldn't happen but it's the only cause of
  1132  	// https://travis-ci.org/kjk/ravendb-go-client/builds/404760557
  1133  	// that I can think of
  1134  	if idx < 0 || idx >= len(nodesCopy) {
  1135  		return // topology index changed / removed
  1136  	}
  1137  
  1138  	serverNode := nodesCopy[idx]
  1139  	if serverNode != nodeStatus.node {
  1140  		return // topology changed, nothing to check
  1141  	}
  1142  
  1143  	err := re.performHealthCheck(serverNode, idx)
  1144  	if err != nil {
  1145  		// TODO: logging
  1146  		status := re.getFailedNodeTimer(nodeStatus.node)
  1147  		if status != nil {
  1148  			status.updateTimer()
  1149  		}
  1150  
  1151  		return // will wait for the next timer call
  1152  	}
  1153  
  1154  	status := re.getFailedNodeTimer(nodeStatus.node)
  1155  	if status != nil {
  1156  		re.failedNodesTimers.Delete(nodeStatus.node)
  1157  		status.Close()
  1158  	}
  1159  
  1160  	nodeSelector := re.getNodeSelector()
  1161  	if nodeSelector != nil {
  1162  		nodeSelector.restoreNodeIndex(idx)
  1163  	}
  1164  }
  1165  
  1166  func (re *RequestExecutor) clusterPerformHealthCheck(serverNode *ServerNode, nodeIndex int) error {
  1167  	panicIf(!re.isCluster, "clusterPerformHealthCheck() called on non-cluster RequestExector")
  1168  	command := NewGetTcpInfoCommand("health-check", "")
  1169  	return re.Execute(serverNode, nodeIndex, command, false, nil)
  1170  }
  1171  
  1172  func (re *RequestExecutor) performHealthCheck(serverNode *ServerNode, nodeIndex int) error {
  1173  	// Note: in Java this is done via virtual functions
  1174  	if re.isCluster {
  1175  		return re.clusterPerformHealthCheck(serverNode, nodeIndex)
  1176  	}
  1177  	// Note: not reusing global singleton because in Go GetCommand() is not thread-safe
  1178  	op := NewGetStatisticsOperation("failure=check")
  1179  	command, err := op.GetCommand(re.conventions)
  1180  	if err != nil {
  1181  		return err
  1182  	}
  1183  	return re.Execute(serverNode, nodeIndex, command, false, nil)
  1184  }
  1185  
  1186  // note: static
  1187  func (re *RequestExecutor) addFailedResponseToCommand(chosenNode *ServerNode, command RavenCommand, request *http.Request, response *http.Response, e error) {
  1188  	failedNodes := command.getBase().FailedNodes
  1189  
  1190  	if response != nil && response.Body != nil {
  1191  		var schema exceptionSchema
  1192  		responseJson, err := ioutil.ReadAll(response.Body)
  1193  		if err == nil {
  1194  			err = jsonUnmarshal(responseJson, &schema)
  1195  		}
  1196  		if err == nil {
  1197  			readException := exceptionDispatcherGetFromSchema(&schema, response.StatusCode, e)
  1198  			failedNodes[chosenNode] = readException
  1199  		} else {
  1200  			exceptionSchema := &exceptionSchema{
  1201  				URL:     request.URL.String(),
  1202  				Type:    "Unparsable Server Response",
  1203  				Message: "Get unrecognized response from the server",
  1204  				Error:   string(responseJson),
  1205  			}
  1206  			exceptionToUse := exceptionDispatcherGetFromSchema(exceptionSchema, response.StatusCode, e)
  1207  
  1208  			failedNodes[chosenNode] = exceptionToUse
  1209  		}
  1210  	}
  1211  
  1212  	// this would be connections that didn't have response, such as "couldn't connect to remote server"
  1213  
  1214  	if e == nil {
  1215  		e = newRavenError("")
  1216  	}
  1217  
  1218  	exceptionSchema := &exceptionSchema{
  1219  		URL:     request.URL.String(),
  1220  		Type:    fmt.Sprintf("%T", e),
  1221  		Message: e.Error(),
  1222  		Error:   e.Error(),
  1223  	}
  1224  
  1225  	exceptionToUse := exceptionDispatcherGetFromSchema(exceptionSchema, http.StatusInternalServerError, e)
  1226  	failedNodes[chosenNode] = exceptionToUse
  1227  }
  1228  
  1229  // Close should be called when deleting executor
  1230  func (re *RequestExecutor) Close() {
  1231  	if re.isDisposed() {
  1232  		return
  1233  	}
  1234  
  1235  	if re.isCluster {
  1236  		// make sure that a potentially pending UpdateTopologyAsync() has
  1237  		// finished
  1238  		re.clusterTopologySemaphore.acquire()
  1239  	}
  1240  
  1241  	re.markDisposed()
  1242  	re.Cache.close()
  1243  
  1244  	re.mu.Lock()
  1245  	defer re.mu.Unlock()
  1246  
  1247  	if re.updateTopologyTimer != nil {
  1248  		re.updateTopologyTimer.Stop()
  1249  		re.updateTopologyTimer = nil
  1250  	}
  1251  	re.disposeAllFailedNodesTimers()
  1252  }
  1253  
  1254  // TODO: create a different client if settings like compression
  1255  // or certificate differ
  1256  func (re *RequestExecutor) createClient() (*http.Client, error) {
  1257  	client := &http.Client{
  1258  		Timeout:   time.Second * 30,
  1259  		Transport: http.DefaultTransport,
  1260  	}
  1261  	if re.Certificate != nil || re.TrustStore != nil {
  1262  		tlsConfig, err := newTLSConfig(re.Certificate, re.TrustStore)
  1263  		if err != nil {
  1264  			return nil, err
  1265  		}
  1266  		client.Transport = &http.Transport{
  1267  			TLSClientConfig: tlsConfig,
  1268  		}
  1269  	}
  1270  	if HTTPClientPostProcessor != nil {
  1271  		HTTPClientPostProcessor(client)
  1272  	}
  1273  	return client, nil
  1274  }
  1275  
  1276  func (re *RequestExecutor) getPreferredNode() (*CurrentIndexAndNode, error) {
  1277  	ns, err := re.ensureNodeSelector()
  1278  	if err != nil {
  1279  		return nil, err
  1280  	}
  1281  
  1282  	return ns.getPreferredNode()
  1283  }
  1284  
  1285  func (re *RequestExecutor) getNodeBySessionID(sessionID int) (*CurrentIndexAndNode, error) {
  1286  	ns, err := re.ensureNodeSelector()
  1287  	if err != nil {
  1288  		return nil, err
  1289  	}
  1290  
  1291  	return ns.getNodeBySessionID(sessionID)
  1292  }
  1293  
  1294  func (re *RequestExecutor) getFastestNode() (*CurrentIndexAndNode, error) {
  1295  	ns, err := re.ensureNodeSelector()
  1296  	if err != nil {
  1297  		return nil, err
  1298  	}
  1299  
  1300  	return ns.getFastestNode()
  1301  }
  1302  
  1303  func (re *RequestExecutor) ensureNodeSelector() (*NodeSelector, error) {
  1304  	re.mu.Lock()
  1305  	firstTopologyUpdate := re.firstTopologyUpdateFuture
  1306  	re.mu.Unlock()
  1307  
  1308  	if firstTopologyUpdate != nil && (!firstTopologyUpdate.IsDone() || firstTopologyUpdate.IsCompletedExceptionally()) {
  1309  		_, err := firstTopologyUpdate.Get()
  1310  		if err != nil {
  1311  			return nil, err
  1312  		}
  1313  	}
  1314  
  1315  	nodeSelector := re.getNodeSelector()
  1316  	if nodeSelector == nil {
  1317  		topology := &Topology{
  1318  			Nodes: re.GetTopologyNodes(),
  1319  			Etag:  re.TopologyEtag,
  1320  		}
  1321  
  1322  		nodeSelector = NewNodeSelector(topology)
  1323  		re.setNodeSelector(nodeSelector)
  1324  	}
  1325  	return nodeSelector, nil
  1326  }
  1327  
  1328  // NodeStatus represents status of server node
  1329  type NodeStatus struct {
  1330  	timerPeriod     time.Duration
  1331  	requestExecutor *RequestExecutor
  1332  	nodeIndex       int
  1333  	node            *ServerNode
  1334  	timer           *time.Timer
  1335  }
  1336  
  1337  func NewNodeStatus(requestExecutor *RequestExecutor, nodeIndex int, node *ServerNode) *NodeStatus {
  1338  	return &NodeStatus{
  1339  		requestExecutor: requestExecutor,
  1340  		nodeIndex:       nodeIndex,
  1341  		node:            node,
  1342  		timerPeriod:     time.Millisecond * 100,
  1343  	}
  1344  }
  1345  
  1346  func (s *NodeStatus) nextTimerPeriod() time.Duration {
  1347  	if s.timerPeriod > time.Second*5 {
  1348  		return time.Second * 5
  1349  	}
  1350  	s.timerPeriod = s.timerPeriod + (time.Millisecond * 100)
  1351  	return s.timerPeriod
  1352  }
  1353  
  1354  func (s *NodeStatus) startTimer() {
  1355  	f := func() {
  1356  		s.timerCallback()
  1357  	}
  1358  	s.timer = time.AfterFunc(s.timerPeriod, f)
  1359  }
  1360  
  1361  func (s *NodeStatus) updateTimer() {
  1362  	// TODO: not sure if Reset
  1363  	s.timer.Reset(s.nextTimerPeriod())
  1364  }
  1365  
  1366  func (s *NodeStatus) timerCallback() {
  1367  	if !s.requestExecutor.isDisposed() {
  1368  		s.requestExecutor.checkNodeStatusCallback(s)
  1369  	}
  1370  }
  1371  
  1372  func (s *NodeStatus) Close() {
  1373  	if s.timer != nil {
  1374  		s.timer.Stop()
  1375  		s.timer = nil
  1376  	}
  1377  }