Skip to content
Snippets Groups Projects
Unverified Commit a4678c6b authored by Caleb Ristvedt's avatar Caleb Ristvedt Committed by Ludovic Courtès
Browse files

database: Make 'register-items' transactional.


* guix/store/database.scm (SQLITE_BUSY, register-output-sql): New variables.
(add-references): Don't try finalizing after each use, only after all the
uses (otherwise a finalized statement would be used if #:cache? was #f).
(call-with-transaction): New procedure.
(register-items): Use call-with-transaction to prevent broken intermediate
states from being visible.

* .dir-locals.el (call-with-transaction): indent it.

Signed-off-by: default avatarLudovic Courtès <ludo@gnu.org>
parent 274fa491
No related branches found
No related tags found
No related merge requests found
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
(eval . (put 'with-extensions 'scheme-indent-function 1)) (eval . (put 'with-extensions 'scheme-indent-function 1))
(eval . (put 'with-database 'scheme-indent-function 2)) (eval . (put 'with-database 'scheme-indent-function 2))
(eval . (put 'call-with-transaction 'scheme-indent-function 2))
(eval . (put 'call-with-container 'scheme-indent-function 1)) (eval . (put 'call-with-container 'scheme-indent-function 1))
(eval . (put 'container-excursion 'scheme-indent-function 1)) (eval . (put 'container-excursion 'scheme-indent-function 1))
......
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017 Caleb Ristvedt <caleb.ristvedt@cune.org> ;;; Copyright © 2017, 2019 Caleb Ristvedt <caleb.ristvedt@cune.org>
;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
...@@ -96,6 +96,31 @@ (define (call-with-database file proc) ...@@ -96,6 +96,31 @@ (define (call-with-database file proc)
(lambda () (lambda ()
(sqlite-close db))))) (sqlite-close db)))))
;; XXX: missing in guile-sqlite3@0.1.0
(define SQLITE_BUSY 5)
(define (call-with-transaction db proc)
"Start a transaction with DB (make as many attempts as necessary) and run
PROC. If PROC exits abnormally, abort the transaction, otherwise commit the
transaction after it finishes."
(catch 'sqlite-error
(lambda ()
;; We use begin immediate here so that if we need to retry, we
;; figure that out immediately rather than because some SQLITE_BUSY
;; exception gets thrown partway through PROC - in which case the
;; part already executed (which may contain side-effects!) would be
;; executed again for every retry.
(sqlite-exec db "begin immediate;")
(let ((result (proc)))
(sqlite-exec db "commit;")
result))
(lambda (key who error description)
(if (= error SQLITE_BUSY)
(call-with-transaction db proc)
(begin
(sqlite-exec db "rollback;")
(throw 'sqlite-error who error description))))))
(define %default-database-file (define %default-database-file
;; Default location of the store database. ;; Default location of the store database.
(string-append %store-database-directory "/db.sqlite")) (string-append %store-database-directory "/db.sqlite"))
...@@ -172,9 +197,9 @@ (define (add-references db referrer references) ...@@ -172,9 +197,9 @@ (define (add-references db referrer references)
(sqlite-bind-arguments stmt #:referrer referrer (sqlite-bind-arguments stmt #:referrer referrer
#:reference reference) #:reference reference)
(sqlite-fold cons '() stmt) ;execute it (sqlite-fold cons '() stmt) ;execute it
(sqlite-finalize stmt)
(last-insert-row-id db)) (last-insert-row-id db))
references))) references)
(sqlite-finalize stmt)))
(define* (sqlite-register db #:key path (references '()) (define* (sqlite-register db #:key path (references '())
deriver hash nar-size time) deriver hash nar-size time)
...@@ -305,6 +330,7 @@ (define to-register ...@@ -305,6 +330,7 @@ (define to-register
(define real-file-name (define real-file-name
(string-append store-dir "/" (basename (store-info-item item)))) (string-append store-dir "/" (basename (store-info-item item))))
;; When TO-REGISTER is already registered, skip it. This makes a ;; When TO-REGISTER is already registered, skip it. This makes a
;; significant differences when 'register-closures' is called ;; significant differences when 'register-closures' is called
;; consecutively for overlapping closures such as 'system' and 'bootcfg'. ;; consecutively for overlapping closures such as 'system' and 'bootcfg'.
...@@ -325,12 +351,14 @@ (define real-file-name ...@@ -325,12 +351,14 @@ (define real-file-name
(mkdir-p db-dir) (mkdir-p db-dir)
(parameterize ((sql-schema schema)) (parameterize ((sql-schema schema))
(with-database (string-append db-dir "/db.sqlite") db (with-database (string-append db-dir "/db.sqlite") db
(let* ((prefix (format #f "registering ~a items" (length items))) (call-with-transaction db
(progress (progress-reporter/bar (length items) (lambda ()
prefix log-port))) (let* ((prefix (format #f "registering ~a items" (length items)))
(call-with-progress-reporter progress (progress (progress-reporter/bar (length items)
(lambda (report) prefix log-port)))
(for-each (lambda (item) (call-with-progress-reporter progress
(register db item) (lambda (report)
(report)) (for-each (lambda (item)
items))))))) (register db item)
(report))
items)))))))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment