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 }