watson-acts_as_ferret 0.4.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. data/LICENSE +20 -0
  2. data/README +104 -0
  3. data/acts_as_ferret.gemspec +58 -0
  4. data/bin/aaf_install +29 -0
  5. data/config/ferret_server.yml +24 -0
  6. data/doc/README.win32 +23 -0
  7. data/doc/demo/README +154 -0
  8. data/doc/demo/README_DEMO +23 -0
  9. data/doc/demo/Rakefile +10 -0
  10. data/doc/demo/app/controllers/admin/backend_controller.rb +14 -0
  11. data/doc/demo/app/controllers/admin_area_controller.rb +4 -0
  12. data/doc/demo/app/controllers/application.rb +5 -0
  13. data/doc/demo/app/controllers/contents_controller.rb +49 -0
  14. data/doc/demo/app/controllers/searches_controller.rb +8 -0
  15. data/doc/demo/app/helpers/admin/backend_helper.rb +2 -0
  16. data/doc/demo/app/helpers/application_helper.rb +3 -0
  17. data/doc/demo/app/helpers/content_helper.rb +2 -0
  18. data/doc/demo/app/helpers/search_helper.rb +2 -0
  19. data/doc/demo/app/models/comment.rb +48 -0
  20. data/doc/demo/app/models/content.rb +12 -0
  21. data/doc/demo/app/models/content_base.rb +28 -0
  22. data/doc/demo/app/models/search.rb +19 -0
  23. data/doc/demo/app/models/shared_index1.rb +3 -0
  24. data/doc/demo/app/models/shared_index2.rb +3 -0
  25. data/doc/demo/app/models/special_content.rb +3 -0
  26. data/doc/demo/app/models/stats.rb +20 -0
  27. data/doc/demo/app/views/admin/backend/search.rhtml +18 -0
  28. data/doc/demo/app/views/contents/_form.rhtml +10 -0
  29. data/doc/demo/app/views/contents/edit.rhtml +9 -0
  30. data/doc/demo/app/views/contents/index.rhtml +24 -0
  31. data/doc/demo/app/views/contents/new.rhtml +8 -0
  32. data/doc/demo/app/views/contents/show.rhtml +8 -0
  33. data/doc/demo/app/views/layouts/application.html.erb +17 -0
  34. data/doc/demo/app/views/searches/_content.html.erb +2 -0
  35. data/doc/demo/app/views/searches/search.html.erb +20 -0
  36. data/doc/demo/config/boot.rb +109 -0
  37. data/doc/demo/config/database.yml +38 -0
  38. data/doc/demo/config/environment.rb +69 -0
  39. data/doc/demo/config/environments/development.rb +16 -0
  40. data/doc/demo/config/environments/production.rb +19 -0
  41. data/doc/demo/config/environments/test.rb +21 -0
  42. data/doc/demo/config/ferret_server.yml +18 -0
  43. data/doc/demo/config/lighttpd.conf +40 -0
  44. data/doc/demo/config/routes.rb +9 -0
  45. data/doc/demo/db/development_structure.sql +15 -0
  46. data/doc/demo/db/migrate/001_initial_migration.rb +18 -0
  47. data/doc/demo/db/migrate/002_add_type_to_contents.rb +9 -0
  48. data/doc/demo/db/migrate/003_create_shared_index1s.rb +11 -0
  49. data/doc/demo/db/migrate/004_create_shared_index2s.rb +11 -0
  50. data/doc/demo/db/migrate/005_special_field.rb +9 -0
  51. data/doc/demo/db/migrate/006_create_stats.rb +15 -0
  52. data/doc/demo/db/schema.sql +18 -0
  53. data/doc/demo/db/schema.sqlite +14 -0
  54. data/doc/demo/doc/README_FOR_APP +2 -0
  55. data/doc/demo/doc/howto.txt +70 -0
  56. data/doc/demo/public/404.html +8 -0
  57. data/doc/demo/public/500.html +8 -0
  58. data/doc/demo/public/dispatch.cgi +10 -0
  59. data/doc/demo/public/dispatch.fcgi +24 -0
  60. data/doc/demo/public/dispatch.rb +10 -0
  61. data/doc/demo/public/favicon.ico +0 -0
  62. data/doc/demo/public/images/rails.png +0 -0
  63. data/doc/demo/public/index.html +277 -0
  64. data/doc/demo/public/robots.txt +1 -0
  65. data/doc/demo/public/stylesheets/scaffold.css +74 -0
  66. data/doc/demo/script/about +3 -0
  67. data/doc/demo/script/breakpointer +3 -0
  68. data/doc/demo/script/console +3 -0
  69. data/doc/demo/script/destroy +3 -0
  70. data/doc/demo/script/ferret_server +10 -0
  71. data/doc/demo/script/generate +3 -0
  72. data/doc/demo/script/performance/benchmarker +3 -0
  73. data/doc/demo/script/performance/profiler +3 -0
  74. data/doc/demo/script/plugin +3 -0
  75. data/doc/demo/script/process/inspector +3 -0
  76. data/doc/demo/script/process/reaper +3 -0
  77. data/doc/demo/script/process/spawner +3 -0
  78. data/doc/demo/script/process/spinner +3 -0
  79. data/doc/demo/script/runner +3 -0
  80. data/doc/demo/script/server +3 -0
  81. data/doc/demo/test/fixtures/comments.yml +12 -0
  82. data/doc/demo/test/fixtures/contents.yml +13 -0
  83. data/doc/demo/test/fixtures/remote_contents.yml +9 -0
  84. data/doc/demo/test/fixtures/shared_index1s.yml +7 -0
  85. data/doc/demo/test/fixtures/shared_index2s.yml +7 -0
  86. data/doc/demo/test/functional/admin/backend_controller_test.rb +35 -0
  87. data/doc/demo/test/functional/contents_controller_test.rb +81 -0
  88. data/doc/demo/test/functional/searches_controller_test.rb +71 -0
  89. data/doc/demo/test/smoke/drb_smoke_test.rb +321 -0
  90. data/doc/demo/test/smoke/process_stats.rb +21 -0
  91. data/doc/demo/test/test_helper.rb +30 -0
  92. data/doc/demo/test/unit/comment_test.rb +217 -0
  93. data/doc/demo/test/unit/content_test.rb +705 -0
  94. data/doc/demo/test/unit/ferret_result_test.rb +24 -0
  95. data/doc/demo/test/unit/multi_index_test.rb +329 -0
  96. data/doc/demo/test/unit/remote_index_test.rb +23 -0
  97. data/doc/demo/test/unit/shared_index1_test.rb +108 -0
  98. data/doc/demo/test/unit/shared_index2_test.rb +13 -0
  99. data/doc/demo/test/unit/sort_test.rb +21 -0
  100. data/doc/demo/test/unit/special_content_test.rb +25 -0
  101. data/doc/demo/vendor/plugins/will_paginate/LICENSE +18 -0
  102. data/doc/demo/vendor/plugins/will_paginate/README +108 -0
  103. data/doc/demo/vendor/plugins/will_paginate/Rakefile +23 -0
  104. data/doc/demo/vendor/plugins/will_paginate/init.rb +21 -0
  105. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/collection.rb +45 -0
  106. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb +44 -0
  107. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/finder.rb +159 -0
  108. data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb +95 -0
  109. data/doc/demo/vendor/plugins/will_paginate/test/array_pagination_test.rb +23 -0
  110. data/doc/demo/vendor/plugins/will_paginate/test/boot.rb +27 -0
  111. data/doc/demo/vendor/plugins/will_paginate/test/console +10 -0
  112. data/doc/demo/vendor/plugins/will_paginate/test/finder_test.rb +219 -0
  113. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/admin.rb +3 -0
  114. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/companies.yml +24 -0
  115. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/company.rb +23 -0
  116. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developer.rb +11 -0
  117. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml +13 -0
  118. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/project.rb +4 -0
  119. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/projects.yml +7 -0
  120. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/replies.yml +20 -0
  121. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/reply.rb +5 -0
  122. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/schema.sql +44 -0
  123. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topic.rb +19 -0
  124. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topics.yml +30 -0
  125. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/user.rb +2 -0
  126. data/doc/demo/vendor/plugins/will_paginate/test/fixtures/users.yml +35 -0
  127. data/doc/demo/vendor/plugins/will_paginate/test/helper.rb +42 -0
  128. data/doc/demo/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb +64 -0
  129. data/doc/demo/vendor/plugins/will_paginate/test/lib/load_fixtures.rb +10 -0
  130. data/doc/demo/vendor/plugins/will_paginate/test/pagination_test.rb +136 -0
  131. data/doc/monit-example +22 -0
  132. data/init.rb +24 -0
  133. data/install.rb +18 -0
  134. data/lib/act_methods.rb +147 -0
  135. data/lib/acts_as_ferret.rb +593 -0
  136. data/lib/ar_mysql_auto_reconnect_patch.rb +41 -0
  137. data/lib/blank_slate.rb +54 -0
  138. data/lib/bulk_indexer.rb +56 -0
  139. data/lib/class_methods.rb +279 -0
  140. data/lib/ferret_extensions.rb +192 -0
  141. data/lib/ferret_find_methods.rb +142 -0
  142. data/lib/ferret_result.rb +58 -0
  143. data/lib/ferret_server.rb +238 -0
  144. data/lib/index.rb +99 -0
  145. data/lib/instance_methods.rb +172 -0
  146. data/lib/local_index.rb +202 -0
  147. data/lib/more_like_this.rb +217 -0
  148. data/lib/multi_index.rb +133 -0
  149. data/lib/rdig_adapter.rb +149 -0
  150. data/lib/remote_functions.rb +43 -0
  151. data/lib/remote_index.rb +54 -0
  152. data/lib/remote_multi_index.rb +20 -0
  153. data/lib/search_results.rb +50 -0
  154. data/lib/server_manager.rb +71 -0
  155. data/lib/unix_daemon.rb +86 -0
  156. data/lib/without_ar.rb +52 -0
  157. data/recipes/aaf_recipes.rb +116 -0
  158. data/script/ferret_daemon +94 -0
  159. data/script/ferret_server +12 -0
  160. data/script/ferret_service +178 -0
  161. data/tasks/ferret.rake +39 -0
  162. metadata +246 -0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/generate'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/benchmarker'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/profiler'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/plugin'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/inspector'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/reaper'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/spawner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/spinner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/runner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/server'
