aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2014-03-08 12:15:38 +0100
committerLudovic Courtès <ludo@gnu.org>2014-03-08 12:16:06 +0100
commitd652b851373c1bb97da2e446b0d5aa5d0b1ad46d (patch)
tree42e39dcf5e8ff67213ba8ed22b31e9711e81fbb0
parent4bf1eb4f88f2d2b0596fe8a4b98490fc277f323b (diff)
downloadguix-d652b851373c1bb97da2e446b0d5aa5d0b1ad46d.tar
guix-d652b851373c1bb97da2e446b0d5aa5d0b1ad46d.tar.gz
offload: Make 'parallel-builds' a hard limit.
* guix/scripts/offload.scm (machine-choice-lock-file, machine-slot-file, acquire-build-slot, release-build-slot): New procedures. (choose-build-machine): Operate with (machine-choice-lock-file) taken. Acquire a build slot for each of MACHINES. Release those not used.
-rw-r--r--guix/scripts/offload.scm91
1 files changed, 82 insertions, 9 deletions
diff --git a/guix/scripts/offload.scm b/guix/scripts/offload.scm
index fb5d178109..9ebe930a82 100644
--- a/guix/scripts/offload.scm
+++ b/guix/scripts/offload.scm
@@ -309,6 +309,10 @@ allowed on MACHINE."
(build-machine-name machine)
"." (symbol->string hint) ".lock"))
+(define (machine-choice-lock-file)
+ "Return the name of the file used as a lock when choosing a build machine."
+ (string-append %state-directory "/offload/machine-choice.lock"))
+
(define (lock-file file)
"Wait and acquire an exclusive lock on FILE. Return an open port."
(mkdir-p (dirname file))
@@ -339,17 +343,86 @@ context."
(with-file-lock (machine-lock-file machine hint)
exp ...))
+
+(define (machine-slot-file machine slot)
+ "Return the file name of MACHINE's file for SLOT."
+ ;; For each machine we have a bunch of files representing each build slot.
+ ;; When choosing a build machine, we attempt to get an exclusive lock on one
+ ;; of these; if we fail, that means all the build slots are already taken.
+ ;; Inspired by Nix's build-remote.pl.
+ (string-append (string-append %state-directory "/offload/"
+ (build-machine-name machine)
+ "/" (number->string slot))))
+
+(define (acquire-build-slot machine)
+ "Attempt to acquire a build slot on MACHINE. Return the port representing
+the slot, or #f if none is available.
+
+This mechanism allows us to set a hard limit on the number of simultaneous
+connections allowed to MACHINE."
+ (mkdir-p (dirname (machine-slot-file machine 0)))
+ (with-machine-lock machine 'slots
+ (any (lambda (slot)
+ (let ((port (open-file (machine-slot-file machine slot)
+ "w0")))
+ (catch 'flock-error
+ (lambda ()
+ (fcntl-flock port 'write-lock #:wait? #f)
+ ;; Got it!
+ (format (current-error-port)
+ "process ~a acquired build slot '~a'~%"
+ (getpid) (port-filename port))
+ port)
+ (lambda args
+ ;; PORT is already locked by another process.
+ (close-port port)
+ #f))))
+ (iota (build-machine-parallel-builds machine)))))
+
+(define (release-build-slot slot)
+ "Release SLOT, a build slot as returned as by 'acquire-build-slot'."
+ (close-port slot))
+
(define (choose-build-machine requirements machines)
"Return the best machine among MACHINES fulfilling REQUIREMENTS, or #f."
- (let ((machines (sort (filter (cut machine-matches? <> requirements)
- machines)
- machine-less-loaded-or-faster?)))
- (match machines
- ((head . _)
- ;; Return the best machine unless it's already overloaded.
- (and (< (machine-load head) 2.)
- head))
- (_ #f))))
+
+ ;; Proceed like this:
+ ;; 1. Acquire the global machine-choice lock.
+ ;; 2. For all MACHINES, attempt to acquire a build slot, and filter out
+ ;; those machines for which we failed.
+ ;; 3. Choose the best machine among those that are left.
+ ;; 4. Release the previously-acquired build slots of the other machines.
+ ;; 5. Release the global machine-choice lock.
+
+ (with-file-lock (machine-choice-lock-file)
+ (define machines+slots
+ (map (lambda (machine)
+ (let ((slot (acquire-build-slot machine)))
+ (and slot (list machine slot))))
+ machines))
+
+ (define (undecorate pred)
+ (match-lambda
+ ((machine slot)
+ (and (pred machine)
+ (list machine slot)))))
+
+ (let ((machines+slots (sort (filter (undecorate
+ (cut machine-matches? <> requirements))
+ machines+slots)
+ (undecorate machine-less-loaded-or-faster?))))
+ (match machines+slots
+ (((best slot) (others slots) ...)
+ ;; Release slots from the uninteresting machines.
+ (for-each release-build-slot slots)
+
+ ;; Return the best machine unless it's already overloaded.
+ (if (< (machine-load best) 2.)
+ best
+ (begin
+ (release-build-slot slot)
+ #f)))
+ (() #f)))))
(define* (process-request wants-local? system drv features
#:key