timocratic-skynet 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. data/History.txt +152 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +144 -0
  4. data/README.txt +178 -0
  5. data/Rakefile +5 -0
  6. data/app_generators/skynet_install/USAGE +5 -0
  7. data/app_generators/skynet_install/skynet_install_generator.rb +94 -0
  8. data/app_generators/skynet_install/templates/migration.rb +43 -0
  9. data/app_generators/skynet_install/templates/skynet_config.rb +50 -0
  10. data/app_generators/skynet_install/templates/skynet_initializer.rb +1 -0
  11. data/app_generators/skynet_install/templates/skynet_mysql_schema.sql +33 -0
  12. data/bin/skynet +71 -0
  13. data/bin/skynet_install +36 -0
  14. data/bin/skynet_tuplespace_server +74 -0
  15. data/config/hoe.rb +75 -0
  16. data/config/requirements.rb +17 -0
  17. data/examples/dgrep/README +70 -0
  18. data/examples/dgrep/config/skynet_config.rb +26 -0
  19. data/examples/dgrep/data/shakespeare/README +2 -0
  20. data/examples/dgrep/data/shakespeare/poetry/loverscomplaint +381 -0
  21. data/examples/dgrep/data/shakespeare/poetry/rapeoflucrece +2199 -0
  22. data/examples/dgrep/data/shakespeare/poetry/sonnets +2633 -0
  23. data/examples/dgrep/data/shakespeare/poetry/various +640 -0
  24. data/examples/dgrep/data/shakespeare/poetry/venusandadonis +1423 -0
  25. data/examples/dgrep/data/testfile1.txt +1 -0
  26. data/examples/dgrep/data/testfile2.txt +1 -0
  27. data/examples/dgrep/data/testfile3.txt +1 -0
  28. data/examples/dgrep/data/testfile4.txt +1 -0
  29. data/examples/dgrep/lib/dgrep.rb +59 -0
  30. data/examples/dgrep/lib/mapreduce_test.rb +32 -0
  31. data/examples/dgrep/lib/most_common_words.rb +45 -0
  32. data/examples/dgrep/script/dgrep +75 -0
  33. data/examples/rails_mysql_example/README +66 -0
  34. data/examples/rails_mysql_example/Rakefile +10 -0
  35. data/examples/rails_mysql_example/app/controllers/application.rb +10 -0
  36. data/examples/rails_mysql_example/app/helpers/application_helper.rb +3 -0
  37. data/examples/rails_mysql_example/app/models/user.rb +21 -0
  38. data/examples/rails_mysql_example/app/models/user_favorite.rb +5 -0
  39. data/examples/rails_mysql_example/app/models/user_mailer.rb +12 -0
  40. data/examples/rails_mysql_example/app/views/user_mailer/welcome.erb +5 -0
  41. data/examples/rails_mysql_example/config/boot.rb +109 -0
  42. data/examples/rails_mysql_example/config/database.yml +42 -0
  43. data/examples/rails_mysql_example/config/environment.rb +59 -0
  44. data/examples/rails_mysql_example/config/environments/development.rb +18 -0
  45. data/examples/rails_mysql_example/config/environments/production.rb +19 -0
  46. data/examples/rails_mysql_example/config/environments/test.rb +22 -0
  47. data/examples/rails_mysql_example/config/initializers/inflections.rb +10 -0
  48. data/examples/rails_mysql_example/config/initializers/mime_types.rb +5 -0
  49. data/examples/rails_mysql_example/config/initializers/skynet.rb +1 -0
  50. data/examples/rails_mysql_example/config/routes.rb +35 -0
  51. data/examples/rails_mysql_example/config/skynet_config.rb +36 -0
  52. data/examples/rails_mysql_example/db/migrate/001_create_skynet_tables.rb +43 -0
  53. data/examples/rails_mysql_example/db/migrate/002_create_users.rb +16 -0
  54. data/examples/rails_mysql_example/db/migrate/003_create_user_favorites.rb +14 -0
  55. data/examples/rails_mysql_example/db/schema.rb +85 -0
  56. data/examples/rails_mysql_example/db/skynet_mysql_schema.sql +33 -0
  57. data/examples/rails_mysql_example/doc/README_FOR_APP +2 -0
  58. data/examples/rails_mysql_example/lib/tasks/rails_mysql_example.rake +20 -0
  59. data/examples/rails_mysql_example/public/.htaccess +40 -0
  60. data/examples/rails_mysql_example/public/404.html +30 -0
  61. data/examples/rails_mysql_example/public/422.html +30 -0
  62. data/examples/rails_mysql_example/public/500.html +30 -0
  63. data/examples/rails_mysql_example/public/dispatch.cgi +10 -0
  64. data/examples/rails_mysql_example/public/dispatch.fcgi +24 -0
  65. data/examples/rails_mysql_example/public/dispatch.rb +10 -0
  66. data/examples/rails_mysql_example/public/favicon.ico +0 -0
  67. data/examples/rails_mysql_example/public/images/rails.png +0 -0
  68. data/examples/rails_mysql_example/public/index.html +277 -0
  69. data/examples/rails_mysql_example/public/javascripts/application.js +2 -0
  70. data/examples/rails_mysql_example/public/javascripts/controls.js +963 -0
  71. data/examples/rails_mysql_example/public/javascripts/dragdrop.js +972 -0
  72. data/examples/rails_mysql_example/public/javascripts/effects.js +1120 -0
  73. data/examples/rails_mysql_example/public/javascripts/prototype.js +4225 -0
  74. data/examples/rails_mysql_example/public/robots.txt +5 -0
  75. data/examples/rails_mysql_example/script/about +3 -0
  76. data/examples/rails_mysql_example/script/console +3 -0
  77. data/examples/rails_mysql_example/script/destroy +3 -0
  78. data/examples/rails_mysql_example/script/generate +3 -0
  79. data/examples/rails_mysql_example/script/performance/benchmarker +3 -0
  80. data/examples/rails_mysql_example/script/performance/profiler +3 -0
  81. data/examples/rails_mysql_example/script/performance/request +3 -0
  82. data/examples/rails_mysql_example/script/plugin +3 -0
  83. data/examples/rails_mysql_example/script/process/inspector +3 -0
  84. data/examples/rails_mysql_example/script/process/reaper +3 -0
  85. data/examples/rails_mysql_example/script/process/spawner +3 -0
  86. data/examples/rails_mysql_example/script/runner +3 -0
  87. data/examples/rails_mysql_example/script/server +3 -0
  88. data/examples/rails_mysql_example/test/fixtures/user_favorites.yml +9 -0
  89. data/examples/rails_mysql_example/test/fixtures/users.yml +11 -0
  90. data/examples/rails_mysql_example/test/test_helper.rb +38 -0
  91. data/examples/rails_mysql_example/test/unit/user_favorite_test.rb +8 -0
  92. data/examples/rails_mysql_example/test/unit/user_test.rb +8 -0
  93. data/extras/README +7 -0
  94. data/extras/init.d/skynet +87 -0
  95. data/extras/nagios/check_skynet.sh +121 -0
  96. data/extras/rails/controllers/skynet_controller.rb +43 -0
  97. data/extras/rails/views/skynet/index.rhtml +137 -0
  98. data/lib/skynet.rb +95 -0
  99. data/lib/skynet/mapreduce_helper.rb +74 -0
  100. data/lib/skynet/mapreduce_test.rb +56 -0
  101. data/lib/skynet/message_queue_adapters/message_queue_adapter.rb +70 -0
  102. data/lib/skynet/message_queue_adapters/mysql.rb +509 -0
  103. data/lib/skynet/message_queue_adapters/tuple_space.rb +316 -0
  104. data/lib/skynet/skynet_active_record_extensions.rb +280 -0
  105. data/lib/skynet/skynet_config.rb +232 -0
  106. data/lib/skynet/skynet_console.rb +50 -0
  107. data/lib/skynet/skynet_console_helper.rb +66 -0
  108. data/lib/skynet/skynet_debugger.rb +138 -0
  109. data/lib/skynet/skynet_guid_generator.rb +68 -0
  110. data/lib/skynet/skynet_job.rb +892 -0
  111. data/lib/skynet/skynet_launcher.rb +40 -0
  112. data/lib/skynet/skynet_logger.rb +62 -0
  113. data/lib/skynet/skynet_manager.rb +706 -0
  114. data/lib/skynet/skynet_message.rb +359 -0
  115. data/lib/skynet/skynet_message_queue.rb +136 -0
  116. data/lib/skynet/skynet_partitioners.rb +96 -0
  117. data/lib/skynet/skynet_ruby_extensions.rb +53 -0
  118. data/lib/skynet/skynet_task.rb +118 -0
  119. data/lib/skynet/skynet_tuplespace_server.rb +83 -0
  120. data/lib/skynet/skynet_worker.rb +451 -0
  121. data/lib/skynet/version.rb +9 -0
  122. data/script/destroy +14 -0
  123. data/script/generate +14 -0
  124. data/script/txt2html +74 -0
  125. data/setup.rb +1585 -0
  126. data/tasks/deployment.rake +34 -0
  127. data/tasks/environment.rake +7 -0
  128. data/tasks/website.rake +17 -0
  129. data/test/test_active_record_extensions.rb +138 -0
  130. data/test/test_generator_helper.rb +20 -0
  131. data/test/test_helper.rb +10 -0
  132. data/test/test_mysql_message_queue_adapter.rb +263 -0
  133. data/test/test_skynet.rb +19 -0
  134. data/test/test_skynet_install_generator.rb +49 -0
  135. data/test/test_skynet_job.rb +717 -0
  136. data/test/test_skynet_manager.rb +157 -0
  137. data/test/test_skynet_message.rb +229 -0
  138. data/test/test_skynet_task.rb +24 -0
  139. data/test/test_tuplespace_message_queue.rb +174 -0
  140. data/website/index.html +181 -0
  141. data/website/index.txt +98 -0
  142. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  143. data/website/stylesheets/screen.css +138 -0
  144. data/website/template.rhtml +48 -0
  145. metadata +247 -0
