test-lru.rb 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. require 'rubygems'
  2. require 'redis'
  3. $runs = []; # Remember the error rate of each run for average purposes.
  4. $o = {}; # Options set parsing arguments
  5. def testit(filename)
  6. r = Redis.new
  7. r.config("SET","maxmemory","2000000")
  8. if $o[:ttl]
  9. r.config("SET","maxmemory-policy","volatile-ttl")
  10. else
  11. r.config("SET","maxmemory-policy","allkeys-lru")
  12. end
  13. r.config("SET","maxmemory-samples",5)
  14. r.config("RESETSTAT")
  15. r.flushall
  16. html = ""
  17. html << <<EOF
  18. <html>
  19. <body>
  20. <style>
  21. .box {
  22. width:5px;
  23. height:5px;
  24. float:left;
  25. margin: 1px;
  26. }
  27. .old {
  28. border: 1px black solid;
  29. }
  30. .new {
  31. border: 1px green solid;
  32. }
  33. .otherdb {
  34. border: 1px red solid;
  35. }
  36. .ex {
  37. background-color: #666;
  38. }
  39. </style>
  40. <pre>
  41. EOF
  42. # Fill the DB up to the first eviction.
  43. oldsize = r.dbsize
  44. id = 0
  45. while true
  46. id += 1
  47. begin
  48. r.set(id,"foo")
  49. rescue
  50. break
  51. end
  52. newsize = r.dbsize
  53. break if newsize == oldsize # A key was evicted? Stop.
  54. oldsize = newsize
  55. end
  56. inserted = r.dbsize
  57. first_set_max_id = id
  58. html << "#{r.dbsize} keys inserted.\n"
  59. # Access keys sequentially, so that in theory the first part will be expired
  60. # and the latter part will not, according to perfect LRU.
  61. if $o[:ttl]
  62. STDERR.puts "Set increasing expire value"
  63. (1..first_set_max_id).each{|id|
  64. r.expire(id,1000+id)
  65. STDERR.print(".") if (id % 150) == 0
  66. }
  67. else
  68. STDERR.puts "Access keys sequentially"
  69. (1..first_set_max_id).each{|id|
  70. r.get(id)
  71. sleep 0.001
  72. STDERR.print(".") if (id % 150) == 0
  73. }
  74. end
  75. STDERR.puts
  76. # Insert more 50% keys. We expect that the new keys will rarely be expired
  77. # since their last access time is recent compared to the others.
  78. #
  79. # Note that we insert the first 100 keys of the new set into DB1 instead
  80. # of DB0, so that we can try how cross-DB eviction works.
  81. half = inserted/2
  82. html << "Insert enough keys to evict half the keys we inserted.\n"
  83. add = 0
  84. otherdb_start_idx = id+1
  85. otherdb_end_idx = id+100
  86. while true
  87. add += 1
  88. id += 1
  89. if id >= otherdb_start_idx && id <= otherdb_end_idx
  90. r.select(1)
  91. r.set(id,"foo")
  92. r.select(0)
  93. else
  94. r.set(id,"foo")
  95. end
  96. break if r.info['evicted_keys'].to_i >= half
  97. end
  98. html << "#{add} additional keys added.\n"
  99. html << "#{r.dbsize} keys in DB.\n"
  100. # Check if evicted keys respect LRU
  101. # We consider errors from 1 to N progressively more serious as they violate
  102. # more the access pattern.
  103. errors = 0
  104. e = 1
  105. error_per_key = 100000.0/first_set_max_id
  106. half_set_size = first_set_max_id/2
  107. maxerr = 0
  108. (1..(first_set_max_id/2)).each{|id|
  109. if id >= otherdb_start_idx && id <= otherdb_end_idx
  110. r.select(1)
  111. exists = r.exists(id)
  112. r.select(0)
  113. else
  114. exists = r.exists(id)
  115. end
  116. if id < first_set_max_id/2
  117. thiserr = error_per_key * ((half_set_size-id).to_f/half_set_size)
  118. maxerr += thiserr
  119. errors += thiserr if exists
  120. elsif id >= first_set_max_id/2
  121. thiserr = error_per_key * ((id-half_set_size).to_f/half_set_size)
  122. maxerr += thiserr
  123. errors += thiserr if !exists
  124. end
  125. }
  126. errors = errors*100/maxerr
  127. STDERR.puts "Test finished with #{errors}% error! Generating HTML on stdout."
  128. html << "#{errors}% error!\n"
  129. html << "</pre>"
  130. $runs << errors
  131. # Generate the graphical representation
  132. (1..id).each{|id|
  133. # Mark first set and added items in a different way.
  134. c = "box"
  135. if id >= otherdb_start_idx && id <= otherdb_end_idx
  136. c << " otherdb"
  137. elsif id <= first_set_max_id
  138. c << " old"
  139. else
  140. c << " new"
  141. end
  142. # Add class if exists
  143. if id >= otherdb_start_idx && id <= otherdb_end_idx
  144. r.select(1)
  145. exists = r.exists(id)
  146. r.select(0)
  147. else
  148. exists = r.exists(id)
  149. end
  150. c << " ex" if exists
  151. html << "<div title=\"#{id}\" class=\"#{c}\"></div>"
  152. }
  153. # Close HTML page
  154. html << <<EOF
  155. </body>
  156. </html>
  157. EOF
  158. f = File.open(filename,"w")
  159. f.write(html)
  160. f.close
  161. end
  162. def print_avg
  163. avg = ($runs.reduce {|a,b| a+b}) / $runs.length
  164. puts "#{$runs.length} runs, AVG is #{avg}"
  165. end
  166. if ARGV.length < 1
  167. STDERR.puts "Usage: ruby test-lru.rb <html-output-filename> [--runs <count>] [--ttl]"
  168. STDERR.puts "Options:"
  169. STDERR.puts " --runs <count> Execute the test <count> times."
  170. STDERR.puts " --ttl Set keys with increasing TTL values"
  171. STDERR.puts " (starting from 1000 seconds) in order to"
  172. STDERR.puts " test the volatile-lru policy."
  173. exit 1
  174. end
  175. filename = ARGV[0]
  176. $o[:numruns] = 1
  177. # Options parsing
  178. i = 1
  179. while i < ARGV.length
  180. if ARGV[i] == '--runs'
  181. $o[:numruns] = ARGV[i+1].to_i
  182. i+= 1
  183. elsif ARGV[i] == '--ttl'
  184. $o[:ttl] = true
  185. else
  186. STDERR.puts "Unknown option #{ARGV[i]}"
  187. exit 1
  188. end
  189. i+= 1
  190. end
  191. $o[:numruns].times {
  192. testit(filename)
  193. print_avg if $o[:numruns] != 1
  194. }