[PATCH] substitute: Reuse connections for '--query'.

  • Done
  • quality assurance status badge
Details
2 participants
  • Ludovic Courtès
  • Christopher Baines
Owner
unassigned
Submitted by
Ludovic Courtès
Severity
normal
L
L
Ludovic Courtès wrote on 19 Dec 2020 15:49
(address . guix-patches@gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
20201219144952.2725-1-ludo@gnu.org
This significantly speeds up things like substituting the closure of a
.drv. This is a followup to 5ff521452b9ec2aae9ed8e4bb7bdc250a581f203.

* guix/scripts/substitute.scm (http-multiple-get): Add #:open-connection
and #:keep-alive? and honor them.
(open-connection-for-uri/maybe): Use 'open-connection-for-uri/cached'
instead of 'guix:open-connection-for-uri'. Call 'http-multiple-get'
within 'call-with-cached-connection'.
(open-connection-for-uri/cached): Add #:timeout and #:verify-certificate?
and honor them.
(call-with-cached-connection): Add 'open-connection' parameter and
honor it.
---
guix/scripts/substitute.scm | 97 ++++++++++++++++++++++---------------
1 file changed, 59 insertions(+), 38 deletions(-)

Toggle diff (189 lines)
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index 38702d0c4b..8084c89ae5 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -514,12 +514,18 @@ return its MAX-LENGTH first elements and its tail."
(define* (http-multiple-get base-uri proc seed requests
#:key port (verify-certificate? #t)
+ (open-connection guix:open-connection-for-uri)
+ (keep-alive? #t)
(batch-size 1000))
"Send all of REQUESTS to the server at BASE-URI. Call PROC for each
response, passing it the request object, the response, a port from which to
read the response body, and the previous result, starting with SEED, à la
-'fold'. Return the final result. When PORT is specified, use it as the
-initial connection on which HTTP requests are sent."
+'fold'. Return the final result.
+
+When PORT is specified, use it as the initial connection on which HTTP
+requests are sent; otherwise call OPEN-CONNECTION to open a new connection for
+a URI. When KEEP-ALIVE? is false, close the connection port before
+returning."
(let connect ((port port)
(requests requests)
(result seed))
@@ -528,10 +534,9 @@ initial connection on which HTTP requests are sent."
;; (format (current-error-port) "connecting (~a requests left)..."
;; (length requests))
- (let ((p (or port (guix:open-connection-for-uri
- base-uri
- #:verify-certificate?
- verify-certificate?))))
+ (let ((p (or port (open-connection base-uri
+ #:verify-certificate?
+ verify-certificate?))))
;; For HTTPS, P is not a file port and does not support 'setvbuf'.
(when (file-port? p)
(setvbuf p 'block (expt 2 16)))
@@ -556,7 +561,8 @@ initial connection on which HTTP requests are sent."
(()
(match (drop requests processed)
(()
- (close-port p)
+ (unless keep-alive?
+ (close-port p))
(reverse result))
(remainder
(connect p remainder result))))
@@ -598,18 +604,18 @@ if file doesn't exist, and the narinfo otherwise."
(define* (open-connection-for-uri/maybe uri
#:key
- (verify-certificate? #f)
+ fresh?
(time %fetch-timeout))
- "Open a connection to URI and return a port to it, or, if connection failed,
-print a warning and return #f."
+ "Open a connection to URI via 'open-connection-for-uri/cached' and return a
+port to it, or, if connection failed, print a warning and return #f. Pass
+#:fresh? to 'open-connection-for-uri/cached'."
(define host
(uri-host uri))
(catch #t
(lambda ()
- (guix:open-connection-for-uri uri
- #:verify-certificate? verify-certificate?
- #:timeout time))
+ (open-connection-for-uri/cached uri #:timeout time
+ #:fresh? fresh?))
(match-lambda*
(('getaddrinfo-error error)
(unless (hash-ref %unreachable-hosts host)
@@ -683,23 +689,26 @@ print a warning and return #f."
(define (do-fetch uri)
(case (and=> uri uri-scheme)
((http https)
- (let ((requests (map (cut narinfo-request url <>) paths)))
- (match (open-connection-for-uri/maybe uri)
- (#f
- '())
- (port
- (update-progress!)
- ;; Note: Do not check HTTPS server certificates to avoid depending
- ;; on the X.509 PKI. We can do it because we authenticate
- ;; narinfos, which provides a much stronger guarantee.
- (let ((result (http-multiple-get uri
- handle-narinfo-response '()
- requests
- #:verify-certificate? #f
- #:port port)))
- (close-port port)
- (newline (current-error-port))
- result)))))
+ ;; Note: Do not check HTTPS server certificates to avoid depending
+ ;; on the X.509 PKI. We can do it because we authenticate
+ ;; narinfos, which provides a much stronger guarantee.
+ (let* ((requests (map (cut narinfo-request url <>) paths))
+ (result (call-with-cached-connection uri
+ (lambda (port)
+ (if port
+ (begin
+ (update-progress!)
+ (http-multiple-get uri
+ handle-narinfo-response '()
+ requests
+ #:open-connection
+ open-connection-for-uri/cached
+ #:verify-certificate? #f
+ #:port port))
+ '()))
+ open-connection-for-uri/maybe)))
+ (newline (current-error-port))
+ result))
((file #f)
(let* ((base (string-append (uri-path uri) "/"))
(files (map (compose (cut string-append base <> ".narinfo")
@@ -990,10 +999,14 @@ the URI, its compression method (a string), and the compressed file size."
(define open-connection-for-uri/cached
(let ((cache '()))
- (lambda* (uri #:key fresh?)
+ (lambda* (uri #:key fresh? timeout verify-certificate?)
"Return a connection for URI, possibly reusing a cached connection.
-When FRESH? is true, delete any cached connections for URI and open a new
-one. Return #f if URI's scheme is 'file' or #f."
+When FRESH? is true, delete any cached connections for URI and open a new one.
+Return #f if URI's scheme is 'file' or #f.
+
+When true, TIMEOUT is the maximum number of milliseconds to wait for
+connection establishment. When VERIFY-CERTIFICATE? is true, verify HTTPS
+server certificates."
(define host (uri-host uri))
(define scheme (uri-scheme uri))
(define key (list host scheme (uri-port uri)))
@@ -1005,7 +1018,9 @@ one. Return #f if URI's scheme is 'file' or #f."
;; CACHE, if any.
(let-values (((socket)
(guix:open-connection-for-uri
- uri #:verify-certificate? #f))
+ uri
+ #:verify-certificate? verify-certificate?
+ #:timeout timeout))
((new-cache evicted)
(at-most (- %max-cached-connections 1) cache)))
(for-each (match-lambda
@@ -1019,14 +1034,19 @@ one. Return #f if URI's scheme is 'file' or #f."
(begin
(false-if-exception (close-port socket))
(set! cache (alist-delete key cache))
- (open-connection-for-uri/cached uri))
+ (open-connection-for-uri/cached uri #:timeout timeout
+ #:verify-certificate?
+ verify-certificate?))
(begin
;; Drain input left from the previous use.
(drain-input socket)
socket))))))))
-(define (call-with-cached-connection uri proc)
- (let ((port (open-connection-for-uri/cached uri)))
+(define* (call-with-cached-connection uri proc
+ #:optional
+ (open-connection
+ open-connection-for-uri/cached))
+ (let ((port (open-connection uri)))
(catch #t
(lambda ()
(proc port))
@@ -1038,7 +1058,7 @@ one. Return #f if URI's scheme is 'file' or #f."
(if (or (and (eq? key 'system-error)
(= EPIPE (system-error-errno `(,key ,@args))))
(memq key '(bad-response bad-header bad-header-component)))
- (proc (open-connection-for-uri/cached uri #:fresh? #t))
+ (proc (open-connection uri #:fresh? #t))
(apply throw key args))))))
(define-syntax-rule (with-cached-connection uri port exp ...)
@@ -1341,6 +1361,7 @@ default value."
;;; Local Variables:
;;; eval: (put 'with-timeout 'scheme-indent-function 1)
;;; eval: (put 'with-cached-connection 'scheme-indent-function 2)
+;;; eval: (put 'call-with-cached-connection 'scheme-indent-function 1)
;;; End:
;;; substitute.scm ends here
--
2.29.2
L
L
Ludovic Courtès wrote on 23 Dec 2020 16:06
(address . 45323-done@debbugs.gnu.org)
87o8ikwoih.fsf@gnu.org
Ludovic Courtès <ludo@gnu.org> skribis:

Toggle quote (16 lines)
> This significantly speeds up things like substituting the closure of a
> .drv. This is a followup to 5ff521452b9ec2aae9ed8e4bb7bdc250a581f203.
>
> * guix/scripts/substitute.scm (http-multiple-get): Add #:open-connection
> and #:keep-alive? and honor them.
> (open-connection-for-uri/maybe): Use 'open-connection-for-uri/cached'
> instead of 'guix:open-connection-for-uri'. Call 'http-multiple-get'
> within 'call-with-cached-connection'.
> (open-connection-for-uri/cached): Add #:timeout and #:verify-certificate?
> and honor them.
> (call-with-cached-connection): Add 'open-connection' parameter and
> honor it.
> ---
> guix/scripts/substitute.scm | 97 ++++++++++++++++++++++---------------
> 1 file changed, 59 insertions(+), 38 deletions(-)

Pushed as be5a75ebb5988b87b2392e2113f6590f353dd6cd!

You can check the effect by running ‘guix build XYZ.drv’, where XYZ.drv
is not available locally yet.

Ludo’.
Closed
C
C
Christopher Baines wrote on 24 Dec 2020 12:06
Re: bug#45323: [PATCH] substitute: Reuse connections for '--query'.
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 45323@debbugs.gnu.org)
871rffbgzk.fsf@cbaines.net
Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (23 lines)
> Ludovic Courtès <ludo@gnu.org> skribis:
>
>> This significantly speeds up things like substituting the closure of a
>> .drv. This is a followup to 5ff521452b9ec2aae9ed8e4bb7bdc250a581f203.
>>
>> * guix/scripts/substitute.scm (http-multiple-get): Add #:open-connection
>> and #:keep-alive? and honor them.
>> (open-connection-for-uri/maybe): Use 'open-connection-for-uri/cached'
>> instead of 'guix:open-connection-for-uri'. Call 'http-multiple-get'
>> within 'call-with-cached-connection'.
>> (open-connection-for-uri/cached): Add #:timeout and #:verify-certificate?
>> and honor them.
>> (call-with-cached-connection): Add 'open-connection' parameter and
>> honor it.
>> ---
>> guix/scripts/substitute.scm | 97 ++++++++++++++++++++++---------------
>> 1 file changed, 59 insertions(+), 38 deletions(-)
>
> Pushed as be5a75ebb5988b87b2392e2113f6590f353dd6cd!
>
> You can check the effect by running ‘guix build XYZ.drv’, where XYZ.drv
> is not available locally yet.

Hey,

I did do some testing of this, and didn't spot any issues, but I think
it might be causing some issues when things go wrong.

The Guix Build Coordinator uses code from this script, and I'm sometimes
seeing exceptions like [1] when running with these changes. This is when
calling lookup-narinfos.

1:
#<&compound-exception components: (#<&error> #<&irritants irritants: (#<gnutls-error-enum The specified session has been invalidated for some
reason.> write_to_session_record_port)> #<&exception-with-kind-and-args kind: gnutls-error args: (#<gnutls-error-enum The specified session hasbeen invalidated for some reason.> write_to_session_record_port)>)>,

When this happens, things seem to get stuck and retrying calling
lookup-narinfos leads to the same exception. I'm guessing this might be
happening because the broken connection is being cached and reused.

Any ideas?

Thanks,

Chris
-----BEGIN PGP SIGNATURE-----

iQKlBAEBCgCPFiEEPonu50WOcg2XVOCyXiijOwuE9XcFAl/kdj9fFIAAAAAALgAo
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDNF
ODlFRUU3NDU4RTcyMEQ5NzU0RTBCMjVFMjhBMzNCMEI4NEY1NzcRHG1haWxAY2Jh
aW5lcy5uZXQACgkQXiijOwuE9XfkoxAArD6tfSwRpQ3GBdRNImgsseI1/kfjKZPb
qaJr7BHyrkS/uqbbx3nwp5mN5NfvyI13Jqx/mHy1mblm79n22d61p7YFXP+eLxEZ
utNiwdOLh8/xvNfo+YPPnap69IksGxQBm03e8XOi7tqyhMeWrkxuxwHLHBnDitXt
JTZdk88xOsaMHT0QjBExMh7/JSq3wgrEENaP+BGuiBjDI+Fg4fmRKGRO2HKKV1H2
Y+Cb5ElSoHgyx2M1yEpWLlXTdo/3NAoPJi8/Iv2MtT2Rqi81M0ZmB6dN9D3C0BoT
rSHav6RnIfXOBBNoKepf5fyFu3aTWfTtUJy3pL2WB08pOBv9hLWcALajZWYGazIZ
XmoRbvxr73sBZFpl7JovyXVovAeX09memFz5Loxb++wSZfcoWvonGStx734drkNN
C4Qd9P4cApxTzj1pyU03eLXqdvBHi7Wx3/YyZv1rad4NLeZwIOYYc1UVgcQfQ11b
LP20LqDD3O+UkTgf50Q7Fi+fw+kL1U8Ifp4H6D9PRW9IK1mU14gtbLpbDOv+FPK2
M/fJ6DtN4y7LJWBVjAwbaAjCOKRBfR5KxnuxrKoFEOFnWickKxByCB8RxoouPi11
nJTbaoqeamrWG5x3IKvGKCb1nGjaGfajRJVfoCi1ExYIms+4AIbgGvLGVaIgNlSX
coR0FNCOt7I=
=D5uU
-----END PGP SIGNATURE-----

L
L
Ludovic Courtès wrote on 27 Dec 2020 15:57
(name . Christopher Baines)(address . mail@cbaines.net)(address . 45323@debbugs.gnu.org)
87tus7thy1.fsf@gnu.org
Hi!

Christopher Baines <mail@cbaines.net> skribis:

Toggle quote (12 lines)
> The Guix Build Coordinator uses code from this script, and I'm sometimes
> seeing exceptions like [1] when running with these changes. This is when
> calling lookup-narinfos.
>
> 1:
> #<&compound-exception components: (#<&error> #<&irritants irritants: (#<gnutls-error-enum The specified session has been invalidated for some
> reason.> write_to_session_record_port)> #<&exception-with-kind-and-args kind: gnutls-error args: (#<gnutls-error-enum The specified session hasbeen invalidated for some reason.> write_to_session_record_port)>)>,
>
> When this happens, things seem to get stuck and retrying calling
> lookup-narinfos leads to the same exception. I'm guessing this might be
> happening because the broken connection is being cached and reused.

Ah, that looks like another thing that might break. Does the patch
below help?

Thanks for reporting it,
Ludo’.
Toggle diff (26 lines)
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index 8084c89ae5..e53de8c304 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -43,6 +43,7 @@
(open-connection-for-uri
. guix:open-connection-for-uri)
store-path-abbreviation byte-count->string))
+ #:autoload (gnutls) (error/invalid-session)
#:use-module (guix progress)
#:use-module ((guix build syscalls)
#:select (set-thread-name))
@@ -1054,9 +1055,12 @@ server certificates."
;; If PORT was cached and the server closed the connection in the
;; meantime, we get EPIPE. In that case, open a fresh connection and
;; retry. We might also get 'bad-response or a similar exception from
- ;; (web response) later on, once we've sent the request.
+ ;; (web response) later on, once we've sent the request, or a
+ ;; ERROR/INVALID-SESSION from GnuTLS.
(if (or (and (eq? key 'system-error)
(= EPIPE (system-error-errno `(,key ,@args))))
+ (and (eq? key 'gnutls-error)
+ (eq? (first args) error/invalid-session))
(memq key '(bad-response bad-header bad-header-component)))
(proc (open-connection uri #:fresh? #t))
(apply throw key args))))))
C
C
Christopher Baines wrote on 30 Dec 2020 23:55
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 45323@debbugs.gnu.org)
87zh1u9a4j.fsf@cbaines.net
Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (19 lines)
> Hi!
>
> Christopher Baines <mail@cbaines.net> skribis:
>
>> The Guix Build Coordinator uses code from this script, and I'm sometimes
>> seeing exceptions like [1] when running with these changes. This is when
>> calling lookup-narinfos.
>>
>> 1:
>> #<&compound-exception components: (#<&error> #<&irritants irritants: (#<gnutls-error-enum The specified session has been invalidated for some
>> reason.> write_to_session_record_port)> #<&exception-with-kind-and-args kind: gnutls-error args: (#<gnutls-error-enum The specified session hasbeen invalidated for some reason.> write_to_session_record_port)>)>,
>>
>> When this happens, things seem to get stuck and retrying calling
>> lookup-narinfos leads to the same exception. I'm guessing this might be
>> happening because the broken connection is being cached and reused.
>
> Ah, that looks like another thing that might break. Does the patch
> below help?

I've tried using it, and I haven't spotted any problems yet, so I
believe so.

Thanks,

Chris
-----BEGIN PGP SIGNATURE-----

iQKlBAEBCgCPFiEEPonu50WOcg2XVOCyXiijOwuE9XcFAl/tBXxfFIAAAAAALgAo
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDNF
ODlFRUU3NDU4RTcyMEQ5NzU0RTBCMjVFMjhBMzNCMEI4NEY1NzcRHG1haWxAY2Jh
aW5lcy5uZXQACgkQXiijOwuE9XfN5A/+L5ttW8BPV9j4RDe2b8l7BmVEv9g+HdFh
EXMEReI6tcSHIoeT1VHLjq9EGpluiCGTNavKBS4o5Nz9i7sXfXIloBCqzuXLzY0i
V1BuCfChulLmgTQSar89V4eCfk6C+EAkw/Lq5vHVmDjtQT63mUJ8fnSWNvOw59Tq
M9tGa8UPgu6h4urygVqkkjSxGihOWqvILCkcKssyhXhpSei6tPu//IzZ0A42af3+
lRTZdhlbhKBvOedCPP/P4VEGX+d4HtSacSFJ6Q/y5Quznk/oacqy7SD4ZaT9QQSC
Qx2IiTxVaF2GdvLw2+SU4qvtEjfIO1bsWfg8qtPWjYULyF02oMPLzIFOE35DaAWY
aZngSvqbpoUF9O4jne/sIpaKYapcxDo6Yw+oRpE+hO6ki7unTiSP/VRDM2fWtfBy
j18tOuhRBaP+cA9rDgK3UKOSi5RFFTAXVWd4MuGX31RIDaIBt5IPBnPaGeRjjXa3
jFVKLIJvHYc5+0UdmmX9aTGqeYkP9vpmVgmFrzWm8bQ02FmJ+IupsWiPazvs4ijt
5sp/ttdAH60CTr3ORWP36mHYzcP7HcrhqYNdEUAr7lXUWnwDS8GVd2blY2xIjllA
c3Qr0kv7/+P4oAZuqBMxtYfqkmj1gXVeuOjFVU97eqP15EQLgTXKeOZOEzv/Tbv4
kl8jNCGP6zg=
=AOHt
-----END PGP SIGNATURE-----

L
L
Ludovic Courtès wrote on 4 Jan 2021 11:55
(name . Christopher Baines)(address . mail@cbaines.net)(address . 45323@debbugs.gnu.org)
87turxvumr.fsf@gnu.org
Hi,

Christopher Baines <mail@cbaines.net> skribis:

Toggle quote (2 lines)
> Ludovic Courtès <ludo@gnu.org> writes:

[...]

Toggle quote (13 lines)
>>> #<&compound-exception components: (#<&error> #<&irritants irritants: (#<gnutls-error-enum The specified session has been invalidated for some
>>> reason.> write_to_session_record_port)> #<&exception-with-kind-and-args kind: gnutls-error args: (#<gnutls-error-enum The specified session hasbeen invalidated for some reason.> write_to_session_record_port)>)>,
>>>
>>> When this happens, things seem to get stuck and retrying calling
>>> lookup-narinfos leads to the same exception. I'm guessing this might be
>>> happening because the broken connection is being cached and reused.
>>
>> Ah, that looks like another thing that might break. Does the patch
>> below help?
>
> I've tried using it, and I haven't spotted any problems yet, so I
> believe so.

Pushed as 9158020d7853b6e7925802e0d0a082801c680e8f, thanks!

Ludo’.
?