@@ -0,0 +1,12 @@
1
+ first:
2
+ id: 1
3
+ content: comment from fixture
4
+ content_id: 1
5
+ another:
6
+ id: 2
7
+ content: second comment from fixture
8
+ content_id: 1
9
+ comment_for_c2:
10
+ id: 3
11
+ content: regarding content 2
12
+ content_id: 2
@@ -0,0 +1,13 @@
1
+ first:
2
+ id: 1
3
+ title: My Title
4
+ description: A useless description
5
+ type: Content
6
+ another:
7
+ id: 2
8
+ type: Content
9
+ special:
10
+ id: 3
11
+ title: single table inheritance
12
+ special: special field
13
+ type: SpecialContent
@@ -0,0 +1,9 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ first:
3
+ id: 1
4
+ title: title of item one
5
+ content: content of item one
6
+ another:
7
+ id: 2
8
+ title: title of item two
9
+ content: content of item two
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ first:
3
+ id: 1
4
+ name: first one
5
+ another:
6
+ id: 2
7
+ name: another one
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ first:
3
+ id: 1
4
+ name: first two
5
+ another:
6
+ id: 2
7
+ name: another two
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+ require 'admin/backend_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class Admin::BackendController; def rescue_action(e) raise e end; end
6
+
7
+ class Admin::BackendControllerTest < Test::Unit::TestCase
8
+ def setup
9
+ @controller = Admin::BackendController.new
10
+ @request = ActionController::TestRequest.new
11
+ @response = ActionController::TestResponse.new
12
+ Content.destroy_all
13
+ Content.create(:title => 'my title', :description => 'a little bit of content')
14
+ end
15
+
16
+ def teardown
17
+ Content.destroy_all
18
+ end
19
+
20
+ def test_search
21
+ get :search
22
+ assert_response :success
23
+ assert_template 'search'
24
+ assert_nil assigns(:results)
25
+
26
+ post :search, :query => 'title'
27
+ assert_template 'search'
28
+ assert_equal 1, assigns(:results).size
29
+
30
+ post :search, :query => 'monkey'
31
+ assert_template 'search'
32
+ assert assigns(:results).empty?
33
+
34
+ end
35
+ end
@@ -0,0 +1,81 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'contents_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class ContentsController; def rescue_action(e) raise e end; end
6
+
7
+ class ContentsControllerTest < Test::Unit::TestCase
8
+ fixtures :contents
9
+
10
+ def setup
11
+ @controller = ContentsController.new
12
+ @request = ActionController::TestRequest.new
13
+ @response = ActionController::TestResponse.new
14
+ end
15
+
16
+ def test_index
17
+ get :index
18
+ assert_response :success
19
+ assert_template 'index'
20
+ assert_not_nil assigns(:contents)
21
+ end
22
+
23
+ def test_show
24
+ get :show, :id => 1
25
+
26
+ assert_response :success
27
+ assert_template 'show'
28
+
29
+ assert_not_nil assigns(:content)
30
+ assert assigns(:content).valid?
31
+ end
32
+
33
+ def test_new
34
+ get :new
35
+
36
+ assert_response :success
37
+ assert_template 'new'
38
+
39
+ assert_not_nil assigns(:content)
40
+ end
41
+
42
+ def test_create
43
+ num_contents = Content.count
44
+
45
+ post :create, :content => {}
46
+
47
+ assert_response :redirect
48
+ assert_redirected_to contents_url
49
+
50
+ assert_equal num_contents + 1, Content.count
51
+ end
52
+
53
+ def test_edit
54
+ get :edit, :id => 1
55
+
56
+ assert_response :success
57
+ assert_template 'edit'
58
+
59
+ assert_not_nil assigns(:content)
60
+ assert assigns(:content).valid?
61
+ end
62
+
63
+ def test_update
64
+ post :update, :id => 1
65
+ assert_response :redirect
66
+ assert_redirected_to :action => 'show', :id => 1
67
+ end
68
+
69
+ def test_destroy
70
+ assert_not_nil Content.find(1)
71
+
72
+ post :destroy, :id => 1
73
+ assert_response :redirect
74
+ assert_redirected_to :action => 'list'
75
+
76
+ assert_raise(ActiveRecord::RecordNotFound) {
77
+ Content.find(1)
78
+ }
79
+ end
80
+
81
+ end
@@ -0,0 +1,71 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'searches_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class SearchesController; def rescue_action(e) raise e end; end
6
+
7
+ class SearchesControllerTest < Test::Unit::TestCase
8
+ fixtures :contents
9
+
10
+ def setup
11
+ @controller = SearchesController.new
12
+ @request = ActionController::TestRequest.new
13
+ @response = ActionController::TestResponse.new
14
+ ContentBase.rebuild_index
15
+ end
16
+
17
+ def test_search
18
+ get :search
19
+ assert_template 'search'
20
+ assert_response :success
21
+ assert_nil assigns(:results)
22
+ end
23
+
24
+ def test_search
25
+ get :search, :q => 'title'
26
+ assert_template 'search'
27
+ assert_equal 1, assigns(:results).total_hits
28
+ assert_equal 1, assigns(:results).size
29
+
30
+ get :search, :q => 'monkey'
31
+ assert_template 'search'
32
+ assert assigns(:results).empty?
33
+
34
+ # check that model changes are picked up by the searcher (searchers have to
35
+ # be reopened to reflect changes done to the index)
36
+ # wait for the searcher to age a bit (it seems fs timestamp resolution is
37
+ # only 1 sec)
38
+ sleep 1
39
+ Content.create :title => 'another content object', :description => 'description goes hers'
40
+ get :search, :q => 'another'
41
+ assert_template 'search'
42
+ assert_equal 1, assigns(:results).total_hits
43
+ assert_equal 1, assigns(:results).size
44
+
45
+ end
46
+
47
+ def test_pagination
48
+ Content.destroy_all
49
+ 30.times do |i|
50
+ Content.create! :title => "title of Content #{i}", :description => "#{i}"
51
+ end
52
+ get :search, :q => 'title'
53
+ r = assigns(:results)
54
+ assert_equal 30, r.total_hits
55
+ assert_equal 10, r.size
56
+ assert_equal "title of Content 0", r.first.title
57
+ assert_equal "title of Content 9", r.last.title
58
+ assert_equal 1, r.current_page
59
+ assert_equal 3, r.page_count
60
+
61
+ get :search, :q => 'title', :page => 2
62
+ r = assigns(:results)
63
+ assert_equal 30, r.total_hits
64
+ assert_equal 10, r.size
65
+ assert_equal "title of Content 10", r.first.title
66
+ assert_equal "title of Content 19", r.last.title
67
+ assert_equal 2, r.current_page
68
+ assert_equal 3, r.page_count
69
+ end
70
+
71
+ end
@@ -0,0 +1,321 @@
1
+ require 'rubygems'
2
+ require 'benchmark'
3
+ require 'gruff'
4
+
5
+ # Simple smoke test for the DRb server
6
+ # usage:
7
+ #
8
+ # # start the DRb server
9
+ # script/ferret_server -e test start
10
+ #
11
+ # # run the script
12
+ # AAF_REMOTE=true script/runner -e test test/smoke/drb_smoke_test.rb
13
+
14
+ module DrbSmokeTest
15
+
16
+ RECORDS_PER_PROCESS = 100000
17
+ NUM_PROCESSES = 10 # should be an even number
18
+ NUM_DOCS = 50
19
+ NUM_TERMS = 600
20
+ START_TIME = Time.now
21
+
22
+ class Words
23
+ DICTIONARY = '/usr/share/dict/words'
24
+ def initialize
25
+ @words = []
26
+ File.open(DICTIONARY) do |file|
27
+ file.each_line do |word|
28
+ @words << word.strip unless word =~ /'/
29
+ end
30
+ end
31
+ end
32
+
33
+ def to_s
34
+ "#{@words.size} words"
35
+ end
36
+
37
+ def random_word
38
+ @words[rand(@words.size)]
39
+ end
40
+ end
41
+
42
+ puts "compiling sample documents..."
43
+ WORDS = Words.new
44
+ puts WORDS
45
+ DOCUMENTS = []
46
+
47
+ NUM_DOCS.times do
48
+ doc = ''
49
+ NUM_TERMS.times { doc << WORDS.random_word << ' ' }
50
+ DOCUMENTS << doc
51
+ end
52
+
53
+ def self.random_document
54
+ DOCUMENTS[rand(DOCUMENTS.size)]
55
+ end
56
+
57
+ puts "built #{NUM_DOCS} documents with an avg. size of #{DOCUMENTS.join.size / NUM_DOCS} Bytes."
58
+
59
+ class Monitor
60
+ class << self
61
+ def count_connections
62
+ res = Content.connection.execute("show status where variable_name = 'Threads_connected'")
63
+ if res
64
+ res.fetch_row.last
65
+ else
66
+ "error getting connection count"
67
+ end
68
+ end
69
+ def writers_running?
70
+ Dir['*.finished'].size < (NUM_PROCESSES/2)
71
+ end
72
+ def running?
73
+ Dir['*.finished'].size < NUM_PROCESSES
74
+ end
75
+ end
76
+ end
77
+
78
+ class WorkerBase
79
+ def initialize(id)
80
+ @id = id
81
+ end
82
+
83
+ # time since startup in msec
84
+ def get_time
85
+ ((Time.now - START_TIME)*1000).to_i
86
+ end
87
+
88
+ def log(data)
89
+ data << get_time
90
+ @logfile << data.join(',') << "\n"
91
+ end
92
+
93
+ def log_finished
94
+ File.open("#{@id}.finished", 'w') do |f|
95
+ f << "finished at #{Time.now}\n"
96
+ end
97
+ end
98
+
99
+ def clear_logs
100
+ FileUtils.rm_f "#{@id}.*"
101
+ end
102
+
103
+ def run
104
+ File.open("#{self.class.prefix}_#{@id}.log",'w') do |f|
105
+ clear_logs
106
+ sleep 1 # allow other processes to get ready
107
+ @logfile = f
108
+ do_run
109
+ log_finished
110
+ puts "#{@id} finished"
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ class Writer < WorkerBase
117
+ def self.prefix; 'writer' end
118
+ def do_run
119
+ log_interval = RECORDS_PER_PROCESS / 100
120
+ RECORDS_PER_PROCESS.times do |i|
121
+ log create_record(i)
122
+ if i % log_interval == 0
123
+ # log progress
124
+ puts "#{@id}: #{i} records indexed"
125
+ end
126
+ end
127
+ end
128
+
129
+ def create_record(i)
130
+ time = Benchmark.realtime do
131
+ Content.create! :title => "record #{@id} / #{i}", :description => DrbSmokeTest::random_document
132
+ end
133
+ [ time ]
134
+ end
135
+ end
136
+
137
+ class Searcher < WorkerBase
138
+ def self.prefix; 'searcher' end
139
+ def do_run
140
+ while Monitor::writers_running?
141
+ # search with concurrent writes
142
+ log do_search
143
+ end
144
+ t = Time.now
145
+ while (Time.now - t) < 2.minutes
146
+ # the writers have finished, now hammer the server with searches for another 5 minutes
147
+ log do_search
148
+ end
149
+ end
150
+
151
+ # run a search and log it's results.
152
+ # Search is done with a query consisting of the term 'findme'
153
+ # (which is guaranteed to yield 20 results) and a random term from
154
+ # the word list, ORed together
155
+ def do_search
156
+ result = nil
157
+ query = "findme OR #{WORDS.random_word}"
158
+ time = Benchmark.realtime do
159
+ result = Content.find_id_by_contents query
160
+ end
161
+ # time, no of hits
162
+ [ time, result.first, query ]
163
+ end
164
+ end
165
+
166
+ def self.run
167
+ @start = Time.now
168
+
169
+ NUM_PROCESSES.times do |i|
170
+ unless fork
171
+ @id = i
172
+ break
173
+ end
174
+ end
175
+
176
+ if @id
177
+ @id.even? ? Writer.new(@id).run : Searcher.new(@id).run
178
+ else
179
+
180
+ # create some records to search for
181
+ 20.times do |i|
182
+ Content.create! :title => "to find #{i}", :description => ("findme #{i} " << random_document)
183
+ end
184
+
185
+ while Monitor::running?
186
+ puts "open connections: #{Monitor::count_connections}; time elapsed: #{Time.now - @start} seconds"
187
+ sleep 10
188
+ end
189
+ puts "doing the math now..."
190
+ DrbSmokeTest::Stats.new(DrbSmokeTest::Writer::prefix).run
191
+ DrbSmokeTest::Stats.new(DrbSmokeTest::Searcher::prefix).run
192
+ end
193
+ end
194
+
195
+ module Statistics
196
+ def odd?(value)
197
+ value % 2 == 1
198
+ end
199
+
200
+ def median(population)
201
+ if odd?(population.size)
202
+ population[population.size/2]
203
+ else
204
+ mean [ population[population.size/2-1], population[population.size/2] ]
205
+ end
206
+ end
207
+
208
+ def mean(population)
209
+ sum = population.inject(0) { |sum, v| sum + v }
210
+ sum / population.size.to_f
211
+ end
212
+
213
+ # variance and standard_deviation methods from
214
+ # http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/
215
+ def variance(population)
216
+ n = 0
217
+ mean = 0.0
218
+ s = 0.0
219
+ population.each { |x|
220
+ n = n + 1
221
+ delta = x - mean
222
+ mean = mean + (delta / n)
223
+ s = s + delta * (x - mean)
224
+ }
225
+ # if you want to calculate std deviation
226
+ # of a sample change this to "s / (n-1)"
227
+ return s / n
228
+ end
229
+
230
+ # calculate the standard deviation of a population
231
+ # accepts: an array, the population
232
+ # returns: the standard deviation
233
+ def standard_deviation(population)
234
+ Math.sqrt(variance(population))
235
+ rescue
236
+ puts "pop: #{population.inspect}"
237
+ end
238
+ end
239
+
240
+ class Stats
241
+ include Statistics
242
+
243
+ def initialize(prefix)
244
+ @prefix = prefix
245
+ @stats = []
246
+ end
247
+
248
+ def collect_stats
249
+ Dir["#{@prefix}_*.log"].each do |logfile|
250
+ puts logfile
251
+ File.open(logfile) do |f|
252
+ while line = f.gets
253
+ row = line.split(',')
254
+ row[row.size-1] = row.last.to_i
255
+ @stats << row
256
+ end
257
+ end
258
+ end
259
+ puts "#{@stats.size} lines read, now sorting..."
260
+ @stats.sort! { |row1, row2| row1.last <=> row2.last }
261
+ end
262
+
263
+ def with_segments(segment_count)
264
+ t0 = @stats.first.last.to_i
265
+ t1 = @stats.last.last.to_i
266
+ timespan = t1 - t0
267
+ puts "test run took: #{timespan/1000} seconds"
268
+ # we want to draw 1000 points, determine which timespan one point covers
269
+ segment_length = timespan / segment_count
270
+ t = 0
271
+ i = 0
272
+ while t <= t1
273
+ t += segment_length
274
+ segment_stats = []
275
+ while @stats.any? && @stats.first.last.to_i < t
276
+ segment_stats << @stats.shift
277
+ end
278
+ yield segment_stats unless segment_stats.empty?
279
+ end
280
+ end
281
+
282
+ def run
283
+ collect_stats
284
+ segments = []
285
+ with_segments(500) do |segment_stats|
286
+ segments << process_segment(segment_stats)
287
+ end
288
+
289
+ chart("#{@prefix} mean", "#{@prefix.downcase}_mean") do |g|
290
+ g.data :mean, segments.map{ |row| row[0] }
291
+ g.data :stddev, segments.map{ |row| row[1] }
292
+ end
293
+ chart("#{@prefix} median", "#{@prefix.downcase}_median") do |g|
294
+ g.data :median, segments.map{ |row| row[2] }
295
+ end
296
+ end
297
+
298
+ def process_segment(segment)
299
+ times = segment.map{|row|row.first.to_i * 1000}
300
+ [mean(times), standard_deviation(times), median(times), segment.size]
301
+ end
302
+
303
+ def chart(title, fname)
304
+ g = Gruff::Line.new do |g|
305
+ g.title = title
306
+ g.theme = {
307
+ :background_colors => ["#e6e6e6", "#e6e6e6"],
308
+ :colors => ["#ff43a7", '#666666', 'black', 'white', 'grey'],
309
+ :marker_color => "white"
310
+ }
311
+ end
312
+ yield g
313
+ g.write "#{fname}.png"
314
+ end
315
+ end
316
+
317
+ end
318
+
319
+
320
+ DrbSmokeTest::run
321
+