memcached-client.scm 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. ;;; Simple memcached client implementation
  2. ;; Copyright (C) 2012 Free Software Foundation, Inc.
  3. ;; This library is free software; you can redistribute it and/or
  4. ;; modify it under the terms of the GNU Lesser General Public
  5. ;; License as published by the Free Software Foundation; either
  6. ;; version 3 of the License, or (at your option) any later version.
  7. ;;
  8. ;; This library is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;; Lesser General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU Lesser General Public
  14. ;; License along with this library; if not, write to the Free Software
  15. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  16. ;; 02110-1301 USA
  17. (use-modules (rnrs bytevectors)
  18. (fibers)
  19. (fibers channels)
  20. (ice-9 binary-ports)
  21. (ice-9 textual-ports)
  22. (ice-9 rdelim)
  23. (ice-9 match))
  24. (define (server-error port msg . args)
  25. (close-port port)
  26. (apply error msg args))
  27. (define (parse-int port val)
  28. (let ((num (string->number val)))
  29. (unless (and num (integer? num) (exact? num) (>= num 0))
  30. (server-error port "Expected a non-negative integer: ~s" val))
  31. num))
  32. (define (make-item flags bv)
  33. (vector flags bv))
  34. (define (item-flags item)
  35. (vector-ref item 0))
  36. (define (item-bv item)
  37. (vector-ref item 1))
  38. (define (get port . keys)
  39. (put-string port "get ")
  40. (put-string port (string-join keys " "))
  41. (put-string port "\r\n")
  42. (force-output port)
  43. (let lp ((vals '()))
  44. (let ((line (read-line port)))
  45. (when (eof-object? line)
  46. (server-error port "Expected a response to 'get', got EOF"))
  47. (match (string-split (string-trim-right line) #\space)
  48. (("VALUE" key flags length)
  49. (let* ((flags (parse-int port flags))
  50. (length (parse-int port length)))
  51. (unless (member key keys)
  52. (server-error port "Unknown key: ~a" key))
  53. (when (assoc key vals)
  54. (server-error port "Already have response for key: ~a" key))
  55. (let ((bv (get-bytevector-n port length)))
  56. (unless (= (bytevector-length bv) length)
  57. (server-error port "Expected ~A bytes, got ~A" length bv))
  58. (when (eqv? (peek-char port) #\return)
  59. (read-char port))
  60. (unless (eqv? (read-char port) #\newline)
  61. (server-error port "Expected \\n"))
  62. (lp (acons key (make-item flags bv) vals)))))
  63. (("END")
  64. (reverse vals))
  65. (_
  66. (server-error port "Bad line: ~A" line))))))
  67. (define* (set port key flags exptime bytes #:key noreply?)
  68. (put-string port "set ")
  69. (put-string port key)
  70. (put-char port #\space)
  71. (put-string port (number->string flags))
  72. (put-char port #\space)
  73. (put-string port (number->string exptime))
  74. (put-char port #\space)
  75. (put-string port (number->string (bytevector-length bytes)))
  76. (when noreply?
  77. (put-string port " noreply"))
  78. (put-string port "\r\n")
  79. (put-bytevector port bytes)
  80. (put-string port "\r\n")
  81. (force-output port)
  82. (let ((line (read-line port)))
  83. (match line
  84. ((? eof-object?)
  85. (server-error port "EOF while expecting response from server"))
  86. ("STORED\r" #t)
  87. ("NOT_STORED\r" #t)
  88. (_
  89. (server-error port "Unexpected response from server: ~A" line)))))
  90. (define (connect-to-server addrinfo)
  91. (let ((port (socket (addrinfo:fam addrinfo)
  92. (addrinfo:socktype addrinfo)
  93. (addrinfo:protocol addrinfo))))
  94. ;; Disable Nagle's algorithm. We buffer ourselves.
  95. (setsockopt port IPPROTO_TCP TCP_NODELAY 1)
  96. (fcntl port F_SETFL (logior O_NONBLOCK (fcntl port F_GETFL)))
  97. (setvbuf port 'block 1024)
  98. (connect port (addrinfo:addr addrinfo))
  99. port))
  100. (define (client-loop addrinfo n num-connections)
  101. (let ((port (connect-to-server addrinfo))
  102. (key (string-append "test-" (number->string n))))
  103. (let lp ((m 0))
  104. (when (< m num-connections)
  105. (let ((v (string->utf8 (number->string m))))
  106. (set port key 0 0 v)
  107. (let* ((response (get port key))
  108. (item (assoc-ref response key)))
  109. (unless item
  110. (server-error port "Not found: ~A" key))
  111. (unless (equal? (item-bv item) v)
  112. (server-error port "Bad response: ~A (expected ~A)" (item-bv item) v))
  113. (lp (1+ m))))))
  114. (close-port port)))
  115. (define (run-memcached-test num-clients num-connections)
  116. ;; The getaddrinfo call blocks, unfortunately. Call it once before
  117. ;; spawning clients.
  118. (let ((addrinfo (car (getaddrinfo "localhost" (number->string 11211)))))
  119. (for-each
  120. get-message
  121. (map (lambda (n)
  122. (let ((ch (make-channel)))
  123. (spawn-fiber
  124. (lambda ()
  125. (client-loop addrinfo n num-connections)
  126. (put-message ch 'done))
  127. #:parallel? #t)
  128. ch))
  129. (iota num-clients)))))
  130. (run-fibers
  131. (lambda ()
  132. (apply run-memcached-test (map string->number (cdr (program-arguments))))))