#lang scheme/base
(require "../test-base.ss")
(require scheme/class
scheme/dict
scheme/match
"../snooze.ss")
(define (make-connection-pool-tests snooze)
(define (acquired-count)
(get-field acquired-count (send snooze get-database)))
(define (available-count)
(get-field available-count (send snooze get-database)))
(define (current-keepalive) 500)
(define (simplify numbers)
(for/fold ([accum null])
([num (in-list numbers)])
(if (or (null? accum) (not (= num (car accum))))
(cons num accum)
accum)))
(define (quick-sleep [millis (quotient (current-keepalive) 2)])
(sync (alarm-evt (+ (current-inexact-milliseconds) millis)))
(void))
(define (sample-connections thunk)
(let ([accum1 null]
[accum2 null]
[finish? #f])
(thread (lambda ()
(let loop ()
(unless finish?
(set! accum1 (cons (acquired-count) accum1))
(set! accum2 (cons (available-count) accum2))
(quick-sleep (quotient (current-keepalive) 10))
(loop)))))
(thunk)
(quick-sleep)
(set! finish? #t)
(values (simplify accum1)
(simplify accum2))))
(define-check (check-counts counts thunk)
(match counts
[(and counts (list claimed-min
claimed-max
unclaimed-min
unclaimed-max))
(let-values ([(claimed-profile unclaimed-profile)
(sample-connections thunk)])
(with-check-info (['claimed-profile claimed-profile]
['unclaimed-profile unclaimed-profile])
(check-equal? (list (apply min claimed-profile)
(apply max claimed-profile)
(apply min unclaimed-profile)
(apply max unclaimed-profile))
counts)))]))
(test-suite "connection-pool.ss"
#:before
(lambda ()
(printf "Connection pool tests starting.~n")
(send snooze disconnect)
(quick-sleep (current-keepalive)))
#:after
(lambda ()
(send snooze connect)
(printf "Connection pool tests complete.~n"))
(test-case "sanity check"
(check-counts (list 0 0 5 5) void))
(test-case "one connection"
(check-counts
(list 0 1 4 5)
(lambda ()
(send snooze call-with-connection
(lambda ()
(quick-sleep (* (current-keepalive) 2)))
#f)
(quick-sleep (* (current-keepalive) 2)))))
(test-case "parallel connections"
(check-counts
(list 0 4 1 5)
(lambda ()
(apply
sync
(for/list ([i (in-range 4)])
(thread (lambda ()
(send snooze call-with-connection
(lambda ()
(quick-sleep (* (current-keepalive) 2)))
#f)))))
(quick-sleep (* (current-keepalive) 2)))))
(test-case "sequential connections (connection reuse)"
(let ([conns null])
(for/list ([i (in-range 20)])
(send snooze call-with-connection
(lambda ()
(set! conns (cons (send snooze current-connection) conns)))
#f))
(send snooze call-with-connection
(lambda ()
(check-not-false (member (send snooze current-connection) conns)))
#f)))
(test-case "threads killed"
(for/list ([i (in-range 3)])
(thread
(lambda ()
(send snooze call-with-connection
(lambda ()
(kill-thread (current-thread)))
#f))))
(quick-sleep)
(check-counts
(list 0 0 5 5)
(lambda ()
(quick-sleep (current-keepalive)))))
(test-case "disconnections without connections"
(for ([i (in-range 1 1000)])
(send snooze disconnect)))
(test-case "connections created in response to load"
(check-counts
(list 0 10 0 8)
(lambda ()
(apply sync
(for/list ([i (in-range 40)])
(thread (lambda ()
(send snooze call-with-connection
(lambda () (sleep 1)) #f))))))))))
(provide make-connection-pool-tests)