diff options
Diffstat (limited to 'guix/store.scm')
-rw-r--r-- | guix/store.scm | 210 |
1 files changed, 116 insertions, 94 deletions
diff --git a/guix/store.scm b/guix/store.scm index cf5d5eeccc..1ab2b08b47 100644 --- a/guix/store.scm +++ b/guix/store.scm @@ -36,6 +36,7 @@ #:use-module (rnrs bytevectors) #:use-module (ice-9 binary-ports) #:use-module ((ice-9 control) #:select (let/ec)) + #:use-module (ice-9 atomic) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) #:use-module (srfi srfi-9 gnu) @@ -47,7 +48,7 @@ #:use-module (ice-9 match) #:use-module (ice-9 vlist) #:use-module (ice-9 popen) - #:use-module (ice-9 threads) + #:autoload (ice-9 threads) (current-processor-count) #:use-module (ice-9 format) #:use-module (web uri) #:export (%daemon-socket-uri @@ -68,6 +69,7 @@ nix-server-socket current-store-protocol-version ;for internal use + cache-lookup-recorder ;for internal use mcached &store-error store-error? @@ -87,6 +89,11 @@ nix-protocol-error-message nix-protocol-error-status + allocate-store-connection-cache + store-connection-cache + set-store-connection-cache + set-store-connection-cache! + hash-algo build-mode @@ -141,7 +148,6 @@ built-in-builders references references/cached - references/substitutes references* query-path-info* requisites @@ -383,8 +389,8 @@ ;; the session. (ats-cache store-connection-add-to-store-cache) (atts-cache store-connection-add-text-to-store-cache) - (object-cache store-connection-object-cache - (default vlist-null)) ;vhash + (caches store-connection-caches + (default '#())) ;vector (built-in-builders store-connection-built-in-builders (default (delay '())))) ;promise @@ -586,6 +592,10 @@ for this connection will be pinned. Return a server object." (write-int (if reserve-space? 1 0) port)) (letrec* ((built-in-builders (delay (%built-in-builders conn))) + (caches + (make-vector + (atomic-box-ref %store-connection-caches) + vlist-null)) (conn (%make-store-connection port (protocol-major v) @@ -593,7 +603,7 @@ for this connection will be pinned. Return a server object." output flush (make-hash-table 100) (make-hash-table 100) - vlist-null + caches built-in-builders))) (let loop ((done? (process-stderr conn))) (or done? (process-stderr conn))) @@ -616,7 +626,9 @@ connection. Use with care." output flush (make-hash-table 100) (make-hash-table 100) - vlist-null + (make-vector + (atomic-box-ref %store-connection-caches) + vlist-null) (delay (%built-in-builders connection)))) connection)) @@ -775,7 +787,8 @@ encoding conversion errors." (map (if (false-if-exception (resolve-interface '(gnutls))) (cut string-append "https://" <>) (cut string-append "http://" <>)) - '("ci.guix.gnu.org"))) + '("ci.guix.gnu.org" + "bordeaux.guix.gnu.org"))) (define (current-user-name) "Return the name of the calling user." @@ -1464,73 +1477,6 @@ error if there is no such root." "Return the list of references of PATH." store-path-list)) -(define %reference-cache - ;; Brute-force cache mapping store items to their list of references. - ;; Caching matters because when building a profile in the presence of - ;; grafts, we keep calling 'graft-derivation', which in turn calls - ;; 'references/substitutes' many times with the same arguments. Ideally we - ;; would use a cache associated with the daemon connection instead (XXX). - (make-hash-table 100)) - -(define (references/cached store item) - "Like 'references', but cache results." - (or (hash-ref %reference-cache item) - (let ((references (references store item))) - (hash-set! %reference-cache item references) - references))) - -(define (references/substitutes store items) - "Return the list of list of references of ITEMS; the result has the same -length as ITEMS. Query substitute information for any item missing from the -store at once. Raise a '&store-protocol-error' exception if reference -information for one of ITEMS is missing." - (let* ((requested items) - (local-refs (map (lambda (item) - (or (hash-ref %reference-cache item) - (guard (c ((store-protocol-error? c) #f)) - (references store item)))) - items)) - (missing (fold-right (lambda (item local-ref result) - (if local-ref - result - (cons item result))) - '() - items local-refs)) - - ;; Query all the substitutes at once to minimize the cost of - ;; launching 'guix substitute' and making HTTP requests. - (substs (if (null? missing) - '() - (substitutable-path-info store missing)))) - (when (< (length substs) (length missing)) - (raise (condition (&store-protocol-error - (message "cannot determine \ -the list of references") - (status 1))))) - - ;; Intersperse SUBSTS and LOCAL-REFS. - (let loop ((items items) - (local-refs local-refs) - (result '())) - (match items - (() - (let ((result (reverse result))) - (for-each (cut hash-set! %reference-cache <> <>) - requested result) - result)) - ((item items ...) - (match local-refs - ((#f tail ...) - (loop items tail - (cons (any (lambda (subst) - (and (string=? (substitutable-path subst) item) - (substitutable-references subst))) - substs) - result))) - ((head tail ...) - (loop items tail - (cons head result))))))))) - (define* (fold-path store proc seed paths #:optional (relatives (cut references store <>))) "Call PROC for each of the RELATIVES of PATHS, exactly once, and return the @@ -1801,6 +1747,77 @@ This makes sense only when the daemon was started with '--cache-failures'." ;;; +;;; Per-connection caches. +;;; + +;; Number of currently allocated store connection caches--things that go in +;; the 'caches' vector of <store-connection>. +(define %store-connection-caches (make-atomic-box 0)) + +(define (allocate-store-connection-cache name) + "Allocate a new cache for store connections and return its identifier. Said +identifier can be passed as an argument to " + (let loop ((current (atomic-box-ref %store-connection-caches))) + (let ((previous (atomic-box-compare-and-swap! %store-connection-caches + current (+ current 1)))) + (if (= previous current) + current + (loop current))))) + +(define %object-cache-id + ;; The "object cache", mapping lowerable objects such as <package> records + ;; to derivations. + (allocate-store-connection-cache 'object-cache)) + +(define (vector-set vector index value) + (let ((new (vector-copy vector))) + (vector-set! new index value) + new)) + +(define (store-connection-cache store cache) + "Return the cache of STORE identified by CACHE, an identifier as returned by +'allocate-store-connection-cache'." + (vector-ref (store-connection-caches store) cache)) + +(define (set-store-connection-cache store cache value) + "Return a copy of STORE where CACHE has the given VALUE. CACHE must be a +value returned by 'allocate-store-connection-cache'." + (store-connection + (inherit store) + (caches (vector-set (store-connection-caches store) cache value)))) + +(define set-store-connection-caches! ;private + (record-modifier <store-connection> 'caches)) + +(define (set-store-connection-cache! store cache value) + "Set STORE's CACHE to VALUE. + +This is a mutating version that should be avoided. Prefer the functional +'set-store-connection-cache' instead, together with using %STORE-MONAD." + (vector-set! (store-connection-caches store) cache value)) + + +(define %reference-cache-id + ;; Cache mapping store items to their list of references. Caching matters + ;; because when building a profile in the presence of grafts, we keep + ;; calling 'graft-derivation', which in turn calls 'references/cached' many + ;; times with the same arguments. + (allocate-store-connection-cache 'reference-cache)) + +(define (references/cached store item) + "Like 'references', but cache results." + (let ((cache (store-connection-cache store %reference-cache-id))) + (match (vhash-assoc item cache) + ((_ . references) + references) + (#f + (let* ((references (references store item)) + (cache (vhash-cons item references cache))) + (set-store-connection-cache! store %reference-cache-id cache) + references))))) + + +;;; ;;; Store monad. ;;; @@ -1819,7 +1836,9 @@ This makes sense only when the daemon was started with '--cache-failures'." (template-directory instantiations %store-monad) (define* (cache-object-mapping object keys result - #:key (vhash-cons vhash-consq)) + #:key + (cache %object-cache-id) + (vhash-cons vhash-consq)) "Augment the store's object cache with a mapping from OBJECT/KEYS to RESULT. KEYS is a list of additional keys to match against, for instance a (SYSTEM TARGET) tuple. Use VHASH-CONS to insert OBJECT into the cache. @@ -1828,26 +1847,29 @@ OBJECT is typically a high-level object such as a <package> or an <origin>, and RESULT is typically its derivation." (lambda (store) (values result - (store-connection - (inherit store) - (object-cache (vhash-cons object (cons result keys) - (store-connection-object-cache store))))))) - -(define record-cache-lookup! - (if (profiled? "object-cache") + (set-store-connection-cache + store cache + (vhash-cons object (cons result keys) + (store-connection-cache store cache)))))) + +(define (cache-lookup-recorder component title) + "Return a procedure of two arguments to record cache lookups, hits, and +misses for COMPONENT. The procedure must be passed a Boolean indicating +whether the cache lookup was a hit, and the actual cache (a vhash)." + (if (profiled? component) (let ((fresh 0) (lookups 0) (hits 0) (size 0)) (register-profiling-hook! - "object-cache" + component (lambda () - (format (current-error-port) "Store object cache: + (format (current-error-port) "~a: fresh caches: ~5@a lookups: ~5@a hits: ~5@a (~,1f%) cache size: ~5@a entries~%" - fresh lookups hits + title fresh lookups hits (if (zero? lookups) 100. (* 100. (/ hits lookups))) @@ -1855,9 +1877,9 @@ and RESULT is typically its derivation." (lambda (hit? cache) (set! fresh - (if (eq? cache vlist-null) - (+ 1 fresh) - fresh)) + (if (eq? cache vlist-null) + (+ 1 fresh) + fresh)) (set! lookups (+ 1 lookups)) (set! hits (if hit? (+ hits 1) hits)) (set! size (+ (if hit? 0 1) @@ -1865,13 +1887,16 @@ and RESULT is typically its derivation." (lambda (x y) #t))) +(define record-cache-lookup! + (cache-lookup-recorder "object-cache" "Store object cache")) + (define-inlinable (lookup-cached-object object keys vhash-fold*) "Return the cached object in the store connection corresponding to OBJECT and KEYS; use VHASH-FOLD* to look for OBJECT in the cache. KEYS is a list of additional keys to match against, and which are compared with 'equal?'. Return #f on failure and the cached result otherwise." (lambda (store) - (let* ((cache (store-connection-object-cache store)) + (let* ((cache (store-connection-cache store %object-cache-id)) ;; Escape as soon as we find the result. This avoids traversing ;; the whole vlist chain and significantly reduces the number of @@ -2048,9 +2073,6 @@ the store." ;; when using 'gexp->derivation' and co. (make-parameter #f)) -(define set-store-connection-object-cache! - (record-modifier <store-connection> 'object-cache)) - (define* (run-with-store store mval #:key (guile-for-build (%guile-for-build)) @@ -2070,8 +2092,8 @@ connection, and return the result." (when (and store new-store) ;; Copy the object cache from NEW-STORE so we don't fully discard ;; the state. - (let ((cache (store-connection-object-cache new-store))) - (set-store-connection-object-cache! store cache))) + (let ((caches (store-connection-caches new-store))) + (set-store-connection-caches! store caches))) result)))) |