Guard against nested transactions

Not sure how to do this in PostgreSQL, so use a parameter.
This commit is contained in:
Christopher Baines 2025-04-19 13:19:03 +01:00
parent 982121c308
commit 2430bc4307
2 changed files with 111 additions and 93 deletions

View file

@ -19,6 +19,7 @@
#:use-module (system foreign) #:use-module (system foreign)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (ice-9 threads) #:use-module (ice-9 threads)
#:use-module (ice-9 exceptions)
#:use-module (squee) #:use-module (squee)
#:use-module (prometheus) #:use-module (prometheus)
#:use-module (guix-data-service config) #:use-module (guix-data-service config)
@ -31,6 +32,7 @@
open-postgresql-connection open-postgresql-connection
close-postgresql-connection close-postgresql-connection
%postgresql-in-transaction?
with-postgresql-transaction with-postgresql-transaction
check-test-database! check-test-database!
@ -205,8 +207,16 @@
(define %postgresql-connections-name (define %postgresql-connections-name
(make-parameter #f)) (make-parameter #f))
(define %postgresql-in-transaction?
(make-parameter #f))
(define* (with-postgresql-transaction conn f (define* (with-postgresql-transaction conn f
#:key always-rollback?) #:key always-rollback?)
(when (%postgresql-in-transaction?)
(raise-exception
(make-exception-with-message
"nested transaction detected")))
(exec-query conn "BEGIN;") (exec-query conn "BEGIN;")
(with-exception-handler (with-exception-handler
@ -219,7 +229,10 @@
;; TODO Include the stack in the exception via knots ;; TODO Include the stack in the exception via knots
(raise-exception exn)) (raise-exception exn))
(lambda () (lambda ()
(let ((result (f conn))) (let ((result
(parameterize
((%postgresql-in-transaction? #t))
(f conn))))
(exec-query conn (if always-rollback? (exec-query conn (if always-rollback?
"ROLLBACK;" "ROLLBACK;"
"COMMIT;")) "COMMIT;"))

View file

@ -2562,19 +2562,21 @@ SELECT store_path FROM derivation_source_files WHERE id = $1"
(define guix-revision-id-promise (define guix-revision-id-promise
(fibers-delay (fibers-delay
(lambda () (lambda ()
(retry-on-missing-store-item (parameterize
(lambda () ((%postgresql-in-transaction? #f))
(let ((guix-source (retry-on-missing-store-item
channel-derivations-by-system (lambda ()
(fibers-force channel-derivations-by-system-promise))) (let ((guix-source
(load-channel-instances call-with-utility-thread channel-derivations-by-system
read-derivations/fiberized (fibers-force channel-derivations-by-system-promise)))
derivation-ids-hash-table (load-channel-instances call-with-utility-thread
git-repository-id commit read-derivations/fiberized
channel-derivations-by-system))) derivation-ids-hash-table
#:on-exception git-repository-id commit
(lambda () channel-derivations-by-system)))
(fibers-promise-reset channel-derivations-by-system-promise)))))) #:on-exception
(lambda ()
(fibers-promise-reset channel-derivations-by-system-promise)))))))
;; Prompt getting the guix-revision-id as soon as possible ;; Prompt getting the guix-revision-id as soon as possible
(spawn-fiber (spawn-fiber
@ -3032,95 +3034,98 @@ SKIP LOCKED")
(make-channel)) (make-channel))
(define result (define result
(with-postgresql-connection (parameterize
(simple-format #f "load-new-guix-revision ~A" id) ;; Mimic the behaviour of with-postgresql-transaction
(lambda (conn) ((%postgresql-in-transaction? #t))
;; Fix the hash encoding of derivation_output_details. This'll only run (with-postgresql-connection
;; once on any given database, but is kept here just to make sure any (simple-format #f "load-new-guix-revision ~A" id)
;; instances have the data updated. (lambda (conn)
(fix-derivation-output-details-hash-encoding conn) ;; Fix the hash encoding of derivation_output_details. This'll only run
;; once on any given database, but is kept here just to make sure any
;; instances have the data updated.
(fix-derivation-output-details-hash-encoding conn)
(add-hook! after-gc-hook (add-hook! after-gc-hook
(lambda () (lambda ()
(simple-format (current-error-port) (simple-format (current-error-port)
"after gc\n"))) "after gc\n")))
(exec-query conn "BEGIN") (exec-query conn "BEGIN")
(spawn-fiber (spawn-fiber
(lambda () (lambda ()
(while (perform-operation (while (perform-operation
(choice-operation (choice-operation
(wrap-operation (get-operation finished-channel) (wrap-operation (get-operation finished-channel)
(const #f)) (const #f))
(wrap-operation (sleep-operation 20) (wrap-operation (sleep-operation 20)
(const #t)))) (const #t))))
(let ((stats (gc-stats))) (let ((stats (gc-stats)))
(simple-format (simple-format
(current-error-port) (current-error-port)
"process-job heap: ~a MiB used (~a MiB heap)~%" "process-job heap: ~a MiB used (~a MiB heap)~%"
(round (round
(/ (- (assoc-ref stats 'heap-size) (/ (- (assoc-ref stats 'heap-size)
(assoc-ref stats 'heap-free-size)) (assoc-ref stats 'heap-free-size))
(expt 2. 20))) (expt 2. 20)))
(round (round
(/ (assoc-ref stats 'heap-size) (/ (assoc-ref stats 'heap-size)
(expt 2. 20)))))))) (expt 2. 20))))))))
(match (select-job-for-update conn id) (match (select-job-for-update conn id)
(((id commit source git-repository-id)) (((id commit source git-repository-id))
;; With a separate connection, outside of the transaction so the event ;; With a separate connection, outside of the transaction so the event
;; gets persisted regardless. ;; gets persisted regardless.
(with-postgresql-connection (with-postgresql-connection
(simple-format #f "load-new-guix-revision ~A start-event" id) (simple-format #f "load-new-guix-revision ~A start-event" id)
(lambda (start-event-conn) (lambda (start-event-conn)
(record-job-event start-event-conn id "start"))) (record-job-event start-event-conn id "start")))
(simple-format #t "Processing job ~A (commit: ~A, source: ~A)\n\n" (simple-format #t "Processing job ~A (commit: ~A, source: ~A)\n\n"
id commit source) id commit source)
(if (eq? (if (eq?
(with-time-logging (string-append "processing revision " commit) (with-time-logging (string-append "processing revision " commit)
(with-exception-handler (with-exception-handler
(const #f) (const #f)
(lambda () (lambda ()
(with-exception-handler (with-exception-handler
(lambda (exn) (lambda (exn)
(simple-format (current-error-port) (simple-format (current-error-port)
"error: load-new-guix-revision: ~A\n" "error: load-new-guix-revision: ~A\n"
exn) exn)
(print-backtrace-and-exception/knots exn) (print-backtrace-and-exception/knots exn)
(raise-exception exn)) (raise-exception exn))
(lambda () (lambda ()
(load-new-guix-revision (load-new-guix-revision
conn conn
git-repository-id git-repository-id
commit commit
#:skip-system-tests? #t #:skip-system-tests? #t
#:extra-inferior-environment-variables #:extra-inferior-environment-variables
extra-inferior-environment-variables extra-inferior-environment-variables
#:ignore-systems ignore-systems #:ignore-systems ignore-systems
#:ignore-targets ignore-targets #:ignore-targets ignore-targets
#:parallelism parallelism)))) #:parallelism parallelism))))
#:unwind? #t)) #:unwind? #t))
#t) #t)
(begin (begin
(record-job-succeeded conn id) (record-job-succeeded conn id)
(record-job-event conn id "success") (record-job-event conn id "success")
(exec-query conn "COMMIT") (exec-query conn "COMMIT")
#t) #t)
(begin (begin
(exec-query conn "ROLLBACK") (exec-query conn "ROLLBACK")
(record-job-event conn id "failure") (record-job-event conn id "failure")
#f))) #f)))
(() (()
(exec-query conn "ROLLBACK") (exec-query conn "ROLLBACK")
(simple-format #t "job ~A not found to be processed\n" (simple-format #t "job ~A not found to be processed\n"
id)))))) id)))))))
(when result (when result
(fibers-parallel (fibers-parallel