PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/test/test_mem_cache.rb

https://github.com/fiveruns/memcache-client
Ruby | 805 lines | 705 code | 92 blank | 8 comment | 4 complexity | e7b9ea731ad8726ceae0733ba6514d3e MD5 | raw file
  1. # encoding: utf-8
  2. require 'stringio'
  3. require 'test/unit'
  4. require 'rubygems'
  5. begin
  6. gem 'flexmock'
  7. require 'flexmock/test_unit'
  8. rescue => e
  9. puts "Some tests require flexmock, please run `gem install flexmock`"
  10. end
  11. $TESTING = true
  12. require File.dirname(__FILE__) + '/../lib/memcache'
  13. class MemCache
  14. attr_writer :namespace
  15. end
  16. class FakeSocket
  17. attr_reader :written, :data
  18. def initialize
  19. @written = StringIO.new
  20. @data = StringIO.new
  21. end
  22. def write(data)
  23. @written.write data
  24. end
  25. def gets
  26. @data.gets
  27. end
  28. def read(arg)
  29. @data.read arg
  30. end
  31. end
  32. class FakeServer
  33. attr_reader :host, :port, :socket
  34. def initialize(socket = nil)
  35. @closed = false
  36. @host = 'example.com'
  37. @port = 11211
  38. @socket = socket || FakeSocket.new
  39. end
  40. def close
  41. @closed = true
  42. end
  43. def alive?
  44. !@closed
  45. end
  46. end
  47. class TestMemCache < Test::Unit::TestCase
  48. def setup
  49. @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace'
  50. end
  51. def test_consistent_hashing
  52. flexmock(MemCache::Server).new_instances.should_receive(:alive?).and_return(true)
  53. # Setup a continuum of two servers
  54. @cache.servers = ['mike1', 'mike2', 'mike3']
  55. keys = []
  56. 1000.times do |idx|
  57. keys << idx.to_s
  58. end
  59. before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
  60. @cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
  61. after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
  62. same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
  63. # With continuum, we should see about 75% of the keys map to the same server
  64. # With modulo, we would see about 25%.
  65. assert same_count > 700
  66. end
  67. def test_cache_get
  68. server = util_setup_fake_server
  69. assert_equal "\004\b\"\0170123456789",
  70. @cache.cache_get(server, 'my_namespace:key')
  71. assert_equal "get my_namespace:key\r\n",
  72. server.socket.written.string
  73. end
  74. def test_cache_get_EOF
  75. server = util_setup_fake_server
  76. server.socket.data.string = ''
  77. e = assert_raise MemCache::MemCacheError do
  78. @cache.cache_get server, 'my_namespace:key'
  79. end
  80. assert_equal "lost connection to example.com:11211", e.message
  81. end
  82. def test_cache_get_bad_state
  83. server = FakeServer.new
  84. # Write two messages to the socket to test failover
  85. server.socket.data.write "bogus response\r\nbogus response\r\n"
  86. server.socket.data.rewind
  87. @cache.servers = []
  88. @cache.servers << server
  89. e = assert_raise MemCache::MemCacheError do
  90. @cache.cache_get(server, 'my_namespace:key')
  91. end
  92. assert_match /#{Regexp.quote 'unexpected response "bogus response\r\n"'}/, e.message
  93. assert !server.alive?
  94. assert_match /get my_namespace:key\r\n/, server.socket.written.string
  95. end
  96. def test_cache_get_miss
  97. socket = FakeSocket.new
  98. socket.data.write "END\r\n"
  99. socket.data.rewind
  100. server = FakeServer.new socket
  101. assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
  102. assert_equal "get my_namespace:key\r\n",
  103. socket.written.string
  104. end
  105. def test_cache_get_multi
  106. server = util_setup_fake_server
  107. server.socket.data.write "VALUE foo 0 7\r\n"
  108. server.socket.data.write "\004\b\"\bfoo\r\n"
  109. server.socket.data.write "VALUE bar 0 7\r\n"
  110. server.socket.data.write "\004\b\"\bbar\r\n"
  111. server.socket.data.write "END\r\n"
  112. server.socket.data.rewind
  113. result = @cache.cache_get_multi server, 'foo bar baz'
  114. assert_equal 2, result.length
  115. assert_equal "\004\b\"\bfoo", result['foo']
  116. assert_equal "\004\b\"\bbar", result['bar']
  117. end
  118. def test_cache_get_multi_EOF
  119. server = util_setup_fake_server
  120. server.socket.data.string = ''
  121. e = assert_raise MemCache::MemCacheError do
  122. @cache.cache_get_multi server, 'my_namespace:key'
  123. end
  124. assert_equal "lost connection to example.com:11211", e.message
  125. end
  126. def test_cache_get_multi_bad_state
  127. server = FakeServer.new
  128. # Write two messages to the socket to test failover
  129. server.socket.data.write "bogus response\r\nbogus response\r\n"
  130. server.socket.data.rewind
  131. @cache.servers = []
  132. @cache.servers << server
  133. e = assert_raise MemCache::MemCacheError do
  134. @cache.cache_get_multi server, 'my_namespace:key'
  135. end
  136. assert_match /#{Regexp.quote 'unexpected response "bogus response\r\n"'}/, e.message
  137. assert !server.alive?
  138. assert_match /get my_namespace:key\r\n/, server.socket.written.string
  139. end
  140. def test_initialize
  141. cache = MemCache.new :namespace => 'my_namespace', :readonly => true
  142. assert_equal 'my_namespace', cache.namespace
  143. assert_equal true, cache.readonly?
  144. assert_equal true, cache.servers.empty?
  145. end
  146. def test_initialize_compatible
  147. cache = MemCache.new ['localhost:11211', 'localhost:11212'],
  148. :namespace => 'my_namespace', :readonly => true
  149. assert_equal 'my_namespace', cache.namespace
  150. assert_equal true, cache.readonly?
  151. assert_equal false, cache.servers.empty?
  152. end
  153. def test_initialize_compatible_no_hash
  154. cache = MemCache.new ['localhost:11211', 'localhost:11212']
  155. assert_equal nil, cache.namespace
  156. assert_equal false, cache.readonly?
  157. assert_equal false, cache.servers.empty?
  158. end
  159. def test_initialize_compatible_one_server
  160. cache = MemCache.new 'localhost:11211'
  161. assert_equal nil, cache.namespace
  162. assert_equal false, cache.readonly?
  163. assert_equal false, cache.servers.empty?
  164. end
  165. def test_initialize_compatible_bad_arg
  166. e = assert_raise ArgumentError do
  167. cache = MemCache.new Object.new
  168. end
  169. assert_equal 'first argument must be Array, Hash or String', e.message
  170. end
  171. def test_initialize_multiple_servers
  172. cache = MemCache.new %w[localhost:11211 localhost:11212],
  173. :namespace => 'my_namespace', :readonly => true
  174. assert_equal 'my_namespace', cache.namespace
  175. assert_equal true, cache.readonly?
  176. assert_equal false, cache.servers.empty?
  177. assert !cache.instance_variable_get(:@continuum).empty?
  178. end
  179. def test_initialize_too_many_args
  180. assert_raises ArgumentError do
  181. MemCache.new 1, 2, 3
  182. end
  183. end
  184. def test_decr
  185. server = FakeServer.new
  186. server.socket.data.write "5\r\n"
  187. server.socket.data.rewind
  188. @cache.servers = []
  189. @cache.servers << server
  190. value = @cache.decr 'key'
  191. assert_equal "decr my_namespace:key 1\r\n",
  192. @cache.servers.first.socket.written.string
  193. assert_equal 5, value
  194. end
  195. def test_decr_not_found
  196. server = FakeServer.new
  197. server.socket.data.write "NOT_FOUND\r\n"
  198. server.socket.data.rewind
  199. @cache.servers = []
  200. @cache.servers << server
  201. value = @cache.decr 'key'
  202. assert_equal "decr my_namespace:key 1\r\n",
  203. @cache.servers.first.socket.written.string
  204. assert_equal nil, value
  205. end
  206. def test_decr_space_padding
  207. server = FakeServer.new
  208. server.socket.data.write "5 \r\n"
  209. server.socket.data.rewind
  210. @cache.servers = []
  211. @cache.servers << server
  212. value = @cache.decr 'key'
  213. assert_equal "decr my_namespace:key 1\r\n",
  214. @cache.servers.first.socket.written.string
  215. assert_equal 5, value
  216. end
  217. def test_get
  218. util_setup_fake_server
  219. value = @cache.get 'key'
  220. assert_equal "get my_namespace:key\r\n",
  221. @cache.servers.first.socket.written.string
  222. assert_equal '0123456789', value
  223. end
  224. def test_get_bad_key
  225. util_setup_fake_server
  226. assert_raise ArgumentError do @cache.get 'k y' end
  227. util_setup_fake_server
  228. assert_raise ArgumentError do @cache.get 'k' * 250 end
  229. end
  230. def test_get_cache_get_IOError
  231. socket = Object.new
  232. def socket.write(arg) raise IOError, 'some io error'; end
  233. server = FakeServer.new socket
  234. @cache.servers = []
  235. @cache.servers << server
  236. e = assert_raise MemCache::MemCacheError do
  237. @cache.get 'my_namespace:key'
  238. end
  239. assert_equal 'some io error', e.message
  240. end
  241. def test_get_cache_get_SystemCallError
  242. socket = Object.new
  243. def socket.write(arg) raise SystemCallError, 'some syscall error'; end
  244. server = FakeServer.new socket
  245. @cache.servers = []
  246. @cache.servers << server
  247. e = assert_raise MemCache::MemCacheError do
  248. @cache.get 'my_namespace:key'
  249. end
  250. assert_equal 'unknown error - some syscall error', e.message
  251. end
  252. def test_get_no_connection
  253. @cache.servers = 'localhost:1'
  254. e = assert_raise MemCache::MemCacheError do
  255. @cache.get 'key'
  256. end
  257. assert_match /^No connection to server/, e.message
  258. end
  259. def test_get_no_servers
  260. @cache.servers = []
  261. e = assert_raise MemCache::MemCacheError do
  262. @cache.get 'key'
  263. end
  264. assert_equal 'No active servers', e.message
  265. end
  266. def test_get_multi
  267. server = FakeServer.new
  268. server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
  269. server.socket.data.write "\004\b\"\0170123456789\r\n"
  270. server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
  271. server.socket.data.write "\004\b\"\0179876543210\r\n"
  272. server.socket.data.write "END\r\n"
  273. server.socket.data.rewind
  274. @cache.servers = []
  275. @cache.servers << server
  276. values = @cache.get_multi 'key', 'keyb'
  277. assert_equal "get my_namespace:key my_namespace:keyb\r\n",
  278. server.socket.written.string
  279. expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
  280. assert_equal expected.sort, values.sort
  281. end
  282. def test_get_raw
  283. server = FakeServer.new
  284. server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
  285. server.socket.data.write "0123456789\r\n"
  286. server.socket.data.write "END\r\n"
  287. server.socket.data.rewind
  288. @cache.servers = []
  289. @cache.servers << server
  290. value = @cache.get 'key', true
  291. assert_equal "get my_namespace:key\r\n",
  292. @cache.servers.first.socket.written.string
  293. assert_equal '0123456789', value
  294. end
  295. def test_get_server_for_key
  296. server = @cache.get_server_for_key 'key'
  297. assert_equal 'localhost', server.host
  298. assert_equal 1, server.port
  299. end
  300. def test_get_server_for_key_multiple
  301. s1 = util_setup_server @cache, 'one.example.com', ''
  302. s2 = util_setup_server @cache, 'two.example.com', ''
  303. @cache.servers = [s1, s2]
  304. server = @cache.get_server_for_key 'keya'
  305. assert_equal 'two.example.com', server.host
  306. server = @cache.get_server_for_key 'keyb'
  307. assert_equal 'two.example.com', server.host
  308. server = @cache.get_server_for_key 'keyc'
  309. assert_equal 'two.example.com', server.host
  310. server = @cache.get_server_for_key 'keyd'
  311. assert_equal 'one.example.com', server.host
  312. end
  313. def test_get_server_for_key_no_servers
  314. @cache.servers = []
  315. e = assert_raise MemCache::MemCacheError do
  316. @cache.get_server_for_key 'key'
  317. end
  318. assert_equal 'No servers available', e.message
  319. end
  320. def test_get_server_for_key_spaces
  321. e = assert_raise ArgumentError do
  322. @cache.get_server_for_key 'space key'
  323. end
  324. assert_equal 'illegal character in key "space key"', e.message
  325. end
  326. def test_get_server_for_key_length
  327. @cache.get_server_for_key 'x' * 250
  328. long_key = 'x' * 251
  329. e = assert_raise ArgumentError do
  330. @cache.get_server_for_key long_key
  331. end
  332. assert_equal "key too long #{long_key.inspect}", e.message
  333. end
  334. def test_incr
  335. server = FakeServer.new
  336. server.socket.data.write "5\r\n"
  337. server.socket.data.rewind
  338. @cache.servers = []
  339. @cache.servers << server
  340. value = @cache.incr 'key'
  341. assert_equal "incr my_namespace:key 1\r\n",
  342. @cache.servers.first.socket.written.string
  343. assert_equal 5, value
  344. end
  345. def test_incr_not_found
  346. server = FakeServer.new
  347. server.socket.data.write "NOT_FOUND\r\n"
  348. server.socket.data.rewind
  349. @cache.servers = []
  350. @cache.servers << server
  351. value = @cache.incr 'key'
  352. assert_equal "incr my_namespace:key 1\r\n",
  353. @cache.servers.first.socket.written.string
  354. assert_equal nil, value
  355. end
  356. def test_incr_space_padding
  357. server = FakeServer.new
  358. server.socket.data.write "5 \r\n"
  359. server.socket.data.rewind
  360. @cache.servers = []
  361. @cache.servers << server
  362. value = @cache.incr 'key'
  363. assert_equal "incr my_namespace:key 1\r\n",
  364. @cache.servers.first.socket.written.string
  365. assert_equal 5, value
  366. end
  367. def test_make_cache_key
  368. assert_equal 'my_namespace:key', @cache.make_cache_key('key')
  369. @cache.namespace = nil
  370. assert_equal 'key', @cache.make_cache_key('key')
  371. end
  372. def test_servers
  373. server = FakeServer.new
  374. @cache.servers = []
  375. @cache.servers << server
  376. assert_equal [server], @cache.servers
  377. end
  378. def test_servers_equals_type_error
  379. e = assert_raise TypeError do
  380. @cache.servers = [Object.new]
  381. end
  382. assert_equal 'cannot convert Object into MemCache::Server', e.message
  383. end
  384. def test_set
  385. server = FakeServer.new
  386. server.socket.data.write "STORED\r\n"
  387. server.socket.data.rewind
  388. @cache.servers = []
  389. @cache.servers << server
  390. @cache.set 'key', 'value'
  391. dumped = Marshal.dump('value')
  392. expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
  393. # expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
  394. assert_equal expected, server.socket.written.string
  395. end
  396. def test_set_expiry
  397. server = FakeServer.new
  398. server.socket.data.write "STORED\r\n"
  399. server.socket.data.rewind
  400. @cache.servers = []
  401. @cache.servers << server
  402. @cache.set 'key', 'value', 5
  403. dumped = Marshal.dump('value')
  404. expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
  405. assert_equal expected, server.socket.written.string
  406. end
  407. def test_set_raw
  408. server = FakeServer.new
  409. server.socket.data.write "STORED\r\n"
  410. server.socket.data.rewind
  411. @cache.servers = []
  412. @cache.servers << server
  413. @cache.set 'key', 'value', 0, true
  414. expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
  415. assert_equal expected, server.socket.written.string
  416. end
  417. def test_set_readonly
  418. cache = MemCache.new :readonly => true
  419. e = assert_raise MemCache::MemCacheError do
  420. cache.set 'key', 'value'
  421. end
  422. assert_equal 'Update of readonly cache', e.message
  423. end
  424. def test_set_too_big
  425. server = FakeServer.new
  426. # Write two messages to the socket to test failover
  427. server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
  428. server.socket.data.rewind
  429. @cache.servers = []
  430. @cache.servers << server
  431. e = assert_raise MemCache::MemCacheError do
  432. @cache.set 'key', 'v'
  433. end
  434. assert_match /object too large for cache/, e.message
  435. end
  436. def test_add
  437. server = FakeServer.new
  438. server.socket.data.write "STORED\r\n"
  439. server.socket.data.rewind
  440. @cache.servers = []
  441. @cache.servers << server
  442. @cache.add 'key', 'value'
  443. dumped = Marshal.dump('value')
  444. expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
  445. assert_equal expected, server.socket.written.string
  446. end
  447. def test_add_exists
  448. server = FakeServer.new
  449. server.socket.data.write "NOT_STORED\r\n"
  450. server.socket.data.rewind
  451. @cache.servers = []
  452. @cache.servers << server
  453. @cache.add 'key', 'value'
  454. dumped = Marshal.dump('value')
  455. expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
  456. assert_equal expected, server.socket.written.string
  457. end
  458. def test_add_expiry
  459. server = FakeServer.new
  460. server.socket.data.write "STORED\r\n"
  461. server.socket.data.rewind
  462. @cache.servers = []
  463. @cache.servers << server
  464. @cache.add 'key', 'value', 5
  465. dumped = Marshal.dump('value')
  466. expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
  467. assert_equal expected, server.socket.written.string
  468. end
  469. def test_add_raw
  470. server = FakeServer.new
  471. server.socket.data.write "STORED\r\n"
  472. server.socket.data.rewind
  473. @cache.servers = []
  474. @cache.servers << server
  475. @cache.add 'key', 'value', 0, true
  476. expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
  477. assert_equal expected, server.socket.written.string
  478. end
  479. def test_add_readonly
  480. cache = MemCache.new :readonly => true
  481. e = assert_raise MemCache::MemCacheError do
  482. cache.add 'key', 'value'
  483. end
  484. assert_equal 'Update of readonly cache', e.message
  485. end
  486. def test_delete
  487. server = FakeServer.new
  488. @cache.servers = []
  489. @cache.servers << server
  490. @cache.delete 'key'
  491. expected = "delete my_namespace:key 0\r\n"
  492. assert_equal expected, server.socket.written.string
  493. end
  494. def test_delete_with_expiry
  495. server = FakeServer.new
  496. @cache.servers = []
  497. @cache.servers << server
  498. @cache.delete 'key', 300
  499. expected = "delete my_namespace:key 300\r\n"
  500. assert_equal expected, server.socket.written.string
  501. end
  502. def test_flush_all
  503. @cache.servers = []
  504. 3.times { @cache.servers << FakeServer.new }
  505. @cache.flush_all
  506. expected = "flush_all\r\n"
  507. @cache.servers.each do |server|
  508. assert_equal expected, server.socket.written.string
  509. end
  510. end
  511. def test_flush_all_failure
  512. socket = FakeSocket.new
  513. # Write two messages to the socket to test failover
  514. socket.data.write "ERROR\r\nERROR\r\n"
  515. socket.data.rewind
  516. server = FakeServer.new socket
  517. @cache.servers = []
  518. @cache.servers << server
  519. assert_raise MemCache::MemCacheError do
  520. @cache.flush_all
  521. end
  522. assert_match /flush_all\r\n/, socket.written.string
  523. end
  524. def test_stats
  525. socket = FakeSocket.new
  526. socket.data.write "STAT pid 20188\r\nSTAT total_items 32\r\nSTAT version 1.2.3\r\nSTAT rusage_user 1:300\r\nSTAT dummy ok\r\nEND\r\n"
  527. socket.data.rewind
  528. server = FakeServer.new socket
  529. def server.host() 'localhost'; end
  530. def server.port() 11211; end
  531. @cache.servers = []
  532. @cache.servers << server
  533. expected = {
  534. 'localhost:11211' => {
  535. 'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
  536. 'rusage_user' => 1.0003, 'dummy' => 'ok'
  537. }
  538. }
  539. assert_equal expected, @cache.stats
  540. assert_equal "stats\r\n", socket.written.string
  541. end
  542. def test_basic_threaded_operations_should_work
  543. cache = MemCache.new :multithread => true,
  544. :namespace => 'my_namespace',
  545. :readonly => false
  546. server = FakeServer.new
  547. server.socket.data.write "STORED\r\n"
  548. server.socket.data.rewind
  549. cache.servers = []
  550. cache.servers << server
  551. assert cache.multithread
  552. assert_nothing_raised do
  553. cache.set "test", "test value"
  554. end
  555. # TODO Fails in 1.9
  556. assert_match /set my_namespace:test.*\r\n.*test value.*\r\n/, server.socket.written.string
  557. end
  558. def test_basic_unthreaded_operations_should_work
  559. cache = MemCache.new :multithread => false,
  560. :namespace => 'my_namespace',
  561. :readonly => false
  562. server = FakeServer.new
  563. server.socket.data.write "STORED\r\n"
  564. server.socket.data.rewind
  565. cache.servers = []
  566. cache.servers << server
  567. assert !cache.multithread
  568. assert_nothing_raised do
  569. cache.set "test", "test value"
  570. end
  571. # TODO Fails in 1.9
  572. assert_match /set my_namespace:test.*\r\n.*test value\r\n/, server.socket.written.string
  573. end
  574. def util_setup_fake_server
  575. server = FakeServer.new
  576. server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
  577. server.socket.data.write "\004\b\"\0170123456789\r\n"
  578. server.socket.data.write "END\r\n"
  579. server.socket.data.rewind
  580. @cache.servers = []
  581. @cache.servers << server
  582. return server
  583. end
  584. def util_setup_server(memcache, host, responses)
  585. server = MemCache::Server.new memcache, host
  586. server.instance_variable_set :@sock, StringIO.new(responses)
  587. @cache.servers = []
  588. @cache.servers << server
  589. return server
  590. end
  591. end