Index: db.scm ================================================================== --- db.scm +++ db.scm @@ -11,14 +11,14 @@ ;;====================================================================== ;; Database access ;;====================================================================== -(require-extension (srfi 18) extras tcp rpc) -(import (prefix rpc rpc:)) +(require-extension (srfi 18) extras tcp) ;; rpc) +;; (import (prefix rpc rpc:)) -(use sqlite3 srfi-1 posix regex regex-case srfi-69 csv-xml s11n) +(use sqlite3 srfi-1 posix regex regex-case srfi-69 csv-xml s11n md5 message-digest) (import (prefix sqlite3 sqlite3:)) (use zmq) (declare (unit db)) @@ -1181,30 +1181,33 @@ (let ((res (proc))) (set! *client-non-blocking-mode* #f) res)) ;; params = 'target cached remparams -(define (cdb:client-call zmq-socket . params) - (debug:print-info 11 "cdb:client-call zmq-socket=" zmq-socket " params=" params) - (let ((zdat (db:obj->string params)) ;; (with-output-to-string (lambda ()(serialize params)))) - (res #f)) - ;; (signal-mask! signal/int) - (set! *received-response* #f) - (send-message zmq-socket zdat) - ;; (signal-unmask! signal/int) - (set! res (db:string->obj (if *client-non-blocking-mode* - (receive-message* zmq-socket) - (receive-message zmq-socket)))) - (set! *received-response* #t) - (debug:print-info 11 "zmq-socket " (car params) " res=" res) - res)) +(define (cdb:client-call zmq-sockets . params) + (debug:print-info 11 "cdb:client-call zmq-sockets=" zmq-sockets " params=" params) + (let* ((push-socket (vector-ref zmq-sockets 0)) + (sub-socket (vector-ref zmq-sockets 1)) + (query-id (conc (server:get-client-signature) "-" (message-digest-string (md5-primitive) (conc params)))) + (zdat (db:obj->string (vector query-id params))) ;; (with-output-to-string (lambda ()(serialize params)))) + (res #f) + (get-res (lambda () + (db:string->obj (if *client-non-blocking-mode* + (receive-message* sub-socket) + (receive-message sub-socket)))))) + (send-message push-socket zdat) + (let loop ((res (get-res))) + (if res res + (begin + (thread-sleep! 0.5) + (get-res)))))) (define (cdb:set-verbosity zmq-socket val) (cdb:client-call zmq-socket 'set-verbosity #f val)) -(define (cdb:login zmq-socket keyval signature) - (cdb:client-call zmq-socket 'login #t keyval megatest-version signature)) +(define (cdb:login zmq-sockets keyval signature) + (cdb:client-call zmq-sockets 'login #t keyval megatest-version signature)) (define (cdb:logout zmq-socket keyval signature) (cdb:client-call zmq-socket 'logout #t keyval signature)) (define (cdb:num-clients zmq-socket) Index: megatest.scm ================================================================== --- megatest.scm +++ megatest.scm @@ -97,12 +97,11 @@ -env2file fname : write the environment to fname.csh and fname.sh -setvars VAR1=val1,VAR2=val2 : Add environment variables to a run NB// these are overwritten by values set in config files. -server -|hostname : start the server (reduces contention on megatest.db), use - to automatically figure out hostname - -listservers : list the servers - -killserver host:port|pid : kill server specified by host:port or pid + -list-servers : list the servers -repl : start a repl (useful for extending megatest) Spreadsheet generation -extract-ods fname.ods : extract an open document spreadsheet from the database -pathmod path : insert path, i.e. path/runame/itempath/logfile.html @@ -121,10 +120,11 @@ Called as " (string-intersperse (argv) " ") " Built from " megatest-fossil-hash )) ;; -gui : start a gui interface ;; -config fname : override the runconfig file with fname +;; -kill-server host:port|pid : kill server specified by host:port or pid ;; process args (define remargs (args:get-args (argv) (list "-runtests" ;; run a specific test @@ -157,11 +157,11 @@ ":expected" ":tol" ":units" ;; misc "-server" - "-killserver" + "-kill-server" "-port" "-extract-ods" "-pathmod" "-env2file" "-setvars" @@ -184,11 +184,11 @@ ;; misc "-archive" "-repl" "-lock" "-unlock" - "-listservers" + "-list-servers" ;; queries "-test-paths" ;; get path(s) to a test, ordered by youngest first "-runall" ;; run all tests "-remove-runs" @@ -270,52 +270,57 @@ (if (args:get-arg "-server") (begin (debug:print 1 "Launching server...") (server:launch))) -(if (or (args:get-arg "-listservers") - (args:get-arg "-killserver")) +(if (args:get-arg "-list-servers") + ;; (args:get-arg "-kill-server")) (let ((tl (setup-for-run))) (if tl (let ((servers (open-run-close tasks:get-all-servers tasks:open-db)) - (fmtstr "~5a~8a~8a~20a~20a~10a~20a~10a~10a\n") + (fmtstr "~5a~8a~8a~20a~20a~10a~10a~20a~10a~10a\n") (servers-to-kill '())) - (format #t fmtstr "Id" "MTver" "Pid" "Host" "Interface" "Port" "Time" "Priority" "State") - (format #t fmtstr "==" "=====" "===" "====" "=========" "====" "====" "========" "=====") + (format #t fmtstr "Id" "MTver" "Pid" "Host" "Interface" "OutPort" "InPort" "Time" "LastBeat" "State") + (format #t fmtstr "==" "=====" "===" "====" "=========" "=======" "======" "====" "========" "=====") (for-each (lambda (server) - (let* ((killinfo (args:get-arg "-killserver")) - (khost-port (if killinfo (if (substring-index ":" killinfo)(string-split ":") #f) #f)) - (kpid (if killinfo (if (substring-index ":" killinfo) #f (string->number killinfo)) #f)) + (let* (;; (killinfo (args:get-arg "-kill-server")) + ;; (khost-port (if killinfo (if (substring-index ":" killinfo)(string-split ":") #f) #f)) + ;; (kpid (if killinfo (if (substring-index ":" killinfo) #f (string->number killinfo)) #f)) (id (vector-ref server 0)) (pid (vector-ref server 1)) (hostname (vector-ref server 2)) (interface (vector-ref server 3)) - (port (vector-ref server 4)) - (start-time (vector-ref server 5)) - (priority (vector-ref server 6)) - (state (vector-ref server 7)) - (mt-ver (vector-ref server 8)) - (status (open-run-close tasks:server-alive? tasks:open-db #f hostname: hostname port: port)) + (pullport (vector-ref server 4)) + (pubport (vector-ref server 5)) + (start-time (vector-ref server 6)) + (priority (vector-ref server 7)) + (state (vector-ref server 8)) + (mt-ver (vector-ref server 9)) + (last-update (vector-ref server 10)) ;; (open-run-close tasks:server-alive? tasks:open-db #f hostname: hostname port: port)) (killed #f) - (zmq-socket (if status (server:client-connect hostname port) #f))) + (status (< last-update 20))) + ;; (zmq-sockets (if status (server:client-connect hostname port) #f))) ;; no need to login as status of #t indicates we are connecting to correct ;; server - (if (not status) ;; no point in keeping dead records in the db - (open-run-close tasks:server-deregister tasks:open-db hostname port: port pid: pid)) - - (if (and khost-port ;; kill by host/port - (equal? hostname (car khost-port)) - (equal? port (string->number (cadr khost-port)))) - (tasks:kill-server status hostname port pid)) - - (if (and kpid - (equal? hostname (get-host-name)) - (equal? kpid pid)) ;;; YEP, ALL WITH PID WILL BE KILLED!!! - (tasks:kill-server status hostname #f pid)) - - (format #t fmtstr id mt-ver pid hostname interface port start-time priority + (if (equal? state "dead") + (if (> last-update (* 25 60 60)) ;; keep records around for slighly over a day. + (open-run-close tasks:server-deregister tasks:open-db hostname pullport: pullport pid: pid action: 'delete)) + (if (> last-update 20) ;; Mark as dead if not updated in last 20 seconds + (open-run-close tasks:server-deregister tasks:open-db hostname pullport: pullport pid: pid))) + +;; (if (and khost-port ;; kill by host/port +;; (equal? hostname (car khost-port)) +;; (equal? port (string->number (cadr khost-port)))) +;; (tasks:kill-server status hostname port pid)) +;; +;; (if (and kpid +;; (equal? hostname (get-host-name)) +;; (equal? kpid pid)) ;;; YEP, ALL WITH PID WILL BE KILLED!!! +;; (tasks:kill-server status hostname #f pid)) +;; + (format #t fmtstr id mt-ver pid hostname interface pullport pubport start-time last-update (if status "alive" "dead")))) servers) (debug:print-info 1 "Done with listservers") (set! *didsomething* #t) (exit) ;; must do, would have to add checks to many/all calls below @@ -323,11 +328,11 @@ (exit))) ;; if not list or kill then start a client (if appropriate) (if (or (args-defined? "-h" "-version" "-gen-megatest-area" "-gen-megatest-test") (eq? (length (hash-table-keys args:arg-hash)) 0)) (debug:print-info 1 "Server connection not needed") - + (server:client-launch))) ;;====================================================================== ;; Remove old run(s) ;;====================================================================== Index: server.scm ================================================================== --- server.scm +++ server.scm @@ -6,12 +6,12 @@ ;; ;; This program is distributed WITHOUT ANY WARRANTY; without even the ;; implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ;; PURPOSE. -(require-extension (srfi 18) extras tcp rpc s11n) -(import (prefix rpc rpc:)) +(require-extension (srfi 18) extras tcp s11n) +;; (import (prefix rpc rpc:)) (use sqlite3 srfi-1 posix regex regex-case srfi-69 hostinfo md5 message-digest) (import (prefix sqlite3 sqlite3:)) (use zmq) @@ -23,55 +23,98 @@ (declare (uses tests)) (declare (uses tasks)) ;; tasks are where stuff is maintained about what is running. (include "common_records.scm") (include "db_records.scm") + +;; Transition to pub --> sub with pull <-- push +;; +;; 1. client sends request to server via push to the pull port +;; 2. server puts request in queue or processes immediately as appropriate +;; 3. server puts responses from completed requests into pub port +;; +;; TODO +;; +;; Done Tested +;; [x] [ ] 1. Add columns pullport pubport to servers table +;; [x] [ ] 2. Add rm of monitor.db if older than 11/12/2012 +;; [x] [ ] 3. Add create of pullport and pubport with finding of available ports +;; [x] [ ] 4. Add client compose of request +;; [x] [ ] - name of client: testname/itempath-test_id-hostname +;; [x] [ ] - name of request: callname, params +;; [x] [ ] - request key: f(clientname, callname, params) +;; [ ] [ ] 5. Add processing of subscription hits +;; [ ] [ ] - done when get key +;; [ ] [ ] - return results +;; [ ] [ ] 6. Add timeout processing +;; [ ] [ ] - after 60 seconds +;; [ ] [ ] i. check server alive, connect to new if necessary +;; [ ] [ ] ii. resend request +;; [ ] [ ] 7. Turn self ping back on (define (server:make-server-url hostport) (if (not hostport) #f (conc "tcp://" (car hostport) ":" (cadr hostport)))) (define *server-loop-heart-beat* (current-seconds)) (define *heartbeat-mutex* (make-mutex)) -(define (server:self-ping iface port) - (let ((zsocket (server:client-connect iface port))) - (let loop () - (thread-sleep! 2) - (cdb:client-call zsocket 'ping #t) - (debug:print 4 "server:self-ping - I'm alive on " iface ":" port "!") - (mutex-lock! *heartbeat-mutex*) - (set! *server-loop-heart-beat* (current-seconds)) - (mutex-unlock! *heartbeat-mutex*) - (loop)))) - +;; (define (server:self-ping server-info) +;; ;; server-info: server-id interface pullport pubport +;; (let ((iface (list-ref server-info 1)) +;; (pullport (list-ref server-info 2)) +;; (pubport (list-ref server-info 3))) +;; (server:client-connect iface pullport pubport) +;; (let loop () +;; (thread-sleep! 2) +;; (cdb:client-call *runremote* 'ping #t) +;; (debug:print 4 "server:self-ping - I'm alive on " iface ":" pullport "/" pubport "!") +;; (mutex-lock! *heartbeat-mutex*) +;; (set! *server-loop-heart-beat* (current-seconds)) +;; (mutex-unlock! *heartbeat-mutex*) +;; (loop)))) + +(define-inline (zmqsock:get-pub dat)(vector-ref dat 0)) +(define-inline (zmqsock:get-pull dat)(vector-ref dat 1)) +(define-inline (zmqsock:set-pub! dat s)(vector-set! dat s 0)) +(define-inline (zmqsock:set-pull! dat s)(vector-set! dat s 0)) + (define (server:run hostn) (debug:print 0 "Attempting to start the server ...") (if (not *toppath*) (if (not (setup-for-run)) (begin (debug:print 0 "ERROR: cannot find megatest.config, cannot start server, exiting") (exit)))) - (let* ((zmq-socket #f) - (zmq-socket-dat #f) - (iface (if (string=? "-" hostn) - "*" ;; (get-host-name) - hostn)) - (hostname (get-host-name)) - (ipaddrstr (let ((ipstr (if (string=? "-" hostn) - (string-intersperse (map number->string (u8vector->list (hostname->ip hostname))) ".") - #f))) - (if ipstr ipstr hostname))) - (actual-port #f)) - ;; (set! zmq-socket (server:find-free-port-and-open iface zmq-socket 5555 0)) - (set! zmq-socket-dat (server:find-free-port-and-open ipaddrstr zmq-socket (if (args:get-arg "-port") - (string->number (args:get-arg "-port")) - (+ 5000 (random 1001))) - 0)) - (set! zmq-socket (cadr zmq-socket-dat)) - (set! actual-port (caddr zmq-socket-dat)) + (let* ((zmq-sdat1 #f) + (zmq-sdat2 #f) + (pull-socket #f) + (pub-socket #f) + (p1 #f) + (p2 #f) + (zmq-sockets-dat #f) + (iface (if (string=? "-" hostn) + "*" ;; (get-host-name) + hostn)) + (hostname (get-host-name)) + (ipaddrstr (let ((ipstr (if (string=? "-" hostn) + (string-intersperse (map number->string (u8vector->list (hostname->ip hostname))) ".") + #f))) + (if ipstr ipstr hostname)))) + (set! zmq-sockets-dat (server:setup-ports ipaddrstr (if (args:get-arg "-port") + (string->number (args:get-arg "-port")) + (+ 5000 (random 1001))))) + + (set! zmq-sdat1 (car zmq-sockets-dat)) + (set! pull-socket (cadr zmq-sdat1)) ;; (iface s port) + (set! p1 (caddr zmq-sdat1)) + + (set! zmq-sdat2 (cadr zmq-sockets-dat)) + (set! pub-socket (cadr zmq-sdat2)) + (set! p2 (caddr zmq-sdat2)) + (set! *cache-on* #t) ;; (set! th1 (make-thread (lambda () ;; (server:self-ping ipaddrstr actual-port)))) ;; (thread-start! th1) @@ -79,11 +122,11 @@ ;; what to do when we quit ;; (on-exit (lambda () (if (and *toppath* *server-info*) (begin - (open-run-close tasks:server-deregister-self tasks:open-db ipaddrstr)) + (open-run-close tasks:server-deregister-self tasks:open-db ipaddrstr p1 p2)) (let loop () (let ((queue-len 0)) (thread-sleep! (random 5)) (mutex-lock! *incoming-mutex*) (set! queue-len (length *incoming-data*)) @@ -94,25 +137,21 @@ (loop)))))))) ;; The heavy lifting ;; (let loop () - ;; ;; Ugly yuk. - ;; (mutex-lock! *incoming-mutex*) - ;; (set! *server-loop-heart-beat* (list 'waiting (current-seconds))) - ;; (mutex-unlock! *incoming-mutex*) - (let* ((rawmsg (receive-message* zmq-socket)) + ;; (print "GOT HERE EH?") + (let* ((rawmsg (receive-message* pull-socket)) (params (db:string->obj rawmsg)) ;; (with-input-from-string rawmsg (lambda ()(deserialize)))) (res #f)) - ;;; Ugly yuk. - ;; (mutex-lock! *incoming-mutex*) - ;; (set! *server-loop-heart-beat* (list 'working (current-seconds))) - ;; (mutex-unlock! *incoming-mutex*) (debug:print-info 12 "server=> received params=" params) (set! res (cdb:cached-access params)) (debug:print-info 12 "server=> processed res=" res) - (send-message zmq-socket (db:obj->string res)) + + ;; need address here + ;; + ;; (send-message zmq-socket (db:obj->string res)) (if (not *time-to-exit*) (loop) (begin (open-run-close tasks:server-deregister-self tasks:open-db #f) (db:write-cached-data) @@ -137,24 +176,27 @@ (server-loop-heartbeat #f) (server-info #f) (pulse 0)) ;; BUG add a wait on server alive here!! ;; ;; Ugly yuk. - (mutex-lock! *heartbeat-mutex*) - (set! server-loop-heartbeat *server-loop-heart-beat*) - (set! server-info *server-info*) - (mutex-unlock! *heartbeat-mutex*) + ;; == (mutex-lock! *heartbeat-mutex*) + ;; == (set! server-loop-heartbeat *server-loop-heart-beat*) + ;; == (set! server-info *server-info*) + ;; == (mutex-unlock! *heartbeat-mutex*) ;; The logic here is that if the server loop gets stuck blocked in working ;; we don't want to update our heartbeat - (set! pulse (- (current-seconds) server-loop-heartbeat)) - (debug:print-info 2 "Heartbeat period is " pulse " seconds on " (cadr server-info) ":" (caddr server-info) ", last db access is " (- (current-seconds) *last-db-access*) " seconds ago") - (if (> pulse 15) ;; must stay less than 10 seconds - (begin - (open-run-close tasks:server-deregister tasks:open-db (cadr server-info) port: (caddr server-info)) - (debug:print 0 "ERROR: Heartbeat failed, committing servercide") - (exit)) - (open-run-close tasks:server-update-heartbeat tasks:open-db (car server-info))) + ;; == (set! pulse (- (current-seconds) server-loop-heartbeat)) + ;; == (debug:print-info 2 "Heartbeat period is " pulse " seconds on " (cadr server-info) ":" (caddr server-info) ", last db access is " (- (current-seconds) *last-db-access*) " seconds ago") + ;; == (if (> pulse 15) ;; must stay less than 10 seconds + ;; == (begin + ;; == (open-run-close tasks:server-deregister tasks:open-db (cadr server-info) pullport: (caddr server-info)) + ;; == (debug:print 0 "ERROR: Heartbeat failed, committing servercide") + ;; == (exit)) + + ;; NOTE: Get rid of this mechanism! It really is not needed... + (open-run-close tasks:server-update-heartbeat tasks:open-db (car server-info)) + ;; (if ;; (or (> numrunning 0) ;; stay alive for two days after last access (if (> (+ *last-db-access* ;; (* 48 60 60) ;; 48 hrs ;; 60 ;; one minute (* 60 60) ;; one hour @@ -172,12 +214,12 @@ (thread-sleep! 1) (debug:print-info 0 "Max cached queries was " *max-cache-size*) (debug:print-info 0 "Server shutdown complete. Exiting") (exit))))))) -(define (server:find-free-port-and-open iface s port #!key (trynum 50)) - (let ((s (if s s (make-socket 'rep))) +(define (server:find-free-port-and-open iface s port stype #!key (trynum 50)) + (let ((s (if s s (make-socket stype))) (p (if (number? port) port 5555)) (old-handler (current-exception-handler))) (handle-exceptions exn (begin @@ -186,20 +228,28 @@ ;; (old-handler) ;; (print-call-chain) (if (> trynum 0) (server:find-free-port-and-open iface s (+ p 1) trynum: (- trynum 1)) (debug:print-info 0 "Tried ports up to " p - " but all were in use. Please try a different port range by starting the server with parameter \" -port N\" where N is the starting port number to use"))) + " but all were in use. Please try a different port range by starting the server with parameter \" -port N\" where N is the starting port number to use")) + (exit)) ;; To exit or not? That is the question. (let ((zmq-url (conc "tcp://" iface ":" p))) - (print "Trying to start server on " zmq-url) + (debug:print 0 "Trying to start server on " zmq-url) (bind-socket s zmq-url) - (set! *runremote* #f) - (debug:print 0 "Server started on " zmq-url) - (mutex-lock! *heartbeat-mutex*) - (set! *server-info* (open-run-close tasks:server-register tasks:open-db (current-process-id) iface p 0 'live)) - (mutex-unlock! *heartbeat-mutex*) (list iface s port))))) + +(define (server:setup-ports ipaddrstr startport) + (let* ((s1 (server:find-free-port-and-open ipaddrstr #f startport 'pub)) + (p1 (caddr s1)) + (s2 (server:find-free-port-and-open ipaddrstr #f (+ 1 (if p1 p1 (+ startport 1))) 'pull)) + (p2 (caddr s2))) + (set! *runremote* #f) + (debug:print 0 "Server started on " ipaddrstr " ports " p1 " and " p2) + (mutex-lock! *heartbeat-mutex*) + (set! *server-info* (open-run-close tasks:server-register tasks:open-db (current-process-id) ipaddrstr p1 p2 0 'live)) + (mutex-unlock! *heartbeat-mutex*) + (list s1 s2))) (define (server:mk-signature) (message-digest-string (md5-primitive) (with-output-to-string (lambda () @@ -211,32 +261,53 @@ (let ((sig (server:mk-signature))) (set! *my-client-signature* sig) *my-client-signature*))) ;; -(define (server:client-connect iface port #!key (context #f)) +(define (server:client-socket-connect iface port #!key (context #f)(type 'req)(subscriptions '())) (debug:print-info 3 "client-connect " iface ":" port) (let ((connect-ok #f) (zmq-socket (if context - (make-socket 'req context) - (make-socket 'req))) + (make-socket type context) + (make-socket type))) (conurl (server:make-server-url (list iface port)))) (if (socket? zmq-socket) (begin + ;; first apply subscriptions + (for-each (lambda (subscription) + (socket-options-set! zmq-socket 'subscribe subscription)) + subscriptions) (connect-socket zmq-socket conurl) zmq-socket) #f))) - -(define (server:client-login zmq-socket) - (cdb:login zmq-socket *toppath* (server:get-client-signature))) +(define (server:client-login zmq-sockets) + (cdb:login zmq-sockets *toppath* (server:get-client-signature))) (define (server:client-logout zmq-socket) (let ((ok (and (socket? zmq-socket) (cdb:logout zmq-socket *toppath* (server:get-client-signature))))) ;; (close-socket zmq-socket) ok)) + +(define (server:client-connect iface pullport pubport) + (let* ((push-socket (server:client-socket-connect iface pullport 'push)) + (sub-socket (server:client-socket-connect iface pubport 'sub + subscriptions: (list (server:get-client-signature) "all"))) + (zmq-sockets (vector push-socket sub-socket)) + (login-res #f)) + (set! login-res (server:client-login zmq-sockets)) + (if (and (not (null? login-res)) + (car login-res)) + (begin + (debug:print-info 2 "Logged in and connected to " iface ":" pullport "/" pubport ".") + (set! *runremote* zmq-socket) + #t) + (begin + (debug:print-info 2 "Failed to login or connect to " conurl) + (set! *runremote* #f) + #f)))) ;; Do all the connection work, start a server if not already running (define (server:client-setup #!key (numtries 50)) (if (not *toppath*) (if (not (setup-for-run)) @@ -243,47 +314,36 @@ (begin (debug:print 0 "ERROR: failed to find megatest.config, exiting") (exit)))) (let ((hostinfo (open-run-close tasks:get-best-server tasks:open-db))) (if hostinfo - (let ((host (car hostinfo)) - (iface (cadr hostinfo)) - (port (caddr hostinfo))) + (let ((host (list-ref hostinfo 0)) + (iface (list-ref hostinfo 1)) + (pullport (list-ref hostinfo 2)) + (pubport (list-ref hostinfo 3))) (debug:print-info 2 "Setting up to connect to " hostinfo) - (handle-exceptions - exn + ;;(handle-exceptions + ;; exn (begin ;; something went wrong in connecting to the server. In this scenario it is ok ;; to try again (debug:print 0 "ERROR: Failed to open a connection to the server at: " hostinfo) (debug:print 0 " EXCEPTION: " ((condition-property-accessor 'exn 'message) exn)) (debug:print 0 " perhaps jobs killed with -9? Removing server records") - (open-run-close tasks:server-deregister tasks:open-db host port: port) + (open-run-close tasks:server-deregister tasks:open-db host pullport: pullport) (server:client-setup (- numtries 1)) #f) - (let* ((zmq-socket (server:client-connect iface port)) - (login-res (server:client-login zmq-socket)) - (connect-ok (if (null? login-res) #f (car login-res))) - (conurl (server:make-server-url (list iface port)))) - (if connect-ok - (begin - (debug:print-info 2 "Logged in and connected to " conurl) - (set! *runremote* zmq-socket) - #t) - (begin - (debug:print-info 2 "Failed to login or connect to " conurl) - (set! *runremote* #f) - #f))))) - (if (> numtries 0) - (let ((exe (car (argv)))) - (debug:print-info 1 "No server available, attempting to start one...") - (process-run exe (list "-server" "-" "-debug" (conc *verbosity*))) - (sleep 5) ;; give server time to start - ;; we are starting a server, do not try again! That can lead to - ;; recursively starting many processes!!! - (server:client-setup numtries: 0)) - (debug:print-info 1 "Too many attempts, giving up"))))) + (server:client-connect iface pullport pubport))))) + ;; (if (> numtries 0) + ;; (let ((exe (car (argv)))) + ;; (debug:print-info 1 "No server available, attempting to start one...") + ;; (process-run exe (list "-server" "-" "-debug" (conc *verbosity*))) + ;; (sleep 5) ;; give server time to start + ;; ;; we are starting a server, do not try again! That can lead to + ;; ;; recursively starting many processes!!! + ;; (server:client-setup numtries: 0)) + ;; (debug:print-info 1 "Too many attempts, giving up"))))) ;; all routes though here end in exit ... (define (server:launch) (if (not *toppath*) (if (not (setup-for-run)) @@ -293,28 +353,30 @@ (debug:print-info 1 "Starting the standalone server") (let ((hostinfo (open-run-close tasks:get-best-server tasks:open-db))) (if hostinfo (debug:print-info 1 "NOT starting new server, one is already running on " (car hostinfo) ":" (cadr hostinfo)) (if *toppath* - (let* ((th1 (make-thread (lambda () - (let ((server-info #f)) - ;; wait for the server to be online and available - (let loop () - (debug:print-info 1 "Waiting for the server to come online before starting heartbeat") - (thread-sleep! 2) - (mutex-lock! *heartbeat-mutex*) - (set! server-info *server-info* ) - (mutex-unlock! *heartbeat-mutex*) - (if (not server-info)(loop))) - (debug:print 1 "Server alive, starting self-ping") - (server:self-ping (cadr server-info)(caddr server-info)))) "Self ping")) + (let* (;; (th1 (make-thread (lambda () + ;; (let ((server-info #f)) + ;; ;; wait for the server to be online and available + ;; (let loop () + ;; (debug:print-info 1 "Waiting for the server to come online before starting heartbeat") + ;; (thread-sleep! 2) + ;; (mutex-lock! *heartbeat-mutex*) + ;; (set! server-info *server-info* ) + ;; (mutex-unlock! *heartbeat-mutex*) + ;; (if (not server-info)(loop))) + ;; (debug:print 1 "Server alive, starting self-ping") + ;; (server:self-ping server-info) + ;; )) + ;; "Self ping")) (th2 (make-thread (lambda () (server:run (args:get-arg "-server"))) "Server run")) (th3 (make-thread (lambda () (server:keep-running)) "Keep running"))) (set! *client-non-blocking-mode* #t) - (thread-start! th1) + ;; (thread-start! th1) (thread-start! th2) (thread-start! th3) (set! *didsomething* #t) (thread-join! th3)) (debug:print 0 "ERROR: Failed to setup for megatest"))) @@ -327,11 +389,11 @@ (let ((th1 (make-thread (lambda () (if (not *received-response*) (receive-message* *runremote*))) ;; flush out last call if applicable "eat response")) (th2 (make-thread (lambda () - (debug:print 0 "ERROR: Received ^C, attempting clean exit.") + (debug:print 0 "ERROR: Received ^C, attempting clean exit. Please be patient and wait a few seconds before hitting ^C again.") (thread-sleep! 3) ;; give the flush three seconds to do it's stuff (debug:print 0 " Done.") (exit 4)) "exit on ^C timer"))) (thread-start! th2) Index: tasks.scm ================================================================== --- tasks.scm +++ tasks.scm @@ -22,11 +22,19 @@ ;; Tasks db ;;====================================================================== (define (tasks:open-db) (let* ((dbpath (conc *toppath* "/monitor.db")) - (exists (file-exists? dbpath)) + (exists (if (file-exists? dbpath) + ;; BUGGISHNESS: Remove this code in six months. Today is 11/13/2012 + (if (< (file-change-time dbpath) 1352851396.0) + (begin + (debug:print 0 "NOTE: removing old db file " dbpath) + (delete-file dbpath) + #f) + #t) + #f)) (mdb (sqlite3:open-database dbpath)) ;; (never-give-up-open-db dbpath)) (handler (make-busy-timeout 36000))) (sqlite3:set-busy-handler! mdb handler) (sqlite3:execute mdb (conc "PRAGMA synchronous = 0;")) (if (not exists) @@ -52,17 +60,18 @@ CONSTRAINT monitors_constraint UNIQUE (pid,hostname));") (sqlite3:execute mdb "CREATE TABLE IF NOT EXISTS servers (id INTEGER PRIMARY KEY, pid INTEGER, interface TEXT, hostname TEXT, - port INTEGER, + pullport INTEGER, + pubport INTEGER, start_time TIMESTAMP, priority INTEGER, state TEXT, mt_version TEXT, heartbeat TIMESTAMP, - CONSTRAINT servers_constraint UNIQUE (pid,hostname,port));") + CONSTRAINT servers_constraint UNIQUE (pid,hostname,pullport,pubport));") (sqlite3:execute mdb "CREATE TABLE IF NOT EXISTS clients (id INTEGER PRIMARY KEY, server_id INTEGER, pid INTEGER, hostname TEXT, cmdline TEXT, @@ -76,54 +85,58 @@ ;;====================================================================== ;; Server and client management ;;====================================================================== ;; state: 'live, 'shutting-down, 'dead -(define (tasks:server-register mdb pid interface port priority state) +(define (tasks:server-register mdb pid interface pullport pubport priority state) (sqlite3:execute mdb - "INSERT OR REPLACE INTO servers (pid,hostname,port,start_time,priority,state,mt_version,heartbeat,interface) VALUES(?,?,?,strftime('%s','now'),?,?,?,strftime('%s','now'),?);" - pid (get-host-name) port priority (conc state) megatest-version interface) + "INSERT OR REPLACE INTO servers (pid,hostname,pullport,pubport,start_time,priority,state,mt_version,heartbeat,interface) + VALUES(?, ?, ?, ?, strftime('%s','now'), ?, ?, ?, strftime('%s','now'),?);" + pid (get-host-name) pullport pubport priority (conc state) megatest-version interface) (list - (tasks:server-get-server-id mdb (get-host-name) port pid) + (tasks:server-get-server-id mdb (get-host-name) pullport pid) interface - port)) + pullport + pubport)) ;; NB// two servers with same pid on different hosts will be removed from the list if pid: is used! -(define (tasks:server-deregister mdb hostname #!key (port #f)(pid #f)) - (debug:print-info 11 "server-deregister " hostname ", port " port ", pid " pid) +(define (tasks:server-deregister mdb hostname #!key (pullport #f)(pid #f)(action 'markdead)) + (debug:print-info 11 "server-deregister " hostname ", pullport " pullport ", pid " pid) (if pid - ;; (sqlite3:execute mdb "DELETE FROM servers WHERE pid=?;" pid) - (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE pid=?;" pid) - (if port - ;; (sqlite3:execute mdb "DELETE FROM servers WHERE hostname=? AND port=?;" hostname port) - (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE hostname=? AND port=?;" hostname port) + (case action + ((delete)(sqlite3:execute mdb "DELETE FROM servers WHERE pid=?;" pid)) + (else (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE pid=?;" pid))) + (if pullport + (case action + ((delete)(sqlite3:execute mdb "DELETE FROM servers WHERE hostname=? AND port=?;" hostname port)) + (else (sqlite3:execute mdb "UPDATE servers SET state='dead' WHERE hostname=? AND pullport=?;" hostname pullport))) (debug:print 0 "ERROR: tasks:server-deregister called with neither pid nor port specified")))) (define (tasks:server-deregister-self mdb hostname) (tasks:server-deregister mdb hostname pid: (current-process-id))) -(define (tasks:server-get-server-id mdb hostname port pid) +(define (tasks:server-get-server-id mdb hostname pullport pid) (let ((res #f)) (sqlite3:for-each-row (lambda (id) (set! res id)) mdb (if (and hostname pid) "SELECT id FROM servers WHERE hostname=? AND pid=?;" - "SELECT id FROM servers WHERE hostname=? AND port=?;") - hostname (if pid pid port)) + "SELECT id FROM servers WHERE hostname=? AND pullport=?;") + hostname (if pid pid pullport)) res)) (define (tasks:server-update-heartbeat mdb server-id) (sqlite3:execute mdb "UPDATE servers SET heartbeat=strftime('%s','now') WHERE id=?;" server-id)) ;; alive servers keep the heartbeat field upto date with seconds every 6 or so seconds -(define (tasks:server-alive? mdb server-id #!key (hostname #f)(port #f)(pid #f)) +(define (tasks:server-alive? mdb server-id #!key (hostname #f)(pullport #f)(pid #f)) (let* ((server-id (if server-id server-id - (tasks:server-get-server-id mdb hostname port pid))) + (tasks:server-get-server-id mdb hostname pullport pid))) (heartbeat-delta 99e9)) (sqlite3:for-each-row (lambda (delta) (set! heartbeat-delta delta)) mdb "SELECT strftime('%s','now')-heartbeat FROM servers WHERE id=?;" server-id) @@ -158,36 +171,46 @@ ;; remove any others. will not necessarily remove all! (define (tasks:get-best-server mdb) (let ((res '()) (best #f)) (sqlite3:for-each-row - (lambda (id hostname interface port pid) - (set! res (cons (list hostname interface port pid) res)) - (debug:print-info 2 "Found existing server " hostname ":" port " registered in db")) + (lambda (id hostname interface pullport pubport pid) + (set! res (cons (list hostname interface pullport pubport pid) res)) + (debug:print-info 2 "Found existing server " hostname ":" pullport " registered in db")) mdb - "SELECT id,hostname,interface,port,pid FROM servers WHERE state='live' AND mt_version=? ORDER BY start_time ASC LIMIT 1;" megatest-version) + "SELECT id,hostname,interface,pullport,pubport,pid FROM servers WHERE state='live' AND mt_version=? ORDER BY start_time ASC LIMIT 1;" megatest-version) ;; (print "res=" res) (if (null? res) #f (let loop ((hed (car res)) (tal (cdr res))) ;; (print "hed=" hed ", tal=" tal) - (let* ((host (car hed)) - (iface (cadr hed)) - (port (caddr hed)) - (pid (cadddr hed)) - (alive (open-run-close tasks:server-alive? tasks:open-db #f hostname: host port: port))) + (let* ((host (list-ref hed 0)) + (iface (list-ref hed 1)) + (pullport (list-ref hed 2)) + (pubport (list-ref hed 3)) + (pid (list-ref hed 4)) + (alive (open-run-close tasks:server-alive? tasks:open-db #f hostname: host pullport: pullport))) (if alive (begin - (debug:print-info 2 "Found an existing, alive, server " host ":" port ".") - (list host iface port)) + (debug:print-info 2 "Found an existing, alive, server " host ", " pullport " and " pubport ".") + (list host iface pullport pubport)) (begin - (debug:print-info 1 "Removing " host ":" port " from server registry as it appears to be dead") - (tasks:kill-server #f host port pid) + (debug:print-info 1 "Marking " host ":" pullport " as dead in server registry.") + (if port + (open-run-close tasks:server-deregister tasks:open-db host pullport: pullport) + (open-run-close tasks:server-deregister tasks:open-db host pid: pid)) (if (null? tal) #f (loop (car tal)(cdr tal)))))))))) +(define (tasks:mark-server hostname pullport pid state) + (if port + (open-run-close tasks:server-deregister tasks:open-db hostname port: port) + (open-run-close tasks:server-deregister tasks:open-db hostname pid: pid))) + + +;; NOTE: NOT PORTED TO WORK WITH pullport/pubport (define (tasks:kill-server status hostname port pid) (debug:print-info 1 "Removing defunct server record for " hostname ":" port) (if port (open-run-close tasks:server-deregister tasks:open-db hostname port: port) (open-run-close tasks:server-deregister tasks:open-db hostname pid: pid)) @@ -212,18 +235,20 @@ (debug:print-info 1 "Sending signal/term to " pid " on " hostname) (process-signal pid signal/term) ;; local machine, send sig term (thread-sleep! 5) ;; give it five seconds to die peacefully then do a brutal kill (process-signal pid signal/kill)) (debug:print 0 "WARNING: Can't kill frozen server on remote host " hostname)))))) + + (define (tasks:get-all-servers mdb) (let ((res '())) (sqlite3:for-each-row - (lambda (id pid hostname interface port start-time priority state mt-version) - (set! res (cons (vector id pid hostname interface port start-time priority state mt-version) res))) + (lambda (id pid hostname interface pullport pubport start-time priority state mt-version last-update) + (set! res (cons (vector id pid hostname interface pullport pubport start-time priority state mt-version last-update) res))) mdb - "SELECT id,pid,hostname,interface,port,start_time,priority,state,mt_version FROM servers ORDER BY start_time DESC;") + "SELECT id,pid,hostname,interface,pullport,pubport,start_time,priority,state,mt_version,strftime('%s','now')-heartbeat AS last_update FROM servers ORDER BY start_time DESC;") res)) ;;====================================================================== ;; Tasks and Task monitors Index: utils/installall.sh ================================================================== --- utils/installall.sh +++ utils/installall.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#! /bin/env bash set -x # Copyright 2007-2010, Matthew Welland. # @@ -11,11 +11,11 @@ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. echo You may need to do the following first: echo sudo apt-get install libreadline-dev -echo sudo apt-get install libwebkitgtk-dev +echo sudo apt-get install libwebkitgtk-dev echo sudo apt-get install libmotif3 -OR- set KTYPE=26g4 echo KTYPE can be 26, 26g4, or 32 echo KTYPE=$KTYPE echo You are using PREFIX=$PREFIX echo You are using proxy="$proxy" @@ -268,11 +268,12 @@ tar xfz ${ZEROMQ}.tar.gz cd ${ZEROMQ} ln -s $PREFIX/include/uuid src # LDFLAGS=-L$PREFIX/lib ./configure --prefix=$PREFIX - ./configure --enable-static --disable-shared --prefix=$PREFIX --with-uuid=$PREFIX LDFLAGS="-L$PREFIX/lib" CPPFLAGS="-fPIC -I$PREFIX/include" LIBS="-lgcc" + ./configure --enable-static --prefix=$PREFIX --with-uuid=$PREFIX LDFLAGS="-L$PREFIX/lib" CPPFLAGS="-fPIC -I$PREFIX/include" LIBS="-lgcc" + # --disable-shared CPPFLAGS="-fPIC # LDFLAGS="-L/usr/lib64 -L$PREFIX/lib" ./configure --enable-static --prefix=$PREFIX make make install CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" chicken-install $PROX zmq # CSC_OPTIONS="-I$PREFIX/include -L$CSCLIBS" chicken-install $PROX -deploy -prefix $DEPLOYTARG zmq