@@ -220,7 +220,7 @@ def __init__(
220
220
221
221
# Ensures that the ES verification request only fires once and that
222
222
# all requests block until this request returns back.
223
- self ._verified_once = Once ()
223
+ self ._verify_elasticsearch_lock = Lock ()
224
224
225
225
def add_connection (self , host ):
226
226
"""
@@ -406,9 +406,7 @@ def perform_request(self, method, url, headers=None, params=None, body=None):
406
406
407
407
# Before we make the actual API call we verify the Elasticsearch instance.
408
408
if self ._verified_elasticsearch is None :
409
- self ._verified_once .call (
410
- self ._do_verify_elasticsearch , headers = headers , timeout = timeout
411
- )
409
+ self ._do_verify_elasticsearch (headers = headers , timeout = timeout )
412
410
413
411
# If '_verified_elasticsearch' is False we know we're not connected to Elasticsearch.
414
412
if self ._verified_elasticsearch is False :
@@ -536,63 +534,76 @@ def _do_verify_elasticsearch(self, headers, timeout):
536
534
but we're also unable to rule it out due to a permission
537
535
error we instead emit an 'ElasticsearchWarning'.
538
536
"""
539
- # Product check has already been done, no need to do again.
540
- if self . _verified_elasticsearch is not None :
541
- return
537
+ # Ensure that there's only one thread within this section
538
+ # at a time to not emit unnecessary index API calls.
539
+ with self . _verify_elasticsearch_lock :
542
540
543
- headers = {header .lower (): value for header , value in (headers or {}).items ()}
544
- # We know we definitely want JSON so request it via 'accept'
545
- headers .setdefault ("accept" , "application/json" )
541
+ # Product check has already been completed while we were
542
+ # waiting our turn, no need to do again.
543
+ if self ._verified_elasticsearch is not None :
544
+ return
546
545
547
- info_headers = {}
548
- info_response = {}
549
- error = None
546
+ headers = {
547
+ header .lower (): value for header , value in (headers or {}).items ()
548
+ }
549
+ # We know we definitely want JSON so request it via 'accept'
550
+ headers .setdefault ("accept" , "application/json" )
550
551
551
- for conn in chain (self .connection_pool .connections , self .seed_connections ):
552
- try :
553
- _ , info_headers , info_response = conn .perform_request (
554
- "GET" , "/" , headers = headers , timeout = timeout
555
- )
552
+ info_headers = {}
553
+ info_response = {}
554
+ error = None
556
555
557
- # Lowercase all the header names for consistency in accessing them.
558
- info_headers = {
559
- header .lower (): value for header , value in info_headers .items ()
560
- }
556
+ attempted_conns = []
557
+ for conn in chain (self .connection_pool .connections , self .seed_connections ):
558
+ # Only attempt once per connection max.
559
+ if conn in attempted_conns :
560
+ continue
561
+ attempted_conns .append (conn )
561
562
562
- info_response = self .deserializer .loads (
563
- info_response , mimetype = "application/json"
564
- )
565
- break
566
-
567
- # Previous versions of 7.x Elasticsearch required a specific
568
- # permission so if we receive HTTP 401/403 we should warn
569
- # instead of erroring out.
570
- except (AuthenticationException , AuthorizationException ):
571
- warnings .warn (
572
- (
573
- "The client is unable to verify that the server is "
574
- "Elasticsearch due security privileges on the server side"
575
- ),
576
- ElasticsearchWarning ,
577
- stacklevel = 5 ,
578
- )
579
- self ._verified_elasticsearch = True
580
- return
563
+ try :
564
+ _ , info_headers , info_response = conn .perform_request (
565
+ "GET" , "/" , headers = headers , timeout = timeout
566
+ )
581
567
582
- # This connection didn't work, we'll try another .
583
- except ( ConnectionError , SerializationError ) as err :
584
- if error is None :
585
- error = err
568
+ # Lowercase all the header names for consistency in accessing them .
569
+ info_headers = {
570
+ header . lower (): value for header , value in info_headers . items ()
571
+ }
586
572
587
- # If we received a connection error and weren't successful
588
- # anywhere then we reraise the more appropriate error.
589
- if error and not info_response :
590
- raise error
573
+ info_response = self . deserializer . loads (
574
+ info_response , mimetype = "application/json"
575
+ )
576
+ break
591
577
592
- # Check the information we got back from the index request.
593
- self ._verified_elasticsearch = _verify_elasticsearch (
594
- info_headers , info_response
595
- )
578
+ # Previous versions of 7.x Elasticsearch required a specific
579
+ # permission so if we receive HTTP 401/403 we should warn
580
+ # instead of erroring out.
581
+ except (AuthenticationException , AuthorizationException ):
582
+ warnings .warn (
583
+ (
584
+ "The client is unable to verify that the server is "
585
+ "Elasticsearch due security privileges on the server side"
586
+ ),
587
+ ElasticsearchWarning ,
588
+ stacklevel = 5 ,
589
+ )
590
+ self ._verified_elasticsearch = True
591
+ return
592
+
593
+ # This connection didn't work, we'll try another.
594
+ except (ConnectionError , SerializationError , TransportError ) as err :
595
+ if error is None :
596
+ error = err
597
+
598
+ # If we received a connection error and weren't successful
599
+ # anywhere then we re-raise the more appropriate error.
600
+ if error and not info_response :
601
+ raise error
602
+
603
+ # Check the information we got back from the index request.
604
+ self ._verified_elasticsearch = _verify_elasticsearch (
605
+ info_headers , info_response
606
+ )
596
607
597
608
598
609
def _verify_elasticsearch (headers , response ):
@@ -640,17 +651,3 @@ def _verify_elasticsearch(headers, response):
640
651
return False
641
652
642
653
return True
643
-
644
-
645
- class Once :
646
- """Simple class which forces a function to only execute once."""
647
-
648
- def __init__ (self ):
649
- self ._lock = Lock ()
650
- self ._called = False
651
-
652
- def call (self , func , * args , ** kwargs ):
653
- with self ._lock :
654
- if not self ._called :
655
- self ._called = True
656
- func (* args , ** kwargs )
0 commit comments