Overview
Comment: | added .server verification against running server ; also touching .server periodically and checking its age to detect and recover from potential server crashes |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | v1.63 |
Files: | files | file ages | folders |
SHA1: |
65358a4d535d0f0b76c2e144e926780f |
User & Date: | bjbarcla on 2017-01-05 11:03:37 |
Other Links: | branch diff | manifest | tags |
Context
2017-01-10
| ||
00:45 | merged in solid fixes for server stability problems check-in: ebafbfa4a1 user: bjbarcla tags: v1.63 | |
2017-01-09
| ||
12:04 | updated to latest v1.63, WIP check-in: 8e63364af0 user: srehman tags: v1.63-configdbsync | |
2017-01-05
| ||
15:43 | addressed stach dump in db:get-run-stats check-in: bd4a33953c user: bjbarcla tags: v1.63-stackdumpfix | |
11:03 | added .server verification against running server ; also touching .server periodically and checking its age to detect and recover from potential server crashes check-in: 65358a4d53 user: bjbarcla tags: v1.63 | |
09:51 | Bumped version to v1.6303 check-in: aa5fb6de80 user: mrwellan tags: v1.63, v1.6303 | |
Changes
Modified common.scm from [4b29489636] to [6953f07d9c].
︙ | ︙ | |||
134 135 136 137 138 139 140 | (define *run-info-cache* (make-hash-table)) ;; run info is stable, no need to reget (define *launch-setup-mutex* (make-mutex)) ;; need to be able to call launch:setup often so mutex it and re-call the real deal only if *toppath* not set (define *homehost-mutex* (make-mutex)) (defstruct remote (hh-dat (common:get-homehost)) ;; homehost record ( addr . hhflag ) | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | (define *run-info-cache* (make-hash-table)) ;; run info is stable, no need to reget (define *launch-setup-mutex* (make-mutex)) ;; need to be able to call launch:setup often so mutex it and re-call the real deal only if *toppath* not set (define *homehost-mutex* (make-mutex)) (defstruct remote (hh-dat (common:get-homehost)) ;; homehost record ( addr . hhflag ) (server-url (if *toppath* (server:read-dotserver->url *toppath*))) ;; (server:check-if-running *toppath*) #f)) (last-server-check 0) ;; last time we checked to see if the server was alive (conndat #f) (transport *transport-type*) (server-timeout (or (server:get-timeout) 100))) ;; default to 100 seconds ;; launching and hosts (defstruct host |
︙ | ︙ |
Modified http-transport.scm from [85b3cef6fd] to [880b0371c4].
︙ | ︙ | |||
397 398 399 400 401 402 403 | (tasks:server-set-state! (db:delay-if-busy tdbdat) server-id "dbprep") ;;(BB> "http-transport: ->dbprep") (thread-sleep! 0.5) ;; give some margin for queries to complete before switching from file based access to server based access (set! *dbstruct-db* (db:setup)) ;; run-id)) (set! server-going #t) (tasks:server-set-state! (db:delay-if-busy tdbdat) server-id "running") ;;(BB> "http-transport: ->running") | | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | (tasks:server-set-state! (db:delay-if-busy tdbdat) server-id "dbprep") ;;(BB> "http-transport: ->dbprep") (thread-sleep! 0.5) ;; give some margin for queries to complete before switching from file based access to server based access (set! *dbstruct-db* (db:setup)) ;; run-id)) (set! server-going #t) (tasks:server-set-state! (db:delay-if-busy tdbdat) server-id "running") ;;(BB> "http-transport: ->running") (server:write-dotserver *toppath* iface port (current-process-id) 'http) (thread-start! *watchdog*) (server:complete-attempt *toppath*)) (begin ;; gotta exit nicely ;;(BB> "http-transport: ->collision") (tasks:server-set-state! (db:delay-if-busy tdbdat) server-id "collision") (http-transport:server-shutdown server-id port)))))) |
︙ | ︙ | |||
426 427 428 429 430 431 432 | (mutex-unlock! *heartbeat-mutex*) (if (or (not (equal? sdat (list iface port))) (not server-id)) (begin (debug:print-info 0 *default-log-port* "interface changed, refreshing iface and port info") (set! iface (car sdat)) | | > > > > > | < | | | | | | | | | | > > | | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | (mutex-unlock! *heartbeat-mutex*) (if (or (not (equal? sdat (list iface port))) (not server-id)) (begin (debug:print-info 0 *default-log-port* "interface changed, refreshing iface and port info") (set! iface (car sdat)) (set! port (cadr sdat)) (server:write-dotserver *toppath* iface port (current-process-id) 'http))) ;; Transfer *db-last-access* to last-access to use in checking that we are still alive (mutex-lock! *heartbeat-mutex*) (set! last-access *db-last-access*) (mutex-unlock! *heartbeat-mutex*) ;; (debug:print 11 *default-log-port* "last-access=" last-access ", server-timeout=" server-timeout) ;; ;; no_traffic, no running tests, if server 0, no running servers ;; ;; (let ((wait-on-running (configf:lookup *configdat* "server" b"wait-on-running"))) ;; wait on running tasks (if not true then exit on time out) ;; (let* ((hrs-since-start (/ (- (current-seconds) server-start-time) 3600)) (adjusted-timeout (if (> hrs-since-start 1) (- server-timeout (inexact->exact (round (* hrs-since-start 60)))) ;; subtract 60 seconds per hour server-timeout))) (if (common:low-noise-print 120 "server timeout") (debug:print-info 0 *default-log-port* "Adjusted server timeout: " adjusted-timeout)) (cond ((not (server:confirm-dotserver *toppath* iface port (current-process-id) 'http)) (debug:print-info 0 *default-log-port* "Server .server file does not exist or contents do not match. Initiate server shutdown.") (http-transport:server-shutdown server-id port)) ((and *server-run* (> (+ last-access server-timeout) (current-seconds))) (if (common:low-noise-print 120 "server continuing") (debug:print-info 0 *default-log-port* "Server continuing, seconds since last db access: " (- (current-seconds) last-access))) ;; ;; Consider implementing some smarts here to re-insert the record or kill self is ;; the db indicates so ;; ;; (if (tasks:server-am-i-the-server? tdb run-id) ;; (tasks:server-set-state! tdb server-id "running")) ;; (loop 0 server-state bad-sync-count (current-milliseconds))) (else (debug:print-info 0 *default-log-port* "Server timeed out. seconds since last db access: " (- (current-seconds) last-access)) (http-transport:server-shutdown server-id port))))))) ;; code cut out from above ;; ;; (condition-case ;; ;; (if (and (member (mutex-state *db-sync-mutex*) '(abandoned not-abandoned)) ;; ;; (> (- (current-seconds) *db-last-sync*) 5)) ;; if not currently being synced nor recently synced ;; (db:sync-touched *dbstruct-db* *run-id* force-sync: #t) ;; usually done in the watchdog, not here. |
︙ | ︙ |
Modified rmt.scm from [c70f311cf0] to [b725604f3b].
︙ | ︙ | |||
29 30 31 32 33 34 35 | ;;====================================================================== ;; S U P P O R T F U N C T I O N S ;;====================================================================== ;; if a server is either running or in the process of starting call client:setup ;; else return #f to let the calling proc know that there is no server available ;; | | | > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | ;;====================================================================== ;; S U P P O R T F U N C T I O N S ;;====================================================================== ;; if a server is either running or in the process of starting call client:setup ;; else return #f to let the calling proc know that there is no server available ;; (define (rmt:get-connection-info areapath) ;; TODO: push areapath down. (let ((cinfo (remote-conndat *runremote*)) (run-id 0)) (if cinfo cinfo (if (tasks:server-running-or-starting? (db:delay-if-busy (tasks:open-db)) run-id) (client:setup run-id) #f)))) (define *send-receive-mutex* (make-mutex)) ;; should have separate mutex per run-id |
︙ | ︙ | |||
112 113 114 115 116 117 118 | ;; ((and (cdr (remote-hh-dat *runremote*)) ;; on homehost ;; (not (member cmd api:read-only-queries))) ;; (mutex-unlock! *rmt-mutex*) ;; (debug:print-info 12 *default-log-port* "rmt:send-receive, case 4.1") ;; (rmt:open-qry-close-locally cmd 0 params)) | | > | | | | < < | | | > > > > | | | | | > > | > > > > > > > > | | > | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | ;; ((and (cdr (remote-hh-dat *runremote*)) ;; on homehost ;; (not (member cmd api:read-only-queries))) ;; (mutex-unlock! *rmt-mutex*) ;; (debug:print-info 12 *default-log-port* "rmt:send-receive, case 4.1") ;; (rmt:open-qry-close-locally cmd 0 params)) ;; on homehost, no server contact made and this is a write, passively start a server ((and (cdr (remote-hh-dat *runremote*)) ; new (not (remote-server-url *runremote*)) (not (member cmd api:read-only-queries))) (debug:print-info 12 *default-log-port* "rmt:send-receive, case 5") (let ((server-url (server:read-dotserver->url *toppath*))) ;; (server:check-if-running *toppath*))) ;; Do NOT want to run server:check-if-running - very expensive to do for every write call (if server-url (remote-server-url-set! *runremote* server-url) ;; the string can be consumed by the client setup if needed (if (not (server:start-attempted? *toppath*)) (server:kind-run *toppath*)))) (mutex-unlock! *rmt-mutex*) (debug:print-info 12 *default-log-port* "rmt:send-receive, case 5.1") (rmt:open-qry-close-locally cmd 0 params)) ;;; ;; (begin ;; not on homehost, start server and wait ;; (mutex-unlock! *rmt-mutex*) ;; (debug:print-info 12 *default-log-port* "rmt:send-receive, case 5.2") ;; (tasks:start-and-wait-for-server (tasks:open-db) 0 15) ;; (rmt:send-receive cmd rid params attemptnum: attemptnum)) ;) ;) ;;;; ;; if not on homehost ensure we have a connection to a live server ;; NOTE: we *have* a homehost record by now ((and (not (cdr (remote-hh-dat *runremote*))) ;; not on a homehost (not (remote-conndat *runremote*)) ;; and no connection (server:read-dotserver *toppath*)) ;; .server file exists ;; something caused the server entry in tdb to disappear, but the server is still running (server:remove-dotserver-file *toppath* ".*") (mutex-unlock! *rmt-mutex*) (debug:print-info 12 *default-log-port* "rmt:send-receive, case 20") (rmt:send-receive cmd rid params attemptnum: (add1 attemptnum))) ((and (not (cdr (remote-hh-dat *runremote*))) ;; not on a homehost (not (remote-conndat *runremote*))) ;; and no connection (debug:print-info 12 *default-log-port* "rmt:send-receive, case 6 hh-dat: " (remote-hh-dat *runremote*) " conndat: " (remote-conndat *runremote*)) (mutex-unlock! *rmt-mutex*) (tasks:start-and-wait-for-server (tasks:open-db) 0 15) (remote-conndat-set! *runremote* (rmt:get-connection-info *toppath*)) ;; calls client:setup which calls client:setup-http (rmt:send-receive cmd rid params attemptnum: attemptnum)) ;; TODO: add back-off timeout as ;; all set up if get this far, dispatch the query ((cdr (remote-hh-dat *runremote*)) ;; we are on homehost (mutex-unlock! *rmt-mutex*) (debug:print-info 12 *default-log-port* "rmt:send-receive, case 7") (rmt:open-qry-close-locally cmd (if rid rid 0) params)) ;; not on homehost, do server query (else (mutex-unlock! *rmt-mutex*) (debug:print-info 12 *default-log-port* "rmt:send-receive, case 9") (let* ((conninfo (remote-conndat *runremote*)) (dat (case (remote-transport *runremote*) ((http) (condition-case ;; handling here has caused a lot of problems. However it is needed to deal with attemtped communication to servers that have gone away |
︙ | ︙ |
Modified server.scm from [cd39bae7c7] to [11f19c65b6].
1 2 3 4 5 6 7 8 9 10 11 12 | ;; Copyright 2006-2012, Matthew Welland. ;; ;; This program is made available under the GNU GPL version 2.0 or ;; greater. See the accompanying file COPYING for details. ;; ;; 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 s11n) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ;; Copyright 2006-2012, Matthew Welland. ;; ;; This program is made available under the GNU GPL version 2.0 or ;; greater. See the accompanying file COPYING for details. ;; ;; 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 s11n) (use srfi-1 posix regex regex-case srfi-69 hostinfo md5 message-digest directory-utils posix-extras) ;; (use zmq) (use spiffy uri-common intarweb http-client spiffy-request-vars) (declare (unit server)) (declare (uses common)) |
︙ | ︙ | |||
47 48 49 50 51 52 53 | ;; all routes though here end in exit ... ;; ;; start_server ;; (define (server:launch run-id transport-type) ;;(BB> "server:launch fired for run-id="run-id" transport-type="transport-type) | | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | ;; all routes though here end in exit ... ;; ;; start_server ;; (define (server:launch run-id transport-type) ;;(BB> "server:launch fired for run-id="run-id" transport-type="transport-type) (let ((attempt-in-progress (server:start-attempted? *toppath*))) ; check for .server-starting (when attempt-in-progress (debug:print-info 0 *default-log-port* "Server start attempt in progress in other process (=> "attempt-in-progress"<=). Aborting server launch attempt in this process ("(current-process-id)")") (exit))) (let ((dotserver-url (server:check-if-running *toppath*))) ;; check for .server (when dotserver-url (debug:print-info 0 *default-log-port* "Server already running (=> "dotserver-url"<=). Aborting server launch attempt in this process ("(current-process-id)")") (exit) )) (case transport-type ((http)(http-transport:launch run-id)) |
︙ | ︙ | |||
200 201 202 203 204 205 206 | (file-modification-time flagfile)) 15)) ;; exists and less than 15 seconds old (with-input-from-file flagfile (lambda () (read-line)))) ((file-exists? flagfile) ;; it is stale. (server:complete-attempt areapath) #f) (else #f))))) | | > | > | > > > > > > | | | | > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | | | | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | (file-modification-time flagfile)) 15)) ;; exists and less than 15 seconds old (with-input-from-file flagfile (lambda () (read-line)))) ((file-exists? flagfile) ;; it is stale. (server:complete-attempt areapath) #f) (else #f))))) (define (server:read-dotserver areapath) (let ((dotfile (conc areapath "/.server"))) (handle-exceptions exn #f ;; if things go wrong pretend we can't see the file (cond ((not (file-exists? dotfile)) #f) ((not (file-read-access? dotfile)) #f) ((> (server:dotserver-age-seconds areapath) (+ 5 (server:get-timeout))) (server:remove-dotserver-file areapath ".*") #f) (else (let* ((line (with-input-from-file dotfile (lambda () (read-line)))) (tokens (if (string? line) (string-split line ":") #f))) (cond ((eq? 4 (length tokens)) tokens) (else #f)))))))) (define (server:read-dotserver->url areapath) (let ((dotserver-tokens (server:read-dotserver areapath))) (if dotserver-tokens (conc (list-ref dotserver-tokens 0) ":" (list-ref dotserver-tokens 1)) #f))) ;; write a .server file in *toppath* with hostport ;; return #t on success, #f otherwise ;; (define (server:write-dotserver areapath host port pid transport) (let ((lock-file (conc areapath "/.server.lock")) (server-file (conc areapath "/.server"))) (if (common:simple-file-lock lock-file) (let ((res (handle-exceptions exn #f ;; failed for some reason, for the moment simply return #f (with-output-to-file server-file (lambda () (print (conc host ":" port ":" pid ":" transport)))) #t))) (debug:print-info 0 *default-log-port* "server file " server-file " for " host ":" port " created pid="pid) (common:simple-file-release-lock lock-file) res) #f))) ;; this will check that the .server file present matches the server calling this procedure. ;; if parameters match (this-pid and transport) the file will be touched and #t returned ;; otherwise #f will be returned. (define (server:confirm-dotserver areapath this-iface this-port this-pid this-transport) (let* ((tokens (server:read-dotserver areapath))) (cond ((not tokens) (debug:print-info 0 *default-log-port* "INFO: .server file does not exist.") #f) ((not (eq? 4 (length tokens))) (debug:print-info 0 *default-log-port* "INFO: .server file is corrupt. There are not 4 tokens as expeted; there are "(length tokens)".") #f) ((not (equal? this-iface (list-ref tokens 0))) (debug:print-info 0 *default-log-port* "INFO: .server file mismatch. for iface, server has value >"(list-ref tokens 0)"< but this server's value is >"this-iface"<") #f) ((not (equal? (->string this-port) (list-ref tokens 1))) (debug:print-info 0 *default-log-port* "INFO: .server file mismatch. for port, .server has value >"(list-ref tokens 1)"< but this server's value is >"(->string this-port)"<") #f) ((not (equal? (->string this-pid) (list-ref tokens 2))) (debug:print-info 0 *default-log-port* "INFO: .server file mismatch. for pid, .server has value >"(list-ref tokens 2)"< but this server's value is >"(->string this-pid)"<") #f) ((not (equal? (->string this-transport) (->string (list-ref tokens 3)))) (debug:print-info 0 *default-log-port* "INFO: .server file mismatch. for transport, .server has value >"(list-ref tokens 3)"< but this server's value is >"this-transport"<") #f) (else (server:touch-dotserver areapath) #t)))) (define (server:touch-dotserver areapath) (let ((server-file (conc areapath "/.server"))) (change-file-times server-file (current-seconds) (current-seconds)))) (define (server:dotserver-age-seconds areapath) (let ((server-file (conc areapath "/.server"))) (begin (handle-exceptions exn #f (- (current-seconds) (file-modification-time server-file)))))) (define (server:remove-dotserver-file areapath hostport) (let ((dotserver-url (server:read-dotserver->url areapath)) (server-file (conc areapath "/.server")) (lock-file (conc areapath "/.server.lock"))) (if (and dotserver-url (string-match (conc ".*:" hostport "$") dotserver-url)) ;; port matches, good enough info to decide to remove the file (if (common:simple-file-lock lock-file) (begin (handle-exceptions exn #f (delete-file* server-file)) (debug:print-info 0 *default-log-port* "server file " server-file " for " hostport " removed") (common:simple-file-release-lock lock-file)) (debug:print-info 0 *default-log-port* "server file " server-file " for " hostport " NOT removed - could not get lock.")) (debug:print-info 0 *default-log-port* "server file " server-file " for " hostport " NOT removed - dotserver-url("dotserver-url") did not match hostport pattern ("hostport")")))) ;; no longer care if multiple servers are started by accident. older servers will drop off in time. ;; (define (server:check-if-running areapath) (let* ((dotserver-url (server:read-dotserver->url areapath))) ;; tdbdat (tasks:open-db))) (if dotserver-url (let* ((res (case *transport-type* ((http)(server:ping-server dotserver-url)) ;; ((nmsg)(nmsg-transport:ping (tasks:hostinfo-get-interface server) ))) (if res dotserver-url (begin (server:remove-dotserver-file areapath ".*") ;; remove stale dotserver #f))) #f))) ;; called in megatest.scm, host-port is string hostname:port ;; ;; NOTE: This is NOT called directly from clients as not all transports support a client running ;; in the same process as the server. ;; (define (server:ping host-port-in #!key (do-exit #f)) (let ((host:port (if (not host-port-in) ;; use read-dotserver to find (server:read-dotserver->url *toppath*) (if (number? host-port-in) ;; we were handed a server-id (let ((srec (tasks:get-server-by-id (db:delay-if-busy (tasks:open-db)) host-port-in))) ;; (print "srec: " srec " host-port-in: " host-port-in) (if srec (conc (vector-ref srec 3) ":" (vector-ref srec 4)) (conc "no such server-id " host-port-in))) host-port-in)))) |
︙ | ︙ |