Handle migrations and server startup better
The server part of the guix-data-service doesn't work great as a guix service, since it often fails to start if the migrations take any time at all. To address this, start the server before running the migrations, and serve the pages that work without the database, plus a general 503 response. Once the migrations have completed, switch to the normal behaviour.
This commit is contained in:
parent
d19eb07138
commit
8e23d38660
4 changed files with 249 additions and 194 deletions
|
|
@ -71,6 +71,7 @@
|
||||||
#:use-module (guix-data-service web repository controller)
|
#:use-module (guix-data-service web repository controller)
|
||||||
#:use-module (guix-data-service web package controller)
|
#:use-module (guix-data-service web package controller)
|
||||||
#:export (%show-error-details
|
#:export (%show-error-details
|
||||||
|
handle-static-assets
|
||||||
controller))
|
controller))
|
||||||
|
|
||||||
(define cache-control-default-max-age
|
(define cache-control-default-max-age
|
||||||
|
|
@ -513,16 +514,26 @@
|
||||||
|
|
||||||
(define* (controller request method-and-path-components
|
(define* (controller request method-and-path-components
|
||||||
mime-types body
|
mime-types body
|
||||||
secret-key-base)
|
secret-key-base
|
||||||
(define (controller-thunk)
|
startup-completed?)
|
||||||
|
(define (running-controller-thunk)
|
||||||
(actual-controller request
|
(actual-controller request
|
||||||
method-and-path-components
|
method-and-path-components
|
||||||
mime-types
|
mime-types
|
||||||
body
|
body
|
||||||
secret-key-base))
|
secret-key-base))
|
||||||
|
|
||||||
|
(define (startup-controller-thunk)
|
||||||
|
(or
|
||||||
|
(base-controller request method-and-path-components)
|
||||||
|
(render-html
|
||||||
|
#:sxml (server-starting-up-page)
|
||||||
|
#:code 503)))
|
||||||
|
|
||||||
(call-with-error-handling
|
(call-with-error-handling
|
||||||
controller-thunk
|
(if startup-completed?
|
||||||
|
running-controller-thunk
|
||||||
|
startup-controller-thunk)
|
||||||
#:on-error 'backtrace
|
#:on-error 'backtrace
|
||||||
#:post-error (lambda args
|
#:post-error (lambda args
|
||||||
(render-html #:sxml (error-page
|
(render-html #:sxml (error-page
|
||||||
|
|
@ -531,51 +542,8 @@
|
||||||
#f))
|
#f))
|
||||||
#:code 500))))
|
#:code 500))))
|
||||||
|
|
||||||
(define (actual-controller request
|
(define (base-controller request method-and-path-components)
|
||||||
method-and-path-components
|
|
||||||
mime-types
|
|
||||||
body
|
|
||||||
secret-key-base)
|
|
||||||
(define path
|
|
||||||
(uri-path (request-uri request)))
|
|
||||||
|
|
||||||
(define (delegate-to f)
|
|
||||||
(or (f request
|
|
||||||
method-and-path-components
|
|
||||||
mime-types
|
|
||||||
body)
|
|
||||||
(render-html
|
|
||||||
#:sxml (general-not-found
|
|
||||||
"Page not found"
|
|
||||||
"")
|
|
||||||
#:code 404)))
|
|
||||||
|
|
||||||
(define (delegate-to-with-secret-key-base f)
|
|
||||||
(or (f request
|
|
||||||
method-and-path-components
|
|
||||||
mime-types
|
|
||||||
body
|
|
||||||
secret-key-base)
|
|
||||||
(render-html
|
|
||||||
#:sxml (general-not-found
|
|
||||||
"Page not found"
|
|
||||||
"")
|
|
||||||
#:code 404)))
|
|
||||||
|
|
||||||
(match method-and-path-components
|
(match method-and-path-components
|
||||||
(('GET)
|
|
||||||
(render-html
|
|
||||||
#:sxml (index
|
|
||||||
(parallel-via-thread-pool-channel
|
|
||||||
(with-thread-postgresql-connection
|
|
||||||
(lambda (conn)
|
|
||||||
(map
|
|
||||||
(lambda (git-repository-details)
|
|
||||||
(cons
|
|
||||||
git-repository-details
|
|
||||||
(all-branches-with-most-recent-commit
|
|
||||||
conn (first git-repository-details))))
|
|
||||||
(all-git-repositories conn))))))))
|
|
||||||
(('GET "assets" rest ...)
|
(('GET "assets" rest ...)
|
||||||
(or (handle-static-assets (string-join rest "/")
|
(or (handle-static-assets (string-join rest "/")
|
||||||
(request-headers request))
|
(request-headers request))
|
||||||
|
|
@ -610,6 +578,55 @@
|
||||||
"README not found"
|
"README not found"
|
||||||
"The README.html file does not exist")
|
"The README.html file does not exist")
|
||||||
#:code 404))))
|
#:code 404))))
|
||||||
|
((method path ...) #f)))
|
||||||
|
|
||||||
|
(define (actual-controller request
|
||||||
|
method-and-path-components
|
||||||
|
mime-types
|
||||||
|
body
|
||||||
|
secret-key-base)
|
||||||
|
(define path
|
||||||
|
(uri-path (request-uri request)))
|
||||||
|
|
||||||
|
(define (delegate-to f)
|
||||||
|
(or (f request
|
||||||
|
method-and-path-components
|
||||||
|
mime-types
|
||||||
|
body)
|
||||||
|
(render-html
|
||||||
|
#:sxml (general-not-found
|
||||||
|
"Page not found"
|
||||||
|
"")
|
||||||
|
#:code 404)))
|
||||||
|
|
||||||
|
(define (delegate-to-with-secret-key-base f)
|
||||||
|
(or (f request
|
||||||
|
method-and-path-components
|
||||||
|
mime-types
|
||||||
|
body
|
||||||
|
secret-key-base)
|
||||||
|
(render-html
|
||||||
|
#:sxml (general-not-found
|
||||||
|
"Page not found"
|
||||||
|
"")
|
||||||
|
#:code 404)))
|
||||||
|
|
||||||
|
(or
|
||||||
|
(base-controller request method-and-path-components)
|
||||||
|
(match method-and-path-components
|
||||||
|
(('GET)
|
||||||
|
(render-html
|
||||||
|
#:sxml (index
|
||||||
|
(parallel-via-thread-pool-channel
|
||||||
|
(with-thread-postgresql-connection
|
||||||
|
(lambda (conn)
|
||||||
|
(map
|
||||||
|
(lambda (git-repository-details)
|
||||||
|
(cons
|
||||||
|
git-repository-details
|
||||||
|
(all-branches-with-most-recent-commit
|
||||||
|
conn (first git-repository-details))))
|
||||||
|
(all-git-repositories conn))))))))
|
||||||
(('GET "builds")
|
(('GET "builds")
|
||||||
(delegate-to build-controller))
|
(delegate-to build-controller))
|
||||||
(('GET "statistics")
|
(('GET "statistics")
|
||||||
|
|
@ -681,4 +698,4 @@
|
||||||
#:sxml (general-not-found
|
#:sxml (general-not-found
|
||||||
"Page not found"
|
"Page not found"
|
||||||
"")
|
"")
|
||||||
#:code 404))))
|
#:code 404)))))
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,22 @@
|
||||||
#:use-module (web request)
|
#:use-module (web request)
|
||||||
#:use-module (web uri)
|
#:use-module (web uri)
|
||||||
#:use-module (system repl error-handling)
|
#:use-module (system repl error-handling)
|
||||||
|
#:use-module (ice-9 atomic)
|
||||||
#:use-module (fibers web server)
|
#:use-module (fibers web server)
|
||||||
#:use-module (guix-data-service web controller)
|
#:use-module (guix-data-service web controller)
|
||||||
#:use-module (guix-data-service web util)
|
#:use-module (guix-data-service web util)
|
||||||
#:export (start-guix-data-service-web-server))
|
#:export (start-guix-data-service-web-server))
|
||||||
|
|
||||||
(define (handler request body controller secret-key-base)
|
(define (check-startup-completed startup-completed)
|
||||||
|
(if (atomic-box-ref startup-completed)
|
||||||
|
(begin
|
||||||
|
;; Just in case this atomic-box-ref is expensive, only do it when
|
||||||
|
;; necessary
|
||||||
|
(set! check-startup-completed (const #t))
|
||||||
|
#t)
|
||||||
|
#f))
|
||||||
|
|
||||||
|
(define (handler request body controller secret-key-base startup-completed)
|
||||||
(display
|
(display
|
||||||
(format #f "~a ~a\n"
|
(format #f "~a ~a\n"
|
||||||
(request-method request)
|
(request-method request)
|
||||||
|
|
@ -42,14 +52,17 @@
|
||||||
request-components)
|
request-components)
|
||||||
mime-types
|
mime-types
|
||||||
body
|
body
|
||||||
secret-key-base))))
|
secret-key-base
|
||||||
|
(check-startup-completed startup-completed)))))
|
||||||
|
|
||||||
(define* (start-guix-data-service-web-server port host secret-key-base)
|
(define* (start-guix-data-service-web-server port host secret-key-base
|
||||||
|
startup-completed)
|
||||||
(call-with-error-handling
|
(call-with-error-handling
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(run-server (lambda (request body)
|
(run-server (lambda (request body)
|
||||||
(handler request body controller
|
(handler request body controller
|
||||||
secret-key-base))
|
secret-key-base
|
||||||
|
startup-completed))
|
||||||
#:host host
|
#:host host
|
||||||
#:port port))
|
#:port port))
|
||||||
#:on-error 'backtrace
|
#:on-error 'backtrace
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,8 @@
|
||||||
view-narinfos
|
view-narinfos
|
||||||
view-store-item
|
view-store-item
|
||||||
view-derivation-source-file
|
view-derivation-source-file
|
||||||
error-page))
|
error-page
|
||||||
|
server-starting-up-page))
|
||||||
|
|
||||||
(define* (header)
|
(define* (header)
|
||||||
`(nav
|
`(nav
|
||||||
|
|
@ -1004,3 +1005,11 @@
|
||||||
`((b ,key)
|
`((b ,key)
|
||||||
(pre ,args))))
|
(pre ,args))))
|
||||||
'())))))
|
'())))))
|
||||||
|
|
||||||
|
(define* (server-starting-up-page)
|
||||||
|
(layout
|
||||||
|
#:body
|
||||||
|
`(,(header)
|
||||||
|
(div (@ (class "container"))
|
||||||
|
(h1 "Server is starting up")
|
||||||
|
(p "Database migrations are running, this can take some time.")))))
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@
|
||||||
(use-modules (srfi srfi-1)
|
(use-modules (srfi srfi-1)
|
||||||
(srfi srfi-37)
|
(srfi srfi-37)
|
||||||
(ice-9 match)
|
(ice-9 match)
|
||||||
|
(ice-9 atomic)
|
||||||
|
(ice-9 threads)
|
||||||
(ice-9 textual-ports)
|
(ice-9 textual-ports)
|
||||||
(system repl server)
|
(system repl server)
|
||||||
(system repl repl)
|
(system repl repl)
|
||||||
|
|
@ -137,6 +139,72 @@
|
||||||
(when repl-port
|
(when repl-port
|
||||||
(spawn-server (make-tcp-server-socket #:port repl-port))))
|
(spawn-server (make-tcp-server-socket #:port repl-port))))
|
||||||
|
|
||||||
|
(parameterize ((%narinfo-signing-public-key
|
||||||
|
(catch
|
||||||
|
'system-error
|
||||||
|
(lambda ()
|
||||||
|
(and=> (assoc-ref opts 'narinfo-signing-public-key)
|
||||||
|
read-file-sexp))
|
||||||
|
(lambda (key . args)
|
||||||
|
(simple-format
|
||||||
|
(current-error-port)
|
||||||
|
"warning: failed to load narinfo signing public key from ~A\n"
|
||||||
|
(assoc-ref opts 'narinfo-signing-private-key))
|
||||||
|
(simple-format (current-error-port)
|
||||||
|
" ~A: ~A\n"
|
||||||
|
key args))))
|
||||||
|
(%narinfo-signing-private-key
|
||||||
|
(catch
|
||||||
|
'system-error
|
||||||
|
(lambda ()
|
||||||
|
(and=> (assoc-ref opts 'narinfo-signing-private-key)
|
||||||
|
read-file-sexp))
|
||||||
|
(lambda (key . args)
|
||||||
|
(simple-format
|
||||||
|
(current-error-port)
|
||||||
|
"warning: failed to load narinfo signing private key from ~A\n"
|
||||||
|
(assoc-ref opts 'narinfo-signing-private-key))
|
||||||
|
(simple-format (current-error-port)
|
||||||
|
" ~A: ~A\n"
|
||||||
|
key args)
|
||||||
|
(display "warning: not signing narinfo files\n"
|
||||||
|
(current-error-port))
|
||||||
|
#f)))
|
||||||
|
(%show-error-details
|
||||||
|
(assoc-ref opts 'show-error-details)))
|
||||||
|
|
||||||
|
(let* ((startup-completed
|
||||||
|
(make-atomic-box
|
||||||
|
(if (assoc-ref opts 'update-database)
|
||||||
|
#f
|
||||||
|
#t)))
|
||||||
|
(server-thread
|
||||||
|
(call-with-new-thread
|
||||||
|
(lambda ()
|
||||||
|
(with-postgresql-connection-per-thread
|
||||||
|
"web"
|
||||||
|
(lambda ()
|
||||||
|
;; Provide some visual space between the startup output and the server
|
||||||
|
;; starting
|
||||||
|
(simple-format #t "\n\nStarting the server on http://~A:~A/\n\n"
|
||||||
|
(assq-ref opts 'host)
|
||||||
|
(assq-ref opts 'port))
|
||||||
|
|
||||||
|
(start-guix-data-service-web-server
|
||||||
|
(assq-ref opts 'port)
|
||||||
|
(assq-ref opts 'host)
|
||||||
|
(assq-ref opts 'secret-key-base)
|
||||||
|
startup-completed))
|
||||||
|
#:statement-timeout
|
||||||
|
(assq-ref opts 'postgresql-statement-timeout)))))
|
||||||
|
|
||||||
|
(pid-file (assq-ref opts 'pid-file)))
|
||||||
|
|
||||||
|
(when pid-file
|
||||||
|
(call-with-output-file pid-file
|
||||||
|
(lambda (port)
|
||||||
|
(simple-format port "~A\n" (getpid)))))
|
||||||
|
|
||||||
(when (assoc-ref opts 'update-database)
|
(when (assoc-ref opts 'update-database)
|
||||||
(let ((command
|
(let ((command
|
||||||
(list (%config 'sqitch)
|
(list (%config 'sqitch)
|
||||||
|
|
@ -186,62 +254,10 @@
|
||||||
(simple-format
|
(simple-format
|
||||||
(current-error-port)
|
(current-error-port)
|
||||||
"error: sqitch command failed\n")
|
"error: sqitch command failed\n")
|
||||||
(exit 1))))
|
(exit 1))
|
||||||
|
|
||||||
(let ((pid-file (assq-ref opts 'pid-file)))
|
(atomic-box-set! startup-completed #t)))
|
||||||
(when pid-file
|
|
||||||
(call-with-output-file pid-file
|
|
||||||
(lambda (port)
|
|
||||||
(simple-format port "~A\n" (getpid))))))
|
|
||||||
|
|
||||||
(parameterize ((%narinfo-signing-public-key
|
|
||||||
(catch
|
|
||||||
'system-error
|
|
||||||
(lambda ()
|
|
||||||
(and=> (assoc-ref opts 'narinfo-signing-public-key)
|
|
||||||
read-file-sexp))
|
|
||||||
(lambda (key . args)
|
|
||||||
(simple-format
|
|
||||||
(current-error-port)
|
|
||||||
"warning: failed to load narinfo signing public key from ~A\n"
|
|
||||||
(assoc-ref opts 'narinfo-signing-private-key))
|
|
||||||
(simple-format (current-error-port)
|
|
||||||
" ~A: ~A\n"
|
|
||||||
key args))))
|
|
||||||
(%narinfo-signing-private-key
|
|
||||||
(catch
|
|
||||||
'system-error
|
|
||||||
(lambda ()
|
|
||||||
(and=> (assoc-ref opts 'narinfo-signing-private-key)
|
|
||||||
read-file-sexp))
|
|
||||||
(lambda (key . args)
|
|
||||||
(simple-format
|
|
||||||
(current-error-port)
|
|
||||||
"warning: failed to load narinfo signing private key from ~A\n"
|
|
||||||
(assoc-ref opts 'narinfo-signing-private-key))
|
|
||||||
(simple-format (current-error-port)
|
|
||||||
" ~A: ~A\n"
|
|
||||||
key args)
|
|
||||||
(display "warning: not signing narinfo files\n"
|
|
||||||
(current-error-port))
|
|
||||||
#f)))
|
|
||||||
(%show-error-details
|
|
||||||
(assoc-ref opts 'show-error-details)))
|
|
||||||
|
|
||||||
(start-substitute-query-thread)
|
(start-substitute-query-thread)
|
||||||
|
|
||||||
;; Provide some visual space between the startup output and the server
|
(join-thread server-thread))))
|
||||||
;; starting
|
|
||||||
(simple-format #t "\n\nStarting the server on http://~A:~A/\n\n"
|
|
||||||
(assq-ref opts 'host)
|
|
||||||
(assq-ref opts 'port))
|
|
||||||
|
|
||||||
(with-postgresql-connection-per-thread
|
|
||||||
"web"
|
|
||||||
(lambda ()
|
|
||||||
(start-guix-data-service-web-server
|
|
||||||
(assq-ref opts 'port)
|
|
||||||
(assq-ref opts 'host)
|
|
||||||
(assq-ref opts 'secret-key-base)))
|
|
||||||
#:statement-timeout
|
|
||||||
(assq-ref opts 'postgresql-statement-timeout))))
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue