/test/test_mem_cache_db.rb

https://github.com/KellyMahan/memcachedb-client · Ruby · 1011 lines · 769 code · 226 blank · 16 comment · 6 complexity · af9913d887eed5ae8844d0c4ff8f3c91 MD5 · raw file

  1. # encoding: utf-8
  2. require 'logger'
  3. require 'stringio'
  4. require 'test/unit'
  5. require 'rubygems'
  6. begin
  7. gem 'flexmock'
  8. require 'flexmock/test_unit'
  9. rescue LoadError => e
  10. puts "Some tests require flexmock, please run `gem install flexmock`"
  11. end
  12. Thread.abort_on_exception = true
  13. $TESTING = true
  14. require File.dirname(__FILE__) + '/../lib/memcache' if not defined?(MemCacheDb)
  15. class MemCacheDb
  16. attr_writer :namespace
  17. end
  18. class FakeSocketDb
  19. attr_reader :written, :data
  20. def initialize
  21. @written = StringIO.new
  22. @data = StringIO.new
  23. end
  24. def write(data)
  25. @written.write data
  26. end
  27. def gets
  28. @data.gets
  29. end
  30. def read(arg)
  31. @data.read arg
  32. end
  33. end
  34. class Test::Unit::TestCase
  35. def requirement(bool, msg)
  36. if bool
  37. yield
  38. else
  39. puts msg
  40. assert true
  41. end
  42. end
  43. def memcached_running?
  44. TCPSocket.new('localhost', 21201) rescue false
  45. end
  46. def xprofile(name, &block)
  47. a = Time.now
  48. block.call
  49. Time.now - a
  50. end
  51. def profile(name, &block)
  52. require 'ruby-prof'
  53. a = Time.now
  54. result = RubyProf.profile(&block)
  55. time = Time.now - a
  56. printer = RubyProf::GraphHtmlPrinter.new(result)
  57. File.open("#{name}.html", 'w') do |f|
  58. printer.print(f, :min_percent=>1)
  59. end
  60. time
  61. end
  62. end
  63. class FakeServerDb
  64. attr_accessor :host, :port, :socket, :weight, :multithread, :status
  65. def initialize(socket = nil)
  66. @closed = false
  67. @host = 'example.com'
  68. @port = 21201
  69. @socket = socket || FakeSocketDb.new
  70. @weight = 1
  71. @multithread = true
  72. @status = "CONNECTED"
  73. end
  74. def close
  75. # begin
  76. # raise "Already closed"
  77. # rescue => e
  78. # puts e.backtrace.join("\n")
  79. # end
  80. @closed = true
  81. @socket = nil
  82. @status = "NOT CONNECTED"
  83. end
  84. def alive?
  85. # puts "I'm #{@closed ? 'dead' : 'alive'}"
  86. !@closed
  87. end
  88. end
  89. class TestMemCacheDb < Test::Unit::TestCase
  90. def setup
  91. @cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace'
  92. end
  93. def test_performance
  94. requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
  95. cache = MemCacheDb.new(['localhost:21201',"#{host}:21201"])
  96. cache.flush_all
  97. cache.add('a', 1, 120)
  98. with = xprofile 'get' do
  99. 1000.times do
  100. cache.get('a')
  101. end
  102. end
  103. puts ''
  104. puts "1000 gets with socket timeout: #{with} sec"
  105. cache = MemCacheDb.new(['localhost:21201',"127.0.0.1:21201"], :timeout => nil)
  106. cache.add('a', 1, 120)
  107. without = xprofile 'get' do
  108. 1000.times do
  109. cache.get('a')
  110. end
  111. end
  112. puts "1000 gets without socket timeout: #{without} sec"
  113. assert without < with
  114. end
  115. end
  116. def test_consistent_hashing
  117. requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
  118. flexmock(MemCacheDb::Server).new_instances.should_receive(:alive?).and_return(true)
  119. # Setup a continuum of two servers
  120. @cache.servers = ['mike1', 'mike2', 'mike3']
  121. keys = []
  122. 1000.times do |idx|
  123. keys << idx.to_s
  124. end
  125. before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
  126. @cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
  127. after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
  128. same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
  129. # With continuum, we should see about 75% of the keys map to the same server
  130. # With modulo, we would see about 25%.
  131. assert same_count > 700
  132. end
  133. end
  134. def test_get_multi_with_server_failure
  135. @cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace', :logger => nil #Logger.new(STDOUT)
  136. s1 = FakeServerDb.new
  137. s2 = FakeServerDb.new
  138. # Write two messages to the socket to test failover
  139. s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
  140. s1.socket.data.rewind
  141. s2.socket.data.write "bogus response\r\nbogus response\r\n"
  142. s2.socket.data.rewind
  143. @cache.servers = [s1, s2]
  144. assert s1.alive?
  145. assert s2.alive?
  146. # a maps to s1, the rest map to s2
  147. value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
  148. assert_equal({'a'=>'0123456789'}, value)
  149. assert s1.alive?
  150. assert !s2.alive?
  151. end
  152. def test_cache_get_with_failover
  153. @cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace', :logger => nil#Logger.new(STDOUT)
  154. s1 = FakeServerDb.new
  155. s2 = FakeServerDb.new
  156. # Write two messages to the socket to test failover
  157. s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
  158. s1.socket.data.rewind
  159. s2.socket.data.write "bogus response\r\nbogus response\r\n"
  160. s2.socket.data.rewind
  161. @cache.instance_variable_set(:@failover, true)
  162. @cache.servers = [s1, s2]
  163. assert s1.alive?
  164. assert s2.alive?
  165. @cache.get('foo')
  166. assert s1.alive?
  167. assert !s2.alive?
  168. end
  169. def test_cache_get_without_failover
  170. s1 = FakeServerDb.new
  171. s2 = FakeServerDb.new
  172. s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
  173. s1.socket.data.rewind
  174. s2.socket.data.write "bogus response\r\nbogus response\r\n"
  175. s2.socket.data.rewind
  176. @cache.instance_variable_set(:@failover, false)
  177. @cache.servers = [s1, s2]
  178. assert s1.alive?
  179. assert s2.alive?
  180. e = assert_raise MemCacheDb::MemCacheDbError do
  181. @cache.get('foo')
  182. end
  183. assert s1.alive?
  184. assert !s2.alive?
  185. assert_equal "No servers available", e.message
  186. end
  187. def test_cache_get
  188. server = util_setup_fake_server
  189. assert_equal "\004\b\"\0170123456789",
  190. @cache.cache_get(server, 'my_namespace:key')
  191. assert_equal "get my_namespace:key\r\n",
  192. server.socket.written.string
  193. end
  194. def test_cache_get_EOF
  195. server = util_setup_fake_server
  196. server.socket.data.string = ''
  197. e = assert_raise IndexError do
  198. @cache.cache_get server, 'my_namespace:key'
  199. end
  200. assert_equal "No connection to server (NOT CONNECTED)", e.message
  201. end
  202. def test_cache_get_bad_state
  203. server = FakeServerDb.new
  204. # Write two messages to the socket to test failover
  205. server.socket.data.write "bogus response\r\nbogus response\r\n"
  206. server.socket.data.rewind
  207. @cache.servers = []
  208. @cache.servers << server
  209. e = assert_raise IndexError do
  210. @cache.cache_get(server, 'my_namespace:key')
  211. end
  212. assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
  213. assert !server.alive?
  214. end
  215. def test_cache_get_miss
  216. socket = FakeSocketDb.new
  217. socket.data.write "END\r\n"
  218. socket.data.rewind
  219. server = FakeServerDb.new socket
  220. assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
  221. assert_equal "get my_namespace:key\r\n",
  222. socket.written.string
  223. end
  224. def test_cache_get_multi
  225. server = util_setup_fake_server
  226. server.socket.data.write "VALUE foo 0 7\r\n"
  227. server.socket.data.write "\004\b\"\bfoo\r\n"
  228. server.socket.data.write "VALUE bar 0 7\r\n"
  229. server.socket.data.write "\004\b\"\bbar\r\n"
  230. server.socket.data.write "END\r\n"
  231. server.socket.data.rewind
  232. result = @cache.cache_get_multi server, 'foo bar baz'
  233. assert_equal 2, result.length
  234. assert_equal "\004\b\"\bfoo", result['foo']
  235. assert_equal "\004\b\"\bbar", result['bar']
  236. end
  237. def test_cache_get_multi_EOF
  238. server = util_setup_fake_server
  239. server.socket.data.string = ''
  240. e = assert_raise IndexError do
  241. @cache.cache_get_multi server, 'my_namespace:key'
  242. end
  243. assert_equal "No connection to server (NOT CONNECTED)", e.message
  244. end
  245. def test_cache_get_multi_bad_state
  246. server = FakeServerDb.new
  247. # Write two messages to the socket to test failover
  248. server.socket.data.write "bogus response\r\nbogus response\r\n"
  249. server.socket.data.rewind
  250. @cache.servers = []
  251. @cache.servers << server
  252. e = assert_raise IndexError do
  253. @cache.cache_get_multi server, 'my_namespace:key'
  254. end
  255. assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
  256. assert !server.alive?
  257. end
  258. def test_multithread_error
  259. server = FakeServer.new
  260. server.multithread = false
  261. @cache = MemCacheDb.new(['localhost:1'], :multithread => false)
  262. server.socket.data.write "bogus response\r\nbogus response\r\n"
  263. server.socket.data.rewind
  264. @cache.servers = []
  265. @cache.servers << server
  266. assert_nothing_raised do
  267. @cache.set 'a', 1
  268. end
  269. passed = true
  270. Thread.new do
  271. begin
  272. @cache.set 'b', 2
  273. passed = false
  274. rescue MemCacheDb::MemCacheError => me
  275. passed = me.message =~ /multiple threads/
  276. end
  277. end
  278. assert passed
  279. end
  280. def test_initialize
  281. cache = MemCacheDb.new :namespace => 'my_namespace', :readonly => true
  282. assert_equal 'my_namespace', cache.namespace
  283. assert_equal true, cache.readonly?
  284. assert_equal true, cache.servers.empty?
  285. end
  286. def test_initialize_compatible
  287. cache = MemCacheDb.new ['localhost:21201', 'localhost:11212'],
  288. :namespace => 'my_namespace', :readonly => true
  289. assert_equal 'my_namespace', cache.namespace
  290. assert_equal true, cache.readonly?
  291. assert_equal false, cache.servers.empty?
  292. end
  293. def test_initialize_compatible_no_hash
  294. cache = MemCacheDb.new ['localhost:21201', 'localhost:11212']
  295. assert_equal nil, cache.namespace
  296. assert_equal false, cache.readonly?
  297. assert_equal false, cache.servers.empty?
  298. end
  299. def test_initialize_compatible_one_server
  300. cache = MemCacheDb.new 'localhost:21201'
  301. assert_equal nil, cache.namespace
  302. assert_equal false, cache.readonly?
  303. assert_equal false, cache.servers.empty?
  304. end
  305. def test_initialize_compatible_bad_arg
  306. e = assert_raise ArgumentError do
  307. cache = MemCacheDb.new Object.new
  308. end
  309. assert_equal 'first argument must be Array, Hash or String', e.message
  310. end
  311. def test_initialize_multiple_servers
  312. cache = MemCacheDb.new %w[localhost:21201 localhost:11212],
  313. :namespace => 'my_namespace', :readonly => true
  314. assert_equal 'my_namespace', cache.namespace
  315. assert_equal true, cache.readonly?
  316. assert_equal false, cache.servers.empty?
  317. assert !cache.instance_variable_get(:@continuum).empty?
  318. end
  319. def test_initialize_too_many_args
  320. assert_raises ArgumentError do
  321. MemCacheDb.new 1, 2, 3
  322. end
  323. end
  324. def test_decr
  325. server = FakeServerDb.new
  326. server.socket.data.write "5\r\n"
  327. server.socket.data.rewind
  328. @cache.servers = []
  329. @cache.servers << server
  330. value = @cache.decr 'key'
  331. assert_equal "decr my_namespace:key 1\r\n",
  332. @cache.servers.first.socket.written.string
  333. assert_equal 5, value
  334. end
  335. def test_decr_not_found
  336. server = FakeServerDb.new
  337. server.socket.data.write "NOT_FOUND\r\n"
  338. server.socket.data.rewind
  339. @cache.servers = []
  340. @cache.servers << server
  341. value = @cache.decr 'key'
  342. assert_equal "decr my_namespace:key 1\r\n",
  343. @cache.servers.first.socket.written.string
  344. assert_equal nil, value
  345. end
  346. def test_decr_space_padding
  347. server = FakeServerDb.new
  348. server.socket.data.write "5 \r\n"
  349. server.socket.data.rewind
  350. @cache.servers = []
  351. @cache.servers << server
  352. value = @cache.decr 'key'
  353. assert_equal "decr my_namespace:key 1\r\n",
  354. @cache.servers.first.socket.written.string
  355. assert_equal 5, value
  356. end
  357. def test_get
  358. util_setup_fake_server
  359. value = @cache.get 'key'
  360. assert_equal "get my_namespace:key\r\n",
  361. @cache.servers.first.socket.written.string
  362. assert_equal '0123456789', value
  363. end
  364. def test_get_bad_key
  365. util_setup_fake_server
  366. assert_raise ArgumentError do @cache.get 'k y' end
  367. util_setup_fake_server
  368. assert_raise ArgumentError do @cache.get 'k' * 250 end
  369. end
  370. def test_get_cache_get_IOError
  371. socket = Object.new
  372. def socket.write(arg) raise IOError, 'some io error'; end
  373. server = FakeServerDb.new socket
  374. @cache.servers = []
  375. @cache.servers << server
  376. e = assert_raise MemCacheDb::MemCacheDbError do
  377. @cache.get 'my_namespace:key'
  378. end
  379. assert_equal 'some io error', e.message
  380. end
  381. def test_get_cache_get_SystemCallError
  382. socket = Object.new
  383. def socket.write(arg) raise SystemCallError, 'some syscall error'; end
  384. server = FakeServerDb.new socket
  385. @cache.servers = []
  386. @cache.servers << server
  387. e = assert_raise MemCacheDb::MemCacheDbError do
  388. @cache.get 'my_namespace:key'
  389. end
  390. assert_equal 'unknown error - some syscall error', e.message
  391. end
  392. def test_get_no_connection
  393. @cache.servers = 'localhost:1'
  394. e = assert_raise MemCacheDb::MemCacheDbError do
  395. @cache.get 'key'
  396. end
  397. assert_match /^No connection to server/, e.message
  398. end
  399. def test_get_no_servers
  400. @cache.servers = []
  401. e = assert_raise MemCacheDb::MemCacheDbError do
  402. @cache.get 'key'
  403. end
  404. assert_equal 'No active servers', e.message
  405. end
  406. def test_get_multi
  407. server = FakeServerDb.new
  408. server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
  409. server.socket.data.write "\004\b\"\0170123456789\r\n"
  410. server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
  411. server.socket.data.write "\004\b\"\0179876543210\r\n"
  412. server.socket.data.write "END\r\n"
  413. server.socket.data.rewind
  414. @cache.servers = []
  415. @cache.servers << server
  416. values = @cache.get_multi 'key', 'keyb'
  417. assert_equal "get my_namespace:key my_namespace:keyb\r\n",
  418. server.socket.written.string
  419. expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
  420. assert_equal expected.sort, values.sort
  421. end
  422. def test_get_raw
  423. server = FakeServerDb.new
  424. server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
  425. server.socket.data.write "0123456789\r\n"
  426. server.socket.data.write "END\r\n"
  427. server.socket.data.rewind
  428. @cache.servers = []
  429. @cache.servers << server
  430. value = @cache.get 'key', true
  431. assert_equal "get my_namespace:key\r\n",
  432. @cache.servers.first.socket.written.string
  433. assert_equal '0123456789', value
  434. end
  435. def test_get_server_for_key
  436. server = @cache.get_server_for_key 'key'
  437. assert_equal 'localhost', server.host
  438. assert_equal 1, server.port
  439. end
  440. def test_get_server_for_key_multiple
  441. s1 = util_setup_server @cache, 'one.example.com', ''
  442. s2 = util_setup_server @cache, 'two.example.com', ''
  443. @cache.servers = [s1, s2]
  444. server = @cache.get_server_for_key 'keya'
  445. assert_equal 'two.example.com', server.host
  446. server = @cache.get_server_for_key 'keyb'
  447. assert_equal 'two.example.com', server.host
  448. server = @cache.get_server_for_key 'keyc'
  449. assert_equal 'two.example.com', server.host
  450. server = @cache.get_server_for_key 'keyd'
  451. assert_equal 'one.example.com', server.host
  452. end
  453. def test_get_server_for_key_no_servers
  454. @cache.servers = []
  455. e = assert_raise MemCacheDb::MemCacheDbError do
  456. @cache.get_server_for_key 'key'
  457. end
  458. assert_equal 'No servers available', e.message
  459. end
  460. def test_get_server_for_key_spaces
  461. e = assert_raise ArgumentError do
  462. @cache.get_server_for_key 'space key'
  463. end
  464. assert_equal 'illegal character in key "space key"', e.message
  465. end
  466. def test_get_server_for_key_length
  467. @cache.get_server_for_key 'x' * 250
  468. long_key = 'x' * 251
  469. e = assert_raise ArgumentError do
  470. @cache.get_server_for_key long_key
  471. end
  472. assert_equal "key too long #{long_key.inspect}", e.message
  473. end
  474. def test_incr
  475. server = FakeServerDb.new
  476. server.socket.data.write "5\r\n"
  477. server.socket.data.rewind
  478. @cache.servers = []
  479. @cache.servers << server
  480. value = @cache.incr 'key'
  481. assert_equal "incr my_namespace:key 1\r\n",
  482. @cache.servers.first.socket.written.string
  483. assert_equal 5, value
  484. end
  485. def test_incr_not_found
  486. server = FakeServerDb.new
  487. server.socket.data.write "NOT_FOUND\r\n"
  488. server.socket.data.rewind
  489. @cache.servers = []
  490. @cache.servers << server
  491. value = @cache.incr 'key'
  492. assert_equal "incr my_namespace:key 1\r\n",
  493. @cache.servers.first.socket.written.string
  494. assert_equal nil, value
  495. end
  496. def test_incr_space_padding
  497. server = FakeServerDb.new
  498. server.socket.data.write "5 \r\n"
  499. server.socket.data.rewind
  500. @cache.servers = []
  501. @cache.servers << server
  502. value = @cache.incr 'key'
  503. assert_equal "incr my_namespace:key 1\r\n",
  504. @cache.servers.first.socket.written.string
  505. assert_equal 5, value
  506. end
  507. def test_make_cache_key
  508. assert_equal 'my_namespace:key', @cache.make_cache_key('key')
  509. @cache.namespace = nil
  510. assert_equal 'key', @cache.make_cache_key('key')
  511. end
  512. def test_servers
  513. server = FakeServerDb.new
  514. @cache.servers = []
  515. @cache.servers << server
  516. assert_equal [server], @cache.servers
  517. end
  518. def test_set
  519. server = FakeServerDb.new
  520. server.socket.data.write "STORED\r\n"
  521. server.socket.data.rewind
  522. @cache.servers = []
  523. @cache.servers << server
  524. @cache.set 'key', 'value'
  525. dumped = Marshal.dump('value')
  526. expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
  527. # expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
  528. assert_equal expected, server.socket.written.string
  529. end
  530. def test_set_expiry
  531. server = FakeServerDb.new
  532. server.socket.data.write "STORED\r\n"
  533. server.socket.data.rewind
  534. @cache.servers = []
  535. @cache.servers << server
  536. @cache.set 'key', 'value', 5
  537. dumped = Marshal.dump('value')
  538. expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
  539. assert_equal expected, server.socket.written.string
  540. end
  541. def test_set_raw
  542. server = FakeServerDb.new
  543. server.socket.data.write "STORED\r\n"
  544. server.socket.data.rewind
  545. @cache.servers = []
  546. @cache.servers << server
  547. @cache.set 'key', 'value', 0, true
  548. expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
  549. assert_equal expected, server.socket.written.string
  550. end
  551. def test_set_readonly
  552. cache = MemCacheDb.new :readonly => true
  553. e = assert_raise MemCacheDb::MemCacheDbError do
  554. cache.set 'key', 'value'
  555. end
  556. assert_equal 'Update of readonly cache', e.message
  557. end
  558. def test_set_too_big
  559. server = FakeServerDb.new
  560. # Write two messages to the socket to test failover
  561. server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
  562. server.socket.data.rewind
  563. @cache.servers = []
  564. @cache.servers << server
  565. e = assert_raise MemCacheDb::MemCacheDbError do
  566. @cache.set 'key', 'v'
  567. end
  568. assert_match /object too large for cache/, e.message
  569. end
  570. def test_add
  571. server = FakeServerDb.new
  572. server.socket.data.write "STORED\r\n"
  573. server.socket.data.rewind
  574. @cache.servers = []
  575. @cache.servers << server
  576. @cache.add 'key', 'value'
  577. dumped = Marshal.dump('value')
  578. expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
  579. assert_equal expected, server.socket.written.string
  580. end
  581. def test_add_exists
  582. server = FakeServerDb.new
  583. server.socket.data.write "NOT_STORED\r\n"
  584. server.socket.data.rewind
  585. @cache.servers = []
  586. @cache.servers << server
  587. @cache.add 'key', 'value'
  588. dumped = Marshal.dump('value')
  589. expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
  590. assert_equal expected, server.socket.written.string
  591. end
  592. def test_add_expiry
  593. server = FakeServerDb.new
  594. server.socket.data.write "STORED\r\n"
  595. server.socket.data.rewind
  596. @cache.servers = []
  597. @cache.servers << server
  598. @cache.add 'key', 'value', 5
  599. dumped = Marshal.dump('value')
  600. expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
  601. assert_equal expected, server.socket.written.string
  602. end
  603. def test_add_raw
  604. server = FakeServerDb.new
  605. server.socket.data.write "STORED\r\n"
  606. server.socket.data.rewind
  607. @cache.servers = []
  608. @cache.servers << server
  609. @cache.add 'key', 'value', 0, true
  610. expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
  611. assert_equal expected, server.socket.written.string
  612. end
  613. def test_add_raw_int
  614. server = FakeServerDb.new
  615. server.socket.data.write "STORED\r\n"
  616. server.socket.data.rewind
  617. @cache.servers = []
  618. @cache.servers << server
  619. @cache.add 'key', 12, 0, true
  620. expected = "add my_namespace:key 0 0 2\r\n12\r\n"
  621. assert_equal expected, server.socket.written.string
  622. end
  623. def test_add_readonly
  624. cache = MemCacheDb.new :readonly => true
  625. e = assert_raise MemCacheDb::MemCacheDbError do
  626. cache.add 'key', 'value'
  627. end
  628. assert_equal 'Update of readonly cache', e.message
  629. end
  630. def test_delete
  631. server = FakeServerDb.new
  632. @cache.servers = []
  633. @cache.servers << server
  634. @cache.delete 'key'
  635. expected = "delete my_namespace:key 0\r\n"
  636. assert_equal expected, server.socket.written.string
  637. end
  638. def test_delete_with_expiry
  639. server = FakeServerDb.new
  640. @cache.servers = []
  641. @cache.servers << server
  642. @cache.delete 'key', 300
  643. expected = "delete my_namespace:key 300\r\n"
  644. assert_equal expected, server.socket.written.string
  645. end
  646. def test_flush_all
  647. @cache.servers = []
  648. 3.times { @cache.servers << FakeServerDb.new }
  649. @cache.flush_all
  650. expected = "flush_all\r\n"
  651. @cache.servers.each do |server|
  652. assert_equal expected, server.socket.written.string
  653. end
  654. end
  655. def test_flush_all_failure
  656. socket = FakeSocketDb.new
  657. # Write two messages to the socket to test failover
  658. socket.data.write "ERROR\r\nERROR\r\n"
  659. socket.data.rewind
  660. server = FakeServerDb.new socket
  661. @cache.servers = []
  662. @cache.servers << server
  663. assert_raise MemCacheDb::MemCacheDbError do
  664. @cache.flush_all
  665. end
  666. assert_match /flush_all\r\n/, socket.written.string
  667. end
  668. def test_stats
  669. socket = FakeSocketDb.new
  670. 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"
  671. socket.data.rewind
  672. server = FakeServerDb.new socket
  673. def server.host() 'localhost'; end
  674. def server.port() 21201; end
  675. @cache.servers = []
  676. @cache.servers << server
  677. expected = {
  678. 'localhost:21201' => {
  679. 'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
  680. 'rusage_user' => 1.0003, 'dummy' => 'ok'
  681. }
  682. }
  683. assert_equal expected, @cache.stats
  684. assert_equal "stats\r\n", socket.written.string
  685. end
  686. def test_basic_threaded_operations_should_work
  687. cache = MemCacheDb.new :multithread => true,
  688. :namespace => 'my_namespace',
  689. :readonly => false
  690. server = FakeServerDb.new
  691. server.socket.data.write "STORED\r\n"
  692. server.socket.data.rewind
  693. cache.servers = []
  694. cache.servers << server
  695. assert cache.multithread
  696. assert_nothing_raised do
  697. cache.set "test", "test value"
  698. end
  699. output = server.socket.written.string
  700. assert_match /set my_namespace:test/, output
  701. assert_match /test value/, output
  702. end
  703. def test_basic_unthreaded_operations_should_work
  704. cache = MemCacheDb.new :multithread => false,
  705. :namespace => 'my_namespace',
  706. :readonly => false
  707. server = FakeServerDb.new
  708. server.socket.data.write "STORED\r\n"
  709. server.socket.data.rewind
  710. cache.servers = []
  711. cache.servers << server
  712. assert !cache.multithread
  713. assert_nothing_raised do
  714. cache.set "test", "test value"
  715. end
  716. output = server.socket.written.string
  717. assert_match /set my_namespace:test/, output
  718. assert_match /test value/, output
  719. end
  720. def util_setup_fake_server
  721. server = FakeServerDb.new
  722. server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
  723. server.socket.data.write "\004\b\"\0170123456789\r\n"
  724. server.socket.data.write "END\r\n"
  725. server.socket.data.rewind
  726. @cache.servers = []
  727. @cache.servers << server
  728. return server
  729. end
  730. def util_setup_server(memcache, host, responses)
  731. server = MemCacheDb::Server.new memcache, host
  732. server.instance_variable_set :@sock, StringIO.new(responses)
  733. @cache.servers = []
  734. @cache.servers << server
  735. return server
  736. end
  737. def test_crazy_multithreaded_access
  738. requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
  739. cache = MemCacheDb.new(['localhost:21201', '127.0.0.1:21201'])
  740. cache.flush_all
  741. workers = []
  742. # Have a bunch of threads perform a bunch of operations at the same time.
  743. # Verify the result of each operation to ensure the request and response
  744. # are not intermingled between threads.
  745. 10.times do
  746. workers << Thread.new do
  747. 100.times do
  748. cache.set('a', 9)
  749. cache.set('b', 11)
  750. cache.add('c', 10, 0, true)
  751. assert_equal "NOT_STORED\r\n", cache.add('a', 11)
  752. assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
  753. inc = cache.incr('c', 10)
  754. assert_equal 0, inc % 5
  755. assert inc > 14
  756. assert cache.decr('c', 5) > 14
  757. assert_equal 11, cache.get('b')
  758. end
  759. end
  760. end
  761. workers.each { |w| w.join }
  762. cache.flush_all
  763. end
  764. end
  765. end