summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2021-05-11 15:01:00 +0200
committerLudovic Courtès <ludo@gnu.org>2021-05-16 23:28:11 +0200
commit938ffcbb0589adc07dc12c79eda3e1e2bb9e7cf8 (patch)
tree9e997f834176b4e438aa1a007d44532af59b1388
parentdc3504913de4a2c549482001f7087362f5400f29 (diff)
publish: Add '--negative-ttl'.
* guix/scripts/publish.scm (show-help, %options): Add '--negative-ttl'. (render-narinfo, render-narinfo/cached, make-request-handler): Add #:negative-ttl and honor it. (run-publish-server): Add #:narinfo-negative-ttl and honor it. (guix-publish): Honor '--negative-ttl'. * tests/publish.scm ("negative TTL", "no negative TTL"): New tests.
-rw-r--r--doc/guix.texi10
-rw-r--r--guix/scripts/publish.scm30
-rw-r--r--tests/publish.scm32
3 files changed, 63 insertions, 9 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index bfc714c5b68..a10943f2d55 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -12703,6 +12703,16 @@ Additionally, when @option{--cache} is used, cached entries that have
12703not been accessed for @var{ttl} and that no longer have a corresponding 12703not been accessed for @var{ttl} and that no longer have a corresponding
12704item in the store, may be deleted. 12704item in the store, may be deleted.
12705 12705
12706@item --negative-ttl=@var{ttl}
12707Similarly produce @code{Cache-Control} HTTP headers to advertise the
12708time-to-live (TTL) of @emph{negative} lookups---missing store items, for
12709which the HTTP 404 code is returned. By default, no negative TTL is
12710advertised.
12711
12712This parameter can help adjust server load and substitute latency by
12713instructing cooperating clients to be more or less patient when a store
12714item is missing.
12715
12706@item --cache-bypass-threshold=@var{size} 12716@item --cache-bypass-threshold=@var{size}
12707When used in conjunction with @option{--cache}, store items smaller than 12717When used in conjunction with @option{--cache}, store items smaller than
12708@var{size} are immediately available, even when they are not yet in 12718@var{size} are immediately available, even when they are not yet in
diff --git a/guix/scripts/publish.scm b/guix/scripts/publish.scm
index 39bb224cad2..ef6fa5f074a 100644
--- a/guix/scripts/publish.scm
+++ b/guix/scripts/publish.scm
@@ -1,7 +1,7 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015 David Thompson <davet@gnu.org> 2;;; Copyright © 2015 David Thompson <davet@gnu.org>
3;;; Copyright © 2020 by Amar M. Singh <nly@disroot.org> 3;;; Copyright © 2020 by Amar M. Singh <nly@disroot.org>
4;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> 4;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
5;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com> 5;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
6;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com> 6;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com>
7;;; 7;;;
@@ -102,6 +102,8 @@ Publish ~a over HTTP.\n") %store-directory)
102 (display (G_ " 102 (display (G_ "
103 --ttl=TTL announce narinfos can be cached for TTL seconds")) 103 --ttl=TTL announce narinfos can be cached for TTL seconds"))
104 (display (G_ " 104 (display (G_ "
105 --negative-ttl=TTL announce missing narinfos can be cached for TTL seconds"))
106 (display (G_ "
105 --nar-path=PATH use PATH as the prefix for nar URLs")) 107 --nar-path=PATH use PATH as the prefix for nar URLs"))
106 (display (G_ " 108 (display (G_ "
107 --public-key=FILE use FILE as the public key for signatures")) 109 --public-key=FILE use FILE as the public key for signatures"))
@@ -224,6 +226,13 @@ usage."
224 (leave (G_ "~a: invalid duration~%") arg)) 226 (leave (G_ "~a: invalid duration~%") arg))
225 (alist-cons 'narinfo-ttl (time-second duration) 227 (alist-cons 'narinfo-ttl (time-second duration)
226 result)))) 228 result))))
229 (option '("negative-ttl") #t #f
230 (lambda (opt name arg result)
231 (let ((duration (string->duration arg)))
232 (unless duration
233 (leave (G_ "~a: invalid duration~%") arg))
234 (alist-cons 'narinfo-negative-ttl (time-second duration)
235 result))))
227 (option '("nar-path") #t #f 236 (option '("nar-path") #t #f
228 (lambda (opt name arg result) 237 (lambda (opt name arg result)
229 (alist-cons 'nar-path arg result))) 238 (alist-cons 'nar-path arg result)))
@@ -390,14 +399,14 @@ References: ~a~%"
390 399
391(define* (render-narinfo store request hash 400(define* (render-narinfo store request hash
392 #:key ttl (compressions (list %no-compression)) 401 #:key ttl (compressions (list %no-compression))
393 (nar-path "nar")) 402 (nar-path "nar") negative-ttl)
394 "Render metadata for the store path corresponding to HASH. If TTL is true, 403 "Render metadata for the store path corresponding to HASH. If TTL is true,
395advertise it as the maximum validity period (in seconds) via the 404advertise it as the maximum validity period (in seconds) via the
396'Cache-Control' header. This allows 'guix substitute' to cache it for an 405'Cache-Control' header. This allows 'guix substitute' to cache it for an
397appropriate duration. NAR-PATH specifies the prefix for nar URLs." 406appropriate duration. NAR-PATH specifies the prefix for nar URLs."
398 (let ((store-path (hash-part->path store hash))) 407 (let ((store-path (hash-part->path store hash)))
399 (if (string-null? store-path) 408 (if (string-null? store-path)
400 (not-found request #:phrase "") 409 (not-found request #:phrase "" #:ttl negative-ttl)
401 (values `((content-type . (application/x-nix-narinfo)) 410 (values `((content-type . (application/x-nix-narinfo))
402 ,@(if ttl 411 ,@(if ttl
403 `((cache-control (max-age . ,ttl))) 412 `((cache-control (max-age . ,ttl)))
@@ -512,7 +521,7 @@ interpreted as the basename of a store item."
512 521
513(define* (render-narinfo/cached store request hash 522(define* (render-narinfo/cached store request hash
514 #:key ttl (compressions (list %no-compression)) 523 #:key ttl (compressions (list %no-compression))
515 (nar-path "nar") 524 (nar-path "nar") negative-ttl
516 cache pool) 525 cache pool)
517 "Respond to the narinfo request for REQUEST. If the narinfo is available in 526 "Respond to the narinfo request for REQUEST. If the narinfo is available in
518CACHE, then send it; otherwise, return 404 and \"bake\" that nar and narinfo 527CACHE, then send it; otherwise, return 404 and \"bake\" that nar and narinfo
@@ -536,7 +545,7 @@ requested using POOL."
536 #:compression 545 #:compression
537 (first compressions))))) 546 (first compressions)))))
538 (cond ((string-null? item) 547 (cond ((string-null? item)
539 (not-found request)) 548 (not-found request #:ttl negative-ttl))
540 ((file-exists? cached) 549 ((file-exists? cached)
541 ;; Narinfo is in cache, send it. 550 ;; Narinfo is in cache, send it.
542 (values `((content-type . (application/x-nix-narinfo)) 551 (values `((content-type . (application/x-nix-narinfo))
@@ -584,7 +593,7 @@ requested using POOL."
584 #:phrase "We're baking it" 593 #:phrase "We're baking it"
585 #:ttl 300))) ;should be available within 5m 594 #:ttl 300))) ;should be available within 5m
586 (else 595 (else
587 (not-found request #:phrase ""))))) 596 (not-found request #:phrase "" #:ttl negative-ttl)))))
588 597
589(define (compress-nar cache item compression) 598(define (compress-nar cache item compression)
590 "Save in directory CACHE the nar for ITEM compressed with COMPRESSION." 599 "Save in directory CACHE the nar for ITEM compressed with COMPRESSION."
@@ -974,7 +983,7 @@ methods, return the applicable compression."
974(define* (make-request-handler store 983(define* (make-request-handler store
975 #:key 984 #:key
976 cache pool 985 cache pool
977 narinfo-ttl 986 narinfo-ttl narinfo-negative-ttl
978 (nar-path "nar") 987 (nar-path "nar")
979 (compressions (list %no-compression))) 988 (compressions (list %no-compression)))
980 (define compression-type? 989 (define compression-type?
@@ -1006,10 +1015,12 @@ methods, return the applicable compression."
1006 #:cache cache 1015 #:cache cache
1007 #:pool pool 1016 #:pool pool
1008 #:ttl narinfo-ttl 1017 #:ttl narinfo-ttl
1018 #:negative-ttl narinfo-negative-ttl
1009 #:nar-path nar-path 1019 #:nar-path nar-path
1010 #:compressions compressions) 1020 #:compressions compressions)
1011 (render-narinfo store request hash 1021 (render-narinfo store request hash
1012 #:ttl narinfo-ttl 1022 #:ttl narinfo-ttl
1023 #:negative-ttl narinfo-negative-ttl
1013 #:nar-path nar-path 1024 #:nar-path nar-path
1014 #:compressions compressions))) 1025 #:compressions compressions)))
1015 ;; /nar/file/NAME/sha256/HASH 1026 ;; /nar/file/NAME/sha256/HASH
@@ -1068,7 +1079,7 @@ methods, return the applicable compression."
1068 #:key 1079 #:key
1069 advertise? port 1080 advertise? port
1070 (compressions (list %no-compression)) 1081 (compressions (list %no-compression))
1071 (nar-path "nar") narinfo-ttl 1082 (nar-path "nar") narinfo-ttl narinfo-negative-ttl
1072 cache pool) 1083 cache pool)
1073 (when advertise? 1084 (when advertise?
1074 (let ((name (service-name))) 1085 (let ((name (service-name)))
@@ -1084,6 +1095,7 @@ methods, return the applicable compression."
1084 #:pool pool 1095 #:pool pool
1085 #:nar-path nar-path 1096 #:nar-path nar-path
1086 #:narinfo-ttl narinfo-ttl 1097 #:narinfo-ttl narinfo-ttl
1098 #:narinfo-negative-ttl narinfo-negative-ttl
1087 #:compressions compressions) 1099 #:compressions compressions)
1088 concurrent-http-server 1100 concurrent-http-server
1089 `(#:socket ,socket))) 1101 `(#:socket ,socket)))
@@ -1127,6 +1139,7 @@ methods, return the applicable compression."
1127 (user (assoc-ref opts 'user)) 1139 (user (assoc-ref opts 'user))
1128 (port (assoc-ref opts 'port)) 1140 (port (assoc-ref opts 'port))
1129 (ttl (assoc-ref opts 'narinfo-ttl)) 1141 (ttl (assoc-ref opts 'narinfo-ttl))
1142 (negative-ttl (assoc-ref opts 'narinfo-negative-ttl))
1130 (compressions (match (filter-map (match-lambda 1143 (compressions (match (filter-map (match-lambda
1131 (('compression . compression) 1144 (('compression . compression)
1132 compression) 1145 compression)
@@ -1192,6 +1205,7 @@ consider using the '--user' option!~%")))
1192 "publish worker")) 1205 "publish worker"))
1193 #:nar-path nar-path 1206 #:nar-path nar-path
1194 #:compressions compressions 1207 #:compressions compressions
1208 #:narinfo-negative-ttl negative-ttl
1195 #:narinfo-ttl ttl)))))) 1209 #:narinfo-ttl ttl))))))
1196 1210
1197;;; Local Variables: 1211;;; Local Variables:
diff --git a/tests/publish.scm b/tests/publish.scm
index 3e67c435acf..c3d086995ab 100644
--- a/tests/publish.scm
+++ b/tests/publish.scm
@@ -1,7 +1,7 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015 David Thompson <davet@gnu.org> 2;;; Copyright © 2015 David Thompson <davet@gnu.org>
3;;; Copyright © 2020 by Amar M. Singh <nly@disroot.org> 3;;; Copyright © 2020 by Amar M. Singh <nly@disroot.org>
4;;; Copyright © 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> 4;;; Copyright © 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
5;;; 5;;;
6;;; This file is part of GNU Guix. 6;;; This file is part of GNU Guix.
7;;; 7;;;
@@ -700,6 +700,36 @@ References: ~%"
700 (= (response-content-length response) (stat:size (stat log))) 700 (= (response-content-length response) (stat:size (stat log)))
701 (first (response-content-type response)))))) 701 (first (response-content-type response))))))
702 702
703(test-equal "negative TTL"
704 `(404 42)
705
706 (call-with-temporary-directory
707 (lambda (cache)
708 (let ((thread (with-separate-output-ports
709 (call-with-new-thread
710 (lambda ()
711 (guix-publish "--port=6786" "-C0"
712 "--negative-ttl=42s"))))))
713 (wait-until-ready 6786)
714
715 (let* ((base "http://localhost:6786/")
716 (url (string-append base (make-string 32 #\z)
717 ".narinfo"))
718 (response (http-get url)))
719 (list (response-code response)
720 (match (assq-ref (response-headers response) 'cache-control)
721 ((('max-age . ttl)) ttl)
722 (_ #f))))))))
723
724(test-equal "no negative TTL"
725 `(404 #f)
726 (let* ((uri (publish-uri
727 (string-append "/" (make-string 32 #\z)
728 ".narinfo")))
729 (response (http-get uri)))
730 (list (response-code response)
731 (assq-ref (response-headers response) 'cache-control))))
732
703(test-equal "/log/NAME not found" 733(test-equal "/log/NAME not found"
704 404 734 404
705 (let ((uri (publish-uri "/log/does-not-exist"))) 735 (let ((uri (publish-uri "/log/does-not-exist")))