@@ -555,14 +555,17 @@ def cancel
555
555
if ( timeo = conninfo_hash [ :connect_timeout ] . to_i ) && timeo > 0
556
556
# Lowest timeout is 2 seconds - like in libpq
557
557
timeo = [ timeo , 2 ] . max
558
- stop_time = timeo + Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
558
+ host_count = conninfo_hash [ :host ] . to_s . count ( "," ) + 1
559
+ stop_time = timeo * host_count + Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
559
560
end
560
561
561
562
poll_status = PG ::PGRES_POLLING_WRITING
562
563
until poll_status == PG ::PGRES_POLLING_OK ||
563
564
poll_status == PG ::PGRES_POLLING_FAILED
564
565
565
- timeout = stop_time &.-( Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) )
566
+ # Set single timeout to parameter "connect_timeout" but
567
+ # don't exceed total connection time of number-of-hosts * connect_timeout.
568
+ timeout = [ timeo , stop_time - Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) ] . min if stop_time
566
569
event = if !timeout || timeout >= 0
567
570
# If the socket needs to read, wait 'til it becomes readable to poll again
568
571
case poll_status
@@ -600,7 +603,6 @@ def cancel
600
603
601
604
# Check to see if it's finished or failed yet
602
605
poll_status = send ( poll_meth )
603
- @last_status = status unless [ PG ::CONNECTION_BAD , PG ::CONNECTION_OK ] . include? ( status )
604
606
end
605
607
606
608
unless status == PG ::CONNECTION_OK
@@ -694,81 +696,47 @@ def new(*args)
694
696
errors = [ ]
695
697
if iopts [ :hostaddr ]
696
698
# hostaddr is provided -> no need to resolve hostnames
697
- ihostaddrs = iopts [ :hostaddr ] . split ( "," , -1 )
698
699
699
- ihosts = iopts [ :host ] . split ( "," , -1 ) if iopts [ :host ]
700
- raise PG ::ConnectionBad , "could not match #{ ihosts . size } host names to #{ ihostaddrs . size } hostaddr values" if ihosts && ihosts . size != ihostaddrs . size
701
-
702
- iports = iopts [ :port ] . split ( "," , -1 )
703
- iports = iports * ihostaddrs . size if iports . size == 1
704
- raise PG ::ConnectionBad , "could not match #{ iports . size } port numbers to #{ ihostaddrs . size } hosts" if iports . size != ihostaddrs . size
705
-
706
- # Try to connect to each hostaddr with separate timeout
707
- ihostaddrs . each_with_index do |ihostaddr , idx |
708
- oopts = iopts . merge ( hostaddr : ihostaddr , port : iports [ idx ] )
709
- oopts [ :host ] = ihosts [ idx ] if ihosts
710
- c = connect_internal ( oopts , errors )
711
- return c if c
712
- end
713
- elsif iopts [ :host ] && !iopts [ :host ] . empty?
714
- # Resolve DNS in Ruby to avoid blocking state while connecting, when it ...
700
+ elsif iopts [ :host ] && !iopts [ :host ] . empty? && PG . library_version >= 100000
701
+ # Resolve DNS in Ruby to avoid blocking state while connecting.
702
+ # Multiple comma-separated values are generated, if the hostname resolves to both IPv4 and IPv6 addresses.
703
+ # This requires PostgreSQL-10+, so no DNS resolving is done on earlier versions.
715
704
ihosts = iopts [ :host ] . split ( "," , -1 )
716
-
717
705
iports = iopts [ :port ] . split ( "," , -1 )
718
706
iports = iports * ihosts . size if iports . size == 1
719
707
raise PG ::ConnectionBad , "could not match #{ iports . size } port numbers to #{ ihosts . size } hosts" if iports . size != ihosts . size
720
708
721
- ihosts . each_with_index do |mhost , idx |
709
+ dests = ihosts . each_with_index . flat_map do |mhost , idx |
722
710
unless host_is_named_pipe? ( mhost )
723
- addrs = if Fiber . respond_to? ( :scheduler ) &&
711
+ if Fiber . respond_to? ( :scheduler ) &&
724
712
Fiber . scheduler &&
725
713
RUBY_VERSION < '3.1.'
726
714
727
715
# Use a second thread to avoid blocking of the scheduler.
728
716
# `TCPSocket.gethostbyname` isn't fiber aware before ruby-3.1.
729
- Thread . new { Addrinfo . getaddrinfo ( mhost , nil , nil , :STREAM ) . map ( &:ip_address ) rescue [ '' ] } . value
717
+ hostaddrs = Thread . new { Addrinfo . getaddrinfo ( mhost , nil , nil , :STREAM ) . map ( &:ip_address ) rescue [ '' ] } . value
730
718
else
731
- Addrinfo . getaddrinfo ( mhost , nil , nil , :STREAM ) . map ( &:ip_address ) rescue [ '' ]
732
- end
733
-
734
- # Try to connect to each host with separate timeout
735
- addrs . each do |addr |
736
- oopts = iopts . merge ( hostaddr : addr , host : mhost , port : iports [ idx ] )
737
- c = connect_internal ( oopts , errors )
738
- return c if c
719
+ hostaddrs = Addrinfo . getaddrinfo ( mhost , nil , nil , :STREAM ) . map ( &:ip_address ) rescue [ '' ]
739
720
end
740
721
else
741
722
# No hostname to resolve (UnixSocket)
742
- oopts = iopts . merge ( host : mhost , port : iports [ idx ] )
743
- c = connect_internal ( oopts , errors )
744
- return c if c
723
+ hostaddrs = [ nil ]
745
724
end
725
+ hostaddrs . map { |hostaddr | [ hostaddr , mhost , iports [ idx ] ] }
746
726
end
727
+ iopts . merge! (
728
+ hostaddr : dests . map { |d | d [ 0 ] } . join ( "," ) ,
729
+ host : dests . map { |d | d [ 1 ] } . join ( "," ) ,
730
+ port : dests . map { |d | d [ 2 ] } . join ( "," ) )
747
731
else
748
732
# No host given
749
- return connect_internal ( iopts )
750
733
end
751
- raise PG ::ConnectionBad , errors . join ( "\n " )
752
- end
753
-
754
- private def connect_internal ( opts , errors = nil )
755
- begin
756
- conn = self . connect_start ( opts ) or
757
- raise ( PG ::Error , "Unable to create a new connection" )
734
+ conn = self . connect_start ( iopts ) or
735
+ raise ( PG ::Error , "Unable to create a new connection" )
758
736
759
- raise PG ::ConnectionBad , conn . error_message if conn . status == PG ::CONNECTION_BAD
737
+ raise PG ::ConnectionBad , conn . error_message if conn . status == PG ::CONNECTION_BAD
760
738
761
- conn . send ( :async_connect_or_reset , :connect_poll )
762
- rescue PG ::ConnectionBad => err
763
- if errors && !( conn && [ PG ::CONNECTION_AWAITING_RESPONSE ] . include? ( conn . instance_variable_get ( :@last_status ) ) )
764
- # Seems to be no authentication error -> try next host
765
- errors << err
766
- return nil
767
- else
768
- # Probably an authentication error
769
- raise
770
- end
771
- end
739
+ conn . send ( :async_connect_or_reset , :connect_poll )
772
740
conn
773
741
end
774
742
0 commit comments