aboutsummaryrefslogtreecommitdiff
path: root/src/connection.lisp
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2021-08-29 12:19:25 -0700
committerSean Whitton <spwhitton@spwhitton.name>2021-08-31 15:55:26 -0700
commit98b727ae3d20a3447288254f421d9524ef8e6548 (patch)
tree4fa54e7aa7220a83f8501c11e6de33ce41b630bf /src/connection.lisp
parent1f12dfda4aeb6d08af454d60caa5985b2bd5b1ba (diff)
downloadconsfigurator-98b727ae3d20a3447288254f421d9524ef8e6548.tar.gz
add CONNECTION-READFILE-AND-REMOVE to improve RUN performance
Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
Diffstat (limited to 'src/connection.lisp')
-rw-r--r--src/connection.lisp48
1 files changed, 37 insertions, 11 deletions
diff --git a/src/connection.lisp b/src/connection.lisp
index 9c1a291..99d4137 100644
--- a/src/connection.lisp
+++ b/src/connection.lisp
@@ -106,6 +106,23 @@ error condition just because EXIT is non-zero."))
(let ((*connection* (slot-value connection 'parent)))
(call-next-method)))
+(defgeneric connection-readfile-and-remove (connection path)
+ (:documentation "As READFILE and then delete the file.
+
+For some connection types, when latency is high, combining these two
+operations is noticeably faster than doing one after the other. For every use
+of RUN we read and delete the file containing the command's stdout, so the
+time savings add up."))
+
+(defmethod connection-readfile-and-remove
+ :around ((connection connection) path)
+ (let ((*connection* (slot-value connection 'parent)))
+ (call-next-method)))
+
+(defmethod connection-readfile-and-remove ((connection connection) path)
+ (prog1 (connection-readfile connection path)
+ (connection-run connection (strcat "rm " (escape-sh-token path)) nil)))
+
;; only functional difference between WRITEFILE and UPLOAD is what args they
;; take: a string vs. a path. for a given connection type, they may have same
;; or different implementations.
@@ -430,17 +447,26 @@ Keyword arguments accepted:
Returns command's stdout, stderr and exit code, unless :FOR-EXIT, in which
case return only the exit code."
(%process-run-args
- (with-remote-temporary-file (stdout)
- (setq cmd (format nil "( ~A ) >~A" cmd stdout))
- (informat 4 "~&RUN ~A" cmd)
- (multiple-value-bind (err exit)
- (connection-run *connection* cmd input)
- (let ((out (readfile stdout)))
- (when inform (informat 1 "~& % ~A~%~{ ~A~%~}" cmd (lines out)))
- (if (or may-fail (= exit 0))
- (if for-exit exit (values out err exit))
- (error 'run-failed
- :cmd cmd :stdout out :stderr err :exit-code exit)))))))
+ (let ((stdout (mktemp)))
+ (handler-bind
+ ((serious-condition
+ (lambda (c)
+ (declare (ignore c))
+ (connection-run
+ *connection*
+ (format nil "rm -f ~A" (escape-sh-token stdout))
+ nil))))
+ (setq cmd (format nil "( ~A ) >~A" cmd stdout))
+ (informat 4 "~&RUN ~A" cmd)
+ (multiple-value-bind (err exit)
+ (connection-run *connection* cmd input)
+ (let ((out (connection-readfile-and-remove *connection* stdout)))
+ (when inform
+ (informat 1 "~& % ~A~%~{ ~A~%~}" cmd (lines out)))
+ (if (or may-fail (= exit 0))
+ (if for-exit exit (values out err exit))
+ (error 'run-failed
+ :cmd cmd :stdout out :stderr err :exit-code exit))))))))
(defun mrun (&rest args)
"Like RUN but don't separate stdout and stderr (\"m\" for \"merged\"; note