75 lines
2.5 KiB
Racket
75 lines
2.5 KiB
Racket
|
#lang imperative-syndicate
|
||
|
;; Extremely simple single-actor supervision
|
||
|
;; Vastly simplified compared to the available options in OTP
|
||
|
|
||
|
(provide (struct-out supervisor)
|
||
|
supervise)
|
||
|
|
||
|
(require racket/exn)
|
||
|
|
||
|
(require (for-syntax syntax/parse))
|
||
|
(require "syntax-classes.rkt")
|
||
|
(require "reflection.rkt")
|
||
|
|
||
|
(require/activate imperative-syndicate/drivers/timer)
|
||
|
|
||
|
(define-logger syndicate/supervise)
|
||
|
|
||
|
(assertion-struct supervisor (id name))
|
||
|
|
||
|
(define-syntax (supervise stx)
|
||
|
(syntax-parse stx
|
||
|
[(_ (~or (~optional (~seq #:name name-expr) #:defaults ([name-expr #'#f])
|
||
|
#:name "#:name")
|
||
|
(~optional (~seq #:linkage [linkage-expr ...]) #:defaults ([(linkage-expr 1) '()])
|
||
|
#:name "#:linkage"))
|
||
|
...
|
||
|
O ...)
|
||
|
(syntax/loc stx
|
||
|
(supervise* name-expr
|
||
|
(lambda () linkage-expr ... (void))
|
||
|
(lambda () O ...)))]))
|
||
|
|
||
|
(define (supervise* name0 linkage-thunk root-facet-thunk)
|
||
|
(define id (gensym 'supervisor))
|
||
|
(define name (or name0 (gensym 'supervisee)))
|
||
|
(spawn #:name (supervisor id name)
|
||
|
#:linkage [(linkage-thunk)] ;; may contain e.g. linkage instructions from during/spawn
|
||
|
|
||
|
(assert (supervisor id name))
|
||
|
|
||
|
(define root-supervisor-facet (current-facet))
|
||
|
|
||
|
(define intensity 1)
|
||
|
(define period 5000) ;; milliseconds
|
||
|
(define sleep-time 10) ;; seconds
|
||
|
(field [restarts '()])
|
||
|
|
||
|
(define (add-restart!)
|
||
|
(define now (current-inexact-milliseconds))
|
||
|
(define oldest-to-keep (- now period))
|
||
|
(restarts (filter (lambda (r) (>= r oldest-to-keep)) (cons now (restarts))))
|
||
|
(when (> (length (restarts)) intensity)
|
||
|
(log-syndicate/supervise-error
|
||
|
"Supervised process ~s reached max restart intensity. Sleeping for ~a seconds"
|
||
|
name
|
||
|
sleep-time)
|
||
|
(sleep sleep-time)))
|
||
|
|
||
|
(define (start-supervisee!)
|
||
|
(spawn #:name name
|
||
|
(stop-when (retracted (supervisor id name)))
|
||
|
(root-facet-thunk)))
|
||
|
|
||
|
(on (message (terminated name $reason))
|
||
|
(when reason
|
||
|
(log-syndicate/supervise-error "Supervised process ~s died" name)
|
||
|
;; (log-syndicate/supervise-error
|
||
|
;; "Supervised process ~s died with exception:\n~a"
|
||
|
;; name
|
||
|
;; (if (exn? reason) (exn->string reason) (format "~v" reason)))
|
||
|
(add-restart!)
|
||
|
(start-supervisee!)))
|
||
|
|
||
|
(on-start (start-supervisee!))))
|