data/lib/skynet.rb ADDED
@@ -0,0 +1,95 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ $:.unshift File.dirname(__FILE__) + '/skynet'
3
+
4
+ # path = File.expand_path(File.dirname(__FILE__))
5
+
6
+
7
+ require 'drb'
8
+ require 'skynet_guid_generator'
9
+ require 'skynet_logger'
10
+ require 'skynet_config'
11
+ require 'timeout'
12
+
13
+ Skynet::CONFIG[:SKYNET_PATH] ||= File.expand_path(File.dirname(__FILE__) +"/..")
14
+ # Skynet::CONFIG[:LAUNCHER_PATH] ||= File.expand_path(ENV['_'])
15
+
16
+ require 'skynet_debugger'
17
+ require 'skynet_message'
18
+ require 'message_queue_adapters/message_queue_adapter'
19
+ require 'message_queue_adapters/tuple_space'
20
+ require "skynet_message_queue"
21
+ require 'skynet_partitioners'
22
+ require 'skynet_job'
23
+ require 'skynet_worker'
24
+ require 'skynet_task'
25
+ require 'skynet_manager'
26
+ require 'skynet_tuplespace_server'
27
+ require 'skynet_ruby_extensions'
28
+ begin
29
+ require 'active_record'
30
+ require 'skynet_active_record_extensions'
31
+ require 'message_queue_adapters/mysql'
32
+ rescue LoadError => e
33
+ end
34
+ require 'mapreduce_test'
35
+ require 'skynet_launcher'
36
+ require 'skynet_console'
37
+ require 'mapreduce_helper'
38
+
39
+
40
+ begin
41
+ require 'fastthread'
42
+ rescue LoadError
43
+ # puts 'fastthread not installed, using thread instead'
44
+ require 'thread'
45
+ end
46
+
47
+ class Skynet
48
+
49
+ # kinda like system() but gives me back a pid
50
+ def self.fork_and_exec(command)
51
+ sleep 0.01 # remove contention on manager drb object
52
+ log = Skynet::Logger.get
53
+ debug "executing /bin/sh -c \"#{command}\""
54
+ pid = safefork do
55
+ close_files
56
+ exec("/bin/sh -c \"#{command}\"")
57
+ exit
58
+ end
59
+ Process.detach(pid)
60
+ pid
61
+ end
62
+
63
+ def self.safefork (&block)
64
+ @fork_tries ||= 0
65
+ fork(&block)
66
+ rescue Errno::EWOULDBLOCK
67
+ raise if @fork_tries >= 20
68
+ @fork_tries += 1
69
+ sleep 5
70
+ retry
71
+ end
72
+
73
+
74
+ # close open file descriptors starting with STDERR+1
75
+ def self.close_files(from=3, to=50)
76
+ close_console
77
+ (from .. to).each do |fd|
78
+ IO.for_fd(fd).close rescue nil
79
+ end
80
+ end
81
+
82
+ def self.close_console
83
+ STDIN.reopen "/dev/null"
84
+ STDOUT.reopen "/dev/null", "a"
85
+ STDERR.reopen STDOUT
86
+ end
87
+
88
+ def self.process_alive?(worker_pid)
89
+ Process.kill(0,worker_pid)
90
+ return true
91
+ rescue Errno::ESRCH => e
92
+ return false
93
+ end
94
+
95
+ end
@@ -0,0 +1,74 @@
1
+ module MapreduceHelper
2
+ # You can include the MapreduceHelper into your class to give you standard self.map and self.reduce methods.
3
+ # You need only implement self.map_each and self.reduce_each methods which accept a single item (istead of an arrad)
4
+ #
5
+ # Example Usage:
6
+ # This example is a bit contrived.
7
+ #
8
+ # class MapReduceTest
9
+ # include MapreduceHelper
10
+ #
11
+ # def self.run
12
+ # job = Skynet::Job.new(
13
+ # :mappers => 2,
14
+ # :reducers => 1,
15
+ # :map_reduce_class => self,
16
+ # :map_data => ['http://www.geni.com'.'http://www.yahoo.com','http://www.cnet.com']
17
+ # )
18
+ # results = job.run
19
+ # end
20
+ #
21
+ # def self.map_each(url)
22
+ # SomeUrlSlurper.gather_results(url) # returns an array of urls of sites that link to the given url
23
+ # end
24
+ #
25
+ # def self.reduce_each(linked_from_url)
26
+ # SomeUrlSluper.find_text("mysite", linked_from_url) # finds all the times "mysite" appears in the given url, which we know links to the url given in the map_data
27
+ # end
28
+ # end
29
+ #
30
+ # MapReduceTest.run
31
+
32
+
33
+ def self.included(base)
34
+ base.extend MapreduceHelper
35
+ end
36
+
37
+ # Takes an array of map_data, iterates over that array calling self.map_each(item) for each
38
+ # item in that array. Catches exceptions in each iteration and continues processing.
39
+ def map(map_data_array)
40
+ raise Skynet::Job::BadMapOrReduceError.new("#{self.class} has no self.map_each method.") unless self.respond_to?(:map_each)
41
+ if map_data_array.is_a?(Array)
42
+ results = []
43
+ map_data_array.each do |data|
44
+ begin
45
+ results << map_each(data)
46
+ rescue Exception => e
47
+ error "ERROR IN #{self} [#{e.class} #{e.message}] #{e.backtrace.join("\n")}"
48
+ end
49
+ end
50
+ results
51
+ else
52
+ map_each(map_data_array)
53
+ end
54
+ end
55
+
56
+ # Takes an array of post reduce_partitioned data, iterates over that array calling self.reduce_each(item) for each
57
+ # item in that array. Catches exceptions in each iteration and continues processing.
58
+ def reduce(reduce_partitioned_data_array)
59
+ return reduce_partitioned_data_array unless self.respond_to?(:reduce_each)
60
+ if reduce_partitioned_data_array.is_a?(Array)
61
+ results = []
62
+ reduce_partitioned_data_array.each do |data|
63
+ begin
64
+ results << reduce_each(data)
65
+ rescue Exception => e
66
+ error "ERROR IN #{self} [#{e.class} #{e.message}] #{e.backtrace.join("\n")}"
67
+ end
68
+ end
69
+ results
70
+ else
71
+ reduce_each(reduce_partitioned_data_array)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,56 @@
1
+ class Skynet
2
+ class MapreduceTest
3
+ include SkynetDebugger
4
+
5
+ def self.map(datas)
6
+ results = {}
7
+ datas.each do |data|
8
+ results[data] ||= 0
9
+ results[data] += 1
10
+ end
11
+ [results]
12
+ end
13
+
14
+ def self.reduce(datas)
15
+ results = {}
16
+ datas.each do |hashes|
17
+ hashes.each do |key,value|
18
+ results[key] ||= 0
19
+ results[key] += value
20
+ end
21
+ end
22
+ results
23
+ end
24
+ end
25
+
26
+ class ProfileCountTest
27
+ def self.run
28
+ job = Skynet::Job.new(
29
+ :mappers => 2,
30
+ :reducers => 1,
31
+ :map_reduce_class => self,
32
+ :map_data => [OpenStruct.new({:created_by => 2}),OpenStruct.new({:created_by => 2}),OpenStruct.new({:created_by => 3})]
33
+ )
34
+ results = job.run
35
+ end
36
+
37
+ def self.map(profiles)
38
+ result = Array.new
39
+ profiles.each do |profile|
40
+ result << [profile.created_by, 1] if profile.created_by
41
+ end
42
+ result
43
+ end
44
+
45
+ def self.reduce(pairs)
46
+ totals = Hash.new
47
+ pairs.each do |pair|
48
+ created_by, count = pair[0], pair[1]
49
+ totals[created_by] ||= 0
50
+ totals[created_by] += count
51
+ end
52
+ totals
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,70 @@
1
+ class Skynet
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ class RequestExpiredError < Skynet::Error
7
+ end
8
+
9
+ class InvalidMessage < Skynet::Error
10
+ end
11
+
12
+ class AbstractClassError < Skynet::Error
13
+ end
14
+
15
+ class MessageQueueAdapter
16
+
17
+ def list_results(data,timeout=nil)
18
+ raise AbstractClassError.new("You must implement list_results in a subclass.")
19
+ end
20
+
21
+ def list_tasks(template,timeout=nil)
22
+ raise AbstractClassError.new("You must implement method in a subclass.")
23
+ end
24
+
25
+ def take_next_task(template,timeout=nil)
26
+ raise AbstractClassError.new("You must implement method in a subclass.")
27
+ end
28
+
29
+ def write_message(template,timeout=nil)
30
+ raise AbstractClassError.new("You must implement method in a subclass.")
31
+ end
32
+
33
+ def write_result(template,timeout=nil)
34
+ raise AbstractClassError.new("You must implement method in a subclass.")
35
+ end
36
+
37
+ def take_result(template,timeout=nil)
38
+ raise AbstractClassError.new("You must implement method in a subclass.")
39
+ end
40
+
41
+ def write_error(template,timeout=nil)
42
+ raise AbstractClassError.new("You must implement method in a subclass.")
43
+ end
44
+
45
+ def write_worker_status(template,timeout=nil)
46
+ raise AbstractClassError.new("You must implement method in a subclass.")
47
+ end
48
+
49
+ def take_worker_status(template,timeout=nil)
50
+ raise AbstractClassError.new("You must implement method in a subclass.")
51
+ end
52
+
53
+ def read_all_worker_statuses(template,timeout=nil)
54
+ raise AbstractClassError.new("You must implement method in a subclass.")
55
+ end
56
+
57
+ def get_worker_version(template,timeout=nil)
58
+ raise AbstractClassError.new("You must implement method in a subclass.")
59
+ end
60
+
61
+ def set_worker_version(template,timeout=nil)
62
+ raise AbstractClassError.new("You must implement method in a subclass.")
63
+ end
64
+
65
+ def clear_outstanding_tasks
66
+ raise AbstractClassError.new("You must implement clear_outstanding_tasks in a subclass.")
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,509 @@
1
+ class SkynetMessageQueue < ActiveRecord::Base
2
+ end
3
+
4
+ class SkynetWorkerQueue < ActiveRecord::Base
5
+ end
6
+
7
+
8
+ class Skynet
9
+
10
+ # require 'mysql'
11
+
12
+ class Error < StandardError
13
+ end
14
+
15
+ class RequestExpiredError < Skynet::Error
16
+ end
17
+
18
+ class InvalidMessage < Skynet::Error
19
+ end
20
+
21
+ class MessageQueueAdapter::Mysql < Skynet::MessageQueueAdapter
22
+
23
+ include SkynetDebugger
24
+ include Skynet::GuidGenerator
25
+
26
+ SEARCH_FIELDS = [:tasktype, :task_id, :job_id, :payload_type, :expire_time, :iteration, :version] unless defined?(SEARCH_FIELDS)
27
+
28
+ Skynet::CONFIG[:MYSQL_MESSAGE_QUEUE_TEMP_CHECK_DELAY] ||= 30
29
+
30
+ @@db_set = false
31
+
32
+ def self.adapter
33
+ :mysql
34
+ end
35
+
36
+ def self.start_or_connect(options={})
37
+ new
38
+ end
39
+
40
+ def initialize(options={})
41
+ if Skynet::CONFIG[:MYSQL_MESSAGE_QUEUE_TABLE]
42
+ SkynetMessageQueue.table_name = Skynet::CONFIG[:MYSQL_MESSAGE_QUEUE_TABLE]
43
+ end
44
+ if not @@db_set
45
+ if Skynet::CONFIG[:MYSQL_QUEUE_DATABASE]
46
+ begin
47
+ SkynetMessageQueue.establish_connection Skynet::CONFIG[:MYSQL_QUEUE_DATABASE]
48
+ SkynetWorkerQueue.establish_connection Skynet::CONFIG[:MYSQL_QUEUE_DATABASE]
49
+ rescue ActiveRecord::AdapterNotSpecified => e
50
+ error "#{Skynet::CONFIG[:MYSQL_QUEUE_DATABASE]} not defined as a database adaptor #{e.message}"
51
+ end
52
+ elsif (not ActiveRecord::Base.connected?) and Skynet::CONFIG[:MYSQL_DATABASE]
53
+ db_options = {
54
+ :adapter => Skynet::CONFIG[:MYSQL_ADAPTER],
55
+ :host => Skynet::CONFIG[:MYSQL_HOST],
56
+ :username => Skynet::CONFIG[:MYSQL_USERNAME],
57
+ :password => Skynet::CONFIG[:MYSQL_PASSWORD],
58
+ :database => Skynet::CONFIG[:MYSQL_DATABASE]
59
+ }
60
+ ActiveRecord::Base.establish_connection(db_options)
61
+ end
62
+ end
63
+ @@db_set = true
64
+
65
+ end
66
+
67
+ def message_queue_table
68
+ Skynet::CONFIG[:MYSQL_MESSAGE_QUEUE_TABLE] || SkynetMessageQueue.table_name
69
+ end
70
+
71
+ def self.debug_class_desc
72
+ "MYSQLMQ"
73
+ end
74
+
75
+ def message_to_conditions(message)
76
+ template_to_conditions(message.to_a)
77
+ end
78
+
79
+ def template_to_conditions(template,fields=Skynet::Message.fields)
80
+ conditions = []
81
+ values = []
82
+
83
+ fields.each_with_index do |field,ii|
84
+ value = template[ii]
85
+ next unless value
86
+ if value.is_a?(Range)
87
+ conditions << "#{field} BETWEEN #{value.first} AND #{value.last}"
88
+ elsif value.is_a?(Symbol) or value.is_a?(String)
89
+ conditions << "#{field} = '#{value}'"
90
+ else
91
+ conditions << "#{field} = #{value}"
92
+ end
93
+ end
94
+ return '' if conditions.empty?
95
+ return conditions.join(" AND ")
96
+ end
97
+
98
+ def message_to_hash(message,timeout=nil,fields=Skynet::Message.fields)
99
+ timeout ||= message.expiry
100
+ hash = {}
101
+ fields.each do |field|
102
+ next if field == :drburi
103
+ # next unless message.send(field)
104
+ if message.send(field).is_a?(Symbol)
105
+ hash[field] = message.send(field).to_s
106
+ elsif field == :payload
107
+ hash[:raw_payload] = message.raw_payload
108
+ else
109
+ hash[field] = message.send(field)
110
+ end
111
+ end
112
+ if timeout
113
+ hash[:timeout] = timeout
114
+ hash[:expire_time] = (Time.now.to_f + timeout) unless hash[:expire_time]
115
+ end
116
+ hash
117
+ end
118
+
119
+ def write_fallback_message(message_row, message)
120
+ tran_id = get_unique_id(1)
121
+ ftm = message.fallback_task_message
122
+ update_sql = %{
123
+ update #{message_queue_table}
124
+ SET iteration = #{ftm.iteration },
125
+ expire_time = #{ftm.expire_time},
126
+ updated_on = '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}',
127
+ tran_id = #{tran_id}
128
+ WHERE id = #{message_row.id} AND iteration = #{message.iteration}
129
+ AND tran_id #{(message_row.tran_id ? " =#{message_row.tran_id}" : ' IS NULL')}
130
+ }
131
+ rows = update(update_sql) || 0
132
+ message_row.tran_id = tran_id if rows == 1
133
+ rows
134
+ end
135
+
136
+ def take_next_task(curver,timeout=0.5,payload_type=nil,queue_id=0)
137
+ timeout = Skynet::CONFIG[:MYSQL_NEXT_TASK_TIMEOUT] if timeout < 1
138
+ debug "TASK NEXT TASK!!!!!!! timeout: #{timeout} queue_id:#{queue_id}"
139
+
140
+ start = Time.now
141
+ template = Skynet::Message.next_task_template(curver, payload_type)
142
+ message = nil
143
+
144
+ loop do
145
+ rows = 0
146
+ message = nil
147
+ template = Skynet::Message.next_task_template(curver, payload_type)
148
+ begin
149
+ message_row = find_next_message(template, payload_type)
150
+ if message_row
151
+ message = Skynet::Message.new(message_row.attributes)
152
+ rows = write_fallback_message(message_row, message)
153
+
154
+ if rows < 1
155
+ old_temp = temperature(payload_type)
156
+ set_temperature(payload_type, template_to_conditions(template), queue_id)
157
+ debug "MISSCOLLISION PTYPE #{payload_type} OLDTEMP: #{old_temp} NEWTEMP: #{temperature(payload_type)}"
158
+ else
159
+ break
160
+ end
161
+ else # no messages on queue with this temp
162
+ old_temp = temperature(payload_type)
163
+ if old_temp > 1
164
+ set_temperature(payload_type, template_to_conditions(template), queue_id)
165
+ end
166
+ debug "MISS PTYPE #{payload_type} OLDTEMP: #{old_temp} NEWTEMP: #{temperature(payload_type)}"
167
+ end
168
+ rescue Skynet::Message::BadMessage => e
169
+ message_row.destroy
170
+ next
171
+ rescue ActiveRecord::StatementInvalid => e
172
+ if e.message =~ /Deadlock/
173
+ old_temp = temperature(payload_type)
174
+ set_temperature(payload_type, template_to_conditions(template), queue_id)
175
+ debug "COLLISION PTYPE #{payload_type} OLDTEMP: #{old_temp} NEWTEMP: #{temperature(payload_type)}"
176
+ else
177
+ raise e
178
+ end
179
+ end
180
+ if Time.now.to_f > start.to_f + timeout
181
+ debug "MISSTIMEOUT PTYPE #{payload_type} #{temperature(payload_type)}"
182
+ raise Skynet::RequestExpiredError.new
183
+ else
184
+ sleepy = rand(timeout * 0.5 )
185
+ debug "EMPTY QUEUE #{temperature(payload_type)} SLEEPING: #{timeout} / #{sleepy}"
186
+ sleep sleepy
187
+ end
188
+ end
189
+
190
+ return message
191
+ end
192
+
193
+ def write_message(message,timeout=nil)
194
+ timeout ||= message.expiry
195
+ SkynetMessageQueue.create(message_to_hash(message, timeout))
196
+ end
197
+
198
+ def write_result(message,result=[],timeout=nil)
199
+ timeout ||= message.expiry
200
+ result_message = message.result_message(result)
201
+ result_message.expire_time = nil
202
+ update_message_with_result(result_message,timeout)
203
+ end
204
+
205
+ def update_message_with_result(message,timeout=nil)
206
+ timeout ||= message.expiry
207
+ timeout_sql = (timeout ? ", timeout = #{timeout}, expire_time = #{Time.now.to_f + timeout}" : '')
208
+ rows = 0
209
+ raw_payload_sql = " raw_payload = "
210
+ raw_payload_sql << (message.raw_payload ? "'#{::Mysql.escape_string(message.raw_payload)}'" : 'NULL')
211
+ update_sql = %{
212
+ update #{message_queue_table}
213
+ set tasktype = "#{message.tasktype}",
214
+ #{raw_payload_sql},
215
+ payload_type = "#{message.payload_type}",
216
+ updated_on = "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}",
217
+ tran_id = NULL
218
+ #{timeout_sql}
219
+ where task_id = #{message.task_id}
220
+ }
221
+ rows = update(update_sql)
222
+
223
+ raise Skynet::RequestExpiredError.new() if rows == 0
224
+ end
225
+
226
+ def take_result(job_id,timeout=1)
227
+ start = Time.now
228
+ result = nil
229
+ sleep_time = 10
230
+ if timeout < 1
231
+ sleep_time = 1
232
+ elsif timeout > 10
233
+ sleep_time = 10
234
+ else
235
+ sleep_time = timeout * 0.25
236
+ end
237
+ message_row = nil
238
+
239
+ loop do
240
+ # message_row = take(Skynet::Message.result_template(job_id), start, timeout,sleep_time)
241
+ conditions = template_to_conditions(Skynet::Message.result_template(job_id))
242
+ # sleep_time ||= timeout
243
+
244
+ message_row = SkynetMessageQueue.find(:first,:conditions => conditions)
245
+
246
+ break if message_row
247
+
248
+ if Time.now.to_f > start.to_f + timeout
249
+ raise Skynet::RequestExpiredError.new
250
+ else
251
+ sleepy = rand(sleep_time)
252
+ # error "RESULT EMPTY SLEEPING: #{sleepy}"
253
+ sleep sleepy
254
+ next
255
+ end
256
+ next
257
+ end
258
+
259
+ result = Skynet::Message.new(message_row.clone.attributes)
260
+ message_row.destroy
261
+ return result if result
262
+ end
263
+
264
+ def list_tasks(iteration=nil)
265
+ conditions = template_to_conditions(Skynet::Message.outstanding_tasks_template(iteration))
266
+ SkynetMessageQueue.find(:all,:conditions => conditions)
267
+ end
268
+
269
+ def list_results
270
+ conditions = template_to_conditions(Skynet::Message.outstanding_results_template)
271
+ SkynetMessageQueue.find(:all,:conditions => conditions)
272
+ end
273
+
274
+ def write_error(message,error='',timeout=nil)
275
+ message.expire_time = nil
276
+ update_message_with_result(message.error_message(error),timeout)
277
+ end
278
+
279
+ def version_active?(curver=nil, queue_id=0)
280
+ return true unless curver
281
+ message_row = find_next_message(Skynet::Message.next_task_template(curver, nil, queue_id), :any, 1)
282
+ if message_row or curver.to_i == get_worker_version.to_i
283
+ true
284
+ else
285
+ false
286
+ end
287
+ end
288
+
289
+ def set_worker_version(ver=nil)
290
+ ver ||= 1
291
+ SkynetMessageQueue.connection.insert("replace #{message_queue_table} (tran_id, task_id, tasktype, version) values (0, 0, 'version',#{ver})")
292
+ ver
293
+ end
294
+
295
+ def get_worker_version
296
+ ver = SkynetMessageQueue.connection.select_value("select version from #{message_queue_table} where tran_id = 0 and tasktype = 'version'")
297
+ if not ver
298
+ begin
299
+ SkynetMessageQueue.connection.insert("insert into #{message_queue_table} (tran_id, task_id, tasktype, version) values (0, 0, 'version', 1)")
300
+ rescue ActiveRecord::StatementInvalid => e
301
+ if e.message =~ /Duplicate/
302
+ return get_worker_version
303
+ else
304
+ raise e
305
+ end
306
+ end
307
+ ver = 1
308
+ end
309
+ ver.to_i
310
+ end
311
+
312
+ def clear_outstanding_tasks
313
+ SkynetMessageQueue.connection.execute("delete from #{message_queue_table} where tasktype = 'task'")
314
+ end
315
+
316
+ def delete_expired_messages
317
+ SkynetMessageQueue.connection.execute("delete from #{message_queue_table} where (tasktype='result' and expire_time < #{Time.now.to_i}) OR (expire_time BETWEEN 1 AND '#{Time.now.to_i - 7200}' and iteration = -1) OR (expire_time BETWEEN 1 AND '#{Time.now.to_i - 36000}')")
318
+ end
319
+
320
+ # select hostname, iteration, count(id) as number_of_workers, count(iteration) as iteration, sum(processed) as processed, max(started_at) as most_recent_task_time from skynet_worker_queues where tasksubtype = 'worker' group by hostname, iteration;
321
+ #
322
+ # select hostname, count(id) as number_of_workers, sum(processed) as processed, max(started_at) as most_recent_task_time,
323
+ # CASE iteration WHEN NULL
324
+ #
325
+ # from skynet_worker_queues where tasksubtype = 'worker' group by hostname;
326
+
327
+ def stats
328
+ stats = {
329
+ :servers => {},
330
+ :results => 0,
331
+ :taken_tasks => 0,
332
+ :untaken_tasks => 0,
333
+ :taken_master_tasks => 0,
334
+ :taken_task_tasks => 0,
335
+ :untaken_master_tasks => 0,
336
+ :untaken_task_tasks => 0,
337
+ :failed_tasks => 0,
338
+ :untaken_future_tasks => 0,
339
+ :time => Time.now.to_f,
340
+ }
341
+
342
+ stats[:untaken_future_tasks] = SkynetWorkerQueue.connection.select_value(%{
343
+ SELECT count(id)
344
+ FROM #{message_queue_table}
345
+ WHERE expire_time > #{Time.now.to_i} and tasktype = 'task' and payload_type = 'master'
346
+ })
347
+
348
+ stat_rows = SkynetWorkerQueue.connection.select_all(%{
349
+ SELECT tasktype, payload_type, iteration, count(id) as number_of_tasks, expire_time
350
+ FROM #{message_queue_table}
351
+ WHERE expire_time <= #{Time.now.to_i}
352
+ GROUP BY tasktype, payload_type, iteration
353
+ })
354
+ stat_rows.each do |row|
355
+ if row["tasktype"] == "result" or row["payload_type"] == "result"
356
+ stats[:results] += row["number_of_tasks"].to_i
357
+ elsif row["tasktype"] == "task"
358
+ type_of_tasks = nil
359
+ if row["payload_type"] == "master"
360
+ type_of_tasks = :master_tasks
361
+ elsif row["payload_type"] == "task"
362
+ type_of_tasks = :task_tasks
363
+ end
364
+ if row["iteration"].to_i == 0
365
+ stats["untaken_#{type_of_tasks}".to_sym] += row["number_of_tasks"].to_i
366
+ stats[:untaken_tasks] += row["number_of_tasks"].to_i
367
+ elsif row["expire_time"].to_i < Time.now.to_i
368
+ stats[:failed_tasks] += row["number_of_tasks"].to_i
369
+ else
370
+ stats["taken_#{type_of_tasks}".to_sym] += row["number_of_tasks"].to_i
371
+ stats[:taken_tasks] += row["number_of_tasks"].to_i
372
+ end
373
+ end
374
+ end
375
+
376
+ stats[:time] = Time.now.to_f - stats[:time]
377
+ stats
378
+ end
379
+
380
+ private
381
+
382
+ def update(sql)
383
+ rows = 0
384
+ 3.times do
385
+ begin
386
+ SkynetMessageQueue.transaction do
387
+ rows = SkynetMessageQueue.connection.update(sql)
388
+ end
389
+ return rows
390
+ rescue ActiveRecord::StatementInvalid => e
391
+ if e.message =~ /Deadlock/ or e.message =~ /Transaction/
392
+ error "#{self.class} update had collision #{e.message}"
393
+ sleep 0.2
394
+ next
395
+ else
396
+ raise e
397
+ end
398
+ end
399
+ end
400
+ return rows
401
+ end
402
+
403
+ Skynet::CONFIG[:MYSQL_TEMPERATURE_CHANGE_SLEEP] ||= 40
404
+
405
+ def find_next_message(template, payload_type, temperature=nil)
406
+ conditions = template_to_conditions(template)
407
+ temperature ||= temperature(payload_type)
408
+ temperature_sql = (temperature > 1 ? " AND id % #{temperature.ceil} = #{rand(temperature).to_i} " : '')
409
+
410
+ ### Mqke sure we get the old ones. If we order by on ever select its VERY expensive.
411
+ order_by = (payload_type != :master and rand(100) < 5) ? "ORDER BY payload_type desc, created_on desc" : ''
412
+
413
+ sql = <<-SQL
414
+ SELECT *
415
+ FROM #{message_queue_table}
416
+ WHERE #{conditions} #{temperature_sql}
417
+ #{order_by}
418
+ LIMIT 1
419
+ SQL
420
+
421
+ SkynetMessageQueue.find_by_sql(sql).first
422
+ end
423
+
424
+ # Skynet::CONFIG[:temperature_growth_rate] ||= 2
425
+ # Skynet::CONFIG[:temperature_backoff_rate] ||= 0.75
426
+
427
+ # TUNEABLE_SETTINGS = [:temp_pow, :temp_interval, :sleep_time]
428
+ #
429
+ # def write_score(new_values,new_result,score)
430
+ # values ||= {}
431
+ # set = new_values.keys.sort.collect{|k|[k,new_values[k]]}.join(",")
432
+ # if not values[set]
433
+ # values[set] ||= {}
434
+ # values[set][:results] ||= []
435
+ # values[set][:scores] ||= []
436
+ # values[set][:settings] ||= {}
437
+ # values[set][:total_score] = 0
438
+ # TUNEABLE_SETTINGS.each do |setting|
439
+ # values[set][:settings][setting] = []
440
+ # end
441
+ # end
442
+ # TUNEABLE_SETTINGS.each do |setting, value|
443
+ # values[set][:settings][setting] << value
444
+ # end
445
+ # values[set][:results] << new_result
446
+ # values[set][:scores] << score + values[set][:total_score]
447
+ # values[set][:total_score] += score
448
+ # end
449
+
450
+ @@temperature ||= {}
451
+ @@temperature[:task] ||= 1
452
+ @@temperature[:master] ||= 1
453
+ @@temperature[:any] ||= 1
454
+
455
+ def temperature(payload_type)
456
+ payload_type ||= :any
457
+ payload_type = payload_type.to_sym
458
+ @@temperature[payload_type.to_sym]
459
+ end
460
+
461
+ Skynet::CONFIG[:MYSQL_QUEUE_TEMP_POW] ||= 0.6
462
+
463
+ def set_temperature(payload_type, conditions, queue_id=0)
464
+ payload_type ||= :any
465
+ payload_type = payload_type.to_sym
466
+
467
+ temp_q_conditions = "queue_id = #{queue_id} AND type = '#{payload_type}' AND updated_on < '#{(Time.now - 5).strftime('%Y-%m-%d %H:%M:%S')}'"
468
+ # "POW(#{(rand(40) + 40) * 0.01})"
469
+ # its almost like the temperature table needs to store the POW and adjust that to be adaptive. Like some % of the time it
470
+ # uses the one in the table, and some % it tries a new one and scores it.
471
+ begin
472
+ temperature = SkynetMessageQueue.connection.select_value(%{select (
473
+ CASE WHEN (@t:=FLOOR(
474
+ POW(@c:=(SELECT count(*) FROM #{message_queue_table} WHERE #{conditions}
475
+ ),#{Skynet::CONFIG[:MYSQL_QUEUE_TEMP_POW]}))) < 1 THEN 1 ELSE @t END) from skynet_queue_temperature WHERE #{temp_q_conditions}
476
+ })
477
+ if temperature
478
+ rows = update("UPDATE skynet_queue_temperature SET temperature = #{temperature} WHERE #{temp_q_conditions}")
479
+ @@temperature[payload_type.to_sym] = temperature.to_f
480
+ else
481
+ sleepy = rand Skynet::CONFIG[:MYSQL_TEMPERATURE_CHANGE_SLEEP]
482
+ sleep sleepy
483
+ @@temperature[payload_type.to_sym] = get_temperature(payload_type, queue_id)
484
+ end
485
+ rescue ActiveRecord::StatementInvalid => e
486
+ if e.message =~ /away/
487
+ ActiveRecord::Base.connection.reconnect!
488
+ SkynetMessageQueue.connection.reconnect!
489
+ end
490
+ end
491
+ # update("UPDATE skynet_queue_temperature SET type = '#{payload_type}', temperature = CASE WHEN @t:=FLOOR(SQRT(select count(*) from #{message_queue_table} WHERE #{conditions})) < 1 THEN 1 ELSE @t END")
492
+ # tasks = SkynetMessageQueue.connection.select_value("select count(*) from #{message_queue_table} WHERE #{conditions}").to_i
493
+ # sleep 4 if payload_type == :tasks and tasks < 100
494
+ # @@temperature[payload_type.to_sym] = tasks ** 0.5
495
+ # @@temperature[payload_type.to_sym] *= multiplier
496
+ @@temperature[payload_type.to_sym] = 1 if @@temperature[payload_type.to_sym] < 1
497
+ end
498
+
499
+ def get_temperature(payload_type, queue_id=0)
500
+ payload_type ||= :any
501
+ payload_type = payload_type.to_sym
502
+ value = SkynetMessageQueue.connection.select_value("select temperature from skynet_queue_temperature WHERE type = '#{payload_type}'").to_f
503
+ if not value
504
+ SkynetMessageQueue.connection.execute("insert into skynet_queue_temperature (queue_id,type,temperature) values (#{queue_id},'#{payload_type}',#{@@temperature[payload_type.to_sym]})")
505
+ end
506
+ value
507
+ end
508
+ end
509
+ end