tengine_core 0.5.28
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +40 -0
- data/Gemfile.lock +95 -0
- data/README.md +54 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/bin/tengine_atd +8 -0
- data/bin/tengine_heartbeat_watchd +8 -0
- data/bin/tengined +182 -0
- data/examples/VERSION +1 -0
- data/examples/uc01_execute_processing_for_event.rb +11 -0
- data/examples/uc02_fire_another_event.rb +16 -0
- data/examples/uc03_2handlers_for_1event.rb +16 -0
- data/examples/uc08_if_both_a_and_b_occurs.rb +11 -0
- data/examples/uc10_if_the_event_occurs_at_the_server.rb +15 -0
- data/examples/uc50_commit_event_at_first.rb +17 -0
- data/examples/uc51_commit_event_at_first_submit.rb +29 -0
- data/examples/uc52_commit_event_after_all_handler_submit.rb +31 -0
- data/examples/uc52_never_commit_event_unless_all_handler_submit.rb +31 -0
- data/examples/uc60_event_in_handler.rb +18 -0
- data/examples/uc62_session_in_driver.rb +16 -0
- data/examples/uc64_safety_countup.rb +14 -0
- data/examples/uc70_driver_enabled_on_activation.rb +13 -0
- data/examples/uc71_driver_disabled_on_activation.rb +14 -0
- data/examples/uc72_setup_eventmachine.rb +17 -0
- data/examples/uc80_raise_io_error.rb +10 -0
- data/examples/uc81_raise_runtime_error.rb +10 -0
- data/examples2/driver01.rb +18 -0
- data/examples2/driver02.rb +19 -0
- data/examples2/uc08_if_both_a_and_b_occurs.rb +13 -0
- data/examples2/uc10_if_the_event_occurs_at_the_server.rb +18 -0
- data/examples2/uc51_commit_event_at_first_submit_1.rb +16 -0
- data/examples2/uc51_commit_event_at_first_submit_2.rb +17 -0
- data/examples2/uc51_commit_event_at_first_submit_3.rb +17 -0
- data/examples2/uc62_session_in_driver.rb +16 -0
- data/examples2/uc71_driver_disabled_on_activation.rb +16 -0
- data/failure_examples/VERSION +1 -0
- data/failure_examples/uc53_submit_outside_of_handler.rb +15 -0
- data/failure_examples/uc61_event_outside_of_handler.rb +12 -0
- data/failure_examples/uc63_session_outside_of_driver.rb +13 -0
- data/lib/tengine/core.rb +74 -0
- data/lib/tengine/core/bootstrap.rb +123 -0
- data/lib/tengine/core/collection_accessible.rb +34 -0
- data/lib/tengine/core/config.rb +10 -0
- data/lib/tengine/core/config/atd.rb +225 -0
- data/lib/tengine/core/config/core.rb +319 -0
- data/lib/tengine/core/config/heartbeat_watcher.rb +229 -0
- data/lib/tengine/core/connection_test/.gitignore +1 -0
- data/lib/tengine/core/connection_test/fire_bar_on_foo.rb +16 -0
- data/lib/tengine/core/driveable.rb +213 -0
- data/lib/tengine/core/driver.rb +69 -0
- data/lib/tengine/core/driver/finder.rb +42 -0
- data/lib/tengine/core/dsl_evaluator.rb +110 -0
- data/lib/tengine/core/dsl_filter_def.rb +11 -0
- data/lib/tengine/core/dsl_loader.rb +108 -0
- data/lib/tengine/core/event.rb +145 -0
- data/lib/tengine/core/event/finder.rb +82 -0
- data/lib/tengine/core/event_exception_reportable.rb +88 -0
- data/lib/tengine/core/event_wrapper.rb +21 -0
- data/lib/tengine/core/find_by_name.rb +31 -0
- data/lib/tengine/core/handler.rb +152 -0
- data/lib/tengine/core/handler_path.rb +33 -0
- data/lib/tengine/core/heartbeat_watcher.rb +161 -0
- data/lib/tengine/core/io_to_logger.rb +22 -0
- data/lib/tengine/core/kernel.rb +510 -0
- data/lib/tengine/core/kernel_runtime.rb +91 -0
- data/lib/tengine/core/method_traceable.rb +38 -0
- data/lib/tengine/core/mongoid_fix.rb +19 -0
- data/lib/tengine/core/mutex.rb +177 -0
- data/lib/tengine/core/optimistic_lock.rb +69 -0
- data/lib/tengine/core/plugins.rb +54 -0
- data/lib/tengine/core/schedule.rb +21 -0
- data/lib/tengine/core/scheduler.rb +156 -0
- data/lib/tengine/core/selectable_attr.rb +29 -0
- data/lib/tengine/core/session.rb +21 -0
- data/lib/tengine/core/session_wrapper.rb +68 -0
- data/lib/tengine/core/setting.rb +21 -0
- data/lib/tengine/core/validation.rb +36 -0
- data/lib/tengine/errors.rb +18 -0
- data/lib/tengine/rspec.rb +8 -0
- data/lib/tengine/rspec/context_wrapper.rb +51 -0
- data/lib/tengine/rspec/extension.rb +53 -0
- data/lib/tengine_core.rb +23 -0
- data/spec/factories/tengine_core_drivers.rb +10 -0
- data/spec/factories/tengine_core_events.rb +14 -0
- data/spec/factories/tengine_core_handler_paths.rb +9 -0
- data/spec/factories/tengine_core_handlers.rb +9 -0
- data/spec/factories/tengine_core_sessions.rb +9 -0
- data/spec/mongoid.yml +35 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/support/mongo_index_key_log.rb +91 -0
- data/spec/tengine/core/bootstrap_spec.rb +278 -0
- data/spec/tengine/core/bugfix/bind_dsl_file_in_multi_byte_dir_spec.rb +21 -0
- data/spec/tengine/core/bugfix/enabled_on_activation_spec.rb +112 -0
- data/spec/tengine/core/bugfix/receive_event_spec.rb +133 -0
- data/spec/tengine/core/bugfix/use_dsl_version_method.rb +12 -0
- data/spec/tengine/core/bugfix/use_dsl_version_method_spec.rb +28 -0
- data/spec/tengine/core/bugfix/use_event_in_handler_dsl.rb +11 -0
- data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
- data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
- data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
- data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
- data/spec/tengine/core/config/atd_spec.rb +62 -0
- data/spec/tengine/core/config/core_spec.rb +479 -0
- data/spec/tengine/core/config/heartbeat_watcher_spec.rb +62 -0
- data/spec/tengine/core/config/syntax_error_in_erb.yml.erb +13 -0
- data/spec/tengine/core/config/wrong_category_name.yml.erb +13 -0
- data/spec/tengine/core/config/wrong_field_name.yml.erb +12 -0
- data/spec/tengine/core/config/wrong_yaml.yml.erb +13 -0
- data/spec/tengine/core/config_spec/another_port.yml +54 -0
- data/spec/tengine/core/config_spec/config_with_dir_absolute_load_path.yml +16 -0
- data/spec/tengine/core/config_spec/config_with_dir_relative_load_path.yml +16 -0
- data/spec/tengine/core/config_spec/config_with_file_absolute_load_path.yml +16 -0
- data/spec/tengine/core/config_spec/config_with_file_relative_load_path.yml +16 -0
- data/spec/tengine/core/config_spec/log_config_spec.rb +235 -0
- data/spec/tengine/core/driveable_spec.rb +240 -0
- data/spec/tengine/core/driver_spec.rb +159 -0
- data/spec/tengine/core/dsl_loader_spec.rb +172 -0
- data/spec/tengine/core/dsls/uc08_if_both_a_and_b_occurs_spec.rb +35 -0
- data/spec/tengine/core/dsls/uc10_if_the_event_occurs_at_the_server_spec.rb +58 -0
- data/spec/tengine/core/dsls/uc50_commit_event_at_first_spec.rb +29 -0
- data/spec/tengine/core/dsls/uc52_commit_event_after_all_handler_submit_spec.rb +33 -0
- data/spec/tengine/core/dsls/uc52_never_commit_event_unless_all_handler_submit_spec.rb +37 -0
- data/spec/tengine/core/dsls/uc53_submit_outside_of_handler_spec.rb +37 -0
- data/spec/tengine/core/dsls/uc60_event_in_handler_spec.rb +31 -0
- data/spec/tengine/core/dsls/uc61_event_outside_of_handler_spec.rb +37 -0
- data/spec/tengine/core/dsls/uc62_session_in_driver_spec.rb +36 -0
- data/spec/tengine/core/dsls/uc63_session_outside_of_driver_spec.rb +35 -0
- data/spec/tengine/core/dsls/uc64_safety_countup_spec.rb +134 -0
- data/spec/tengine/core/dsls/uc70_driver_enabled_on_activation_spec.rb +39 -0
- data/spec/tengine/core/dsls/uc71_driver_disabled_on_activation_spec.rb +36 -0
- data/spec/tengine/core/dsls/uc72_setup_eventmachine_spec.rb +39 -0
- data/spec/tengine/core/dsls/uc80_raise_io_error_spec.rb +53 -0
- data/spec/tengine/core/dsls/uc81_raise_runtime_error_spec.rb +49 -0
- data/spec/tengine/core/event/finder_spec.rb +136 -0
- data/spec/tengine/core/event_exception_reportable_spec.rb +33 -0
- data/spec/tengine/core/event_spec.rb +161 -0
- data/spec/tengine/core/event_wrapper_spec.rb +35 -0
- data/spec/tengine/core/handler_path_spec.rb +87 -0
- data/spec/tengine/core/handler_spec.rb +190 -0
- data/spec/tengine/core/heartbeat_watcher_spec.rb +131 -0
- data/spec/tengine/core/io_to_logger_spec.rb +30 -0
- data/spec/tengine/core/kernel_spec.rb +885 -0
- data/spec/tengine/core/mutex_spec.rb +184 -0
- data/spec/tengine/core/optimistic_lock_spec.rb +55 -0
- data/spec/tengine/core/scheculer_spec.rb +121 -0
- data/spec/tengine/core/selectable_attr_spec.rb +30 -0
- data/spec/tengine/core/session_spec.rb +104 -0
- data/spec/tengine/core/setting_spec.rb +79 -0
- data/spec/tengine/core_spec.rb +13 -0
- data/spec/tengine_spec.rb +14 -0
- data/tengine_core.gemspec +248 -0
- data/tmp/log/.gitignore +1 -0
- data/tmp/tengined_status/.gitignore +1 -0
- metadata +421 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
module Tengine::Core::FindByName
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class Error < Tengine::Errors::NotFound
|
8
|
+
attr_reader :klass, :name, :options
|
9
|
+
def initialize(klass, name, options = nil)
|
10
|
+
@klass, @name, @options = klass, name, options
|
11
|
+
end
|
12
|
+
def message
|
13
|
+
result = "#{klass.name} named #{name.inspect}"
|
14
|
+
result << " with #{options.inspect}" if options && !options.empty?
|
15
|
+
result << ' not found'
|
16
|
+
result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def find_by_name(name)
|
22
|
+
first(:conditions => {:name => name})
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_by_name!(name, *args, &block)
|
26
|
+
result = find_by_name(name, *args, &block)
|
27
|
+
raise Error.new(self, name, args.last) unless result
|
28
|
+
result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
require 'tengine/event'
|
5
|
+
|
6
|
+
# イベントハンドラ
|
7
|
+
#
|
8
|
+
# Tengineコアは、イベントを受信するとそのイベント種別名にマッチするイベントハンドラを探して
|
9
|
+
# 見つかったイベントハンドラをすべて実行します。
|
10
|
+
class Tengine::Core::Handler
|
11
|
+
include Mongoid::Document
|
12
|
+
include Mongoid::Timestamps
|
13
|
+
include Tengine::Core::CollectionAccessible
|
14
|
+
include Tengine::Core::SelectableAttr
|
15
|
+
|
16
|
+
# @attribute 実行するRubyのブロックが定義されているファイル名
|
17
|
+
field :filepath, :type => String
|
18
|
+
|
19
|
+
# @attribute 実行するRubyのブロックが定義されているファイルでの行番号
|
20
|
+
field :lineno , :type => Integer
|
21
|
+
|
22
|
+
# @attribute 処理するイベントのイベント種別名の配列
|
23
|
+
field :event_type_names, :type => Array
|
24
|
+
array_text_accessor :event_type_names
|
25
|
+
|
26
|
+
# @attribute イベントが対象かどうかを判断するためのフィルタ定義
|
27
|
+
field :filter, :type => Hash, :default => {}
|
28
|
+
map_yaml_accessor :filter
|
29
|
+
|
30
|
+
# @attribute 実行対象の取得方法
|
31
|
+
field :target_instantiation_cd, :type => String, :default => '01'
|
32
|
+
|
33
|
+
selectable_attr :target_instantiation_cd do
|
34
|
+
entry '01', :binding , "binding"
|
35
|
+
entry '02', :static , "static"
|
36
|
+
entry '03', :instance_method, "instance_method"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @attribute 実行対象となるメソッドの名前
|
40
|
+
field :target_method_name, :type => String
|
41
|
+
|
42
|
+
|
43
|
+
validates :filepath, :presence => true
|
44
|
+
validates :lineno , :presence => true
|
45
|
+
|
46
|
+
embedded_in :driver, :class_name => "Tengine::Core::Driver"
|
47
|
+
|
48
|
+
def update_handler_path
|
49
|
+
event_type_names.each do |event_type_name|
|
50
|
+
Tengine::Core::HandlerPath.create!(:event_type_name => event_type_name,
|
51
|
+
:driver_id => self.driver.id, :handler_id => self.id)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# def process_event(event, &block)
|
56
|
+
# @caller = eval("self", block.binding)
|
57
|
+
# matched = match?(event)
|
58
|
+
# if matched
|
59
|
+
# # ハンドラの実行
|
60
|
+
# @caller.__safety_driver__(self.driver) do
|
61
|
+
# @caller.__safety_event__(event) do
|
62
|
+
# @caller.instance_eval(&block)
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
# ensure
|
67
|
+
# @caller = nil
|
68
|
+
# end
|
69
|
+
|
70
|
+
def process_event(event)
|
71
|
+
case self.target_instantiation_key
|
72
|
+
when :instance_method then
|
73
|
+
klass = driver.target_class_name.constantize
|
74
|
+
inst = klass.new
|
75
|
+
inst.instance_variable_set(:@__event__, event)
|
76
|
+
m = inst.method(target_method_name)
|
77
|
+
m.arity == 0 ? m.call : m.call(event)
|
78
|
+
when :static then
|
79
|
+
klass = driver.target_class_name.constantize
|
80
|
+
m = klass.method(target_method_name)
|
81
|
+
m.arity == 0 ? m.call : m.call(event)
|
82
|
+
when :binding then
|
83
|
+
# do nothing
|
84
|
+
else
|
85
|
+
raise Tengine::Core::KernelError, "Unsupported target_instantiation_key: #{self.target_instantiation_key.inspect}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def fire(event_type_name)
|
90
|
+
@caller.fire(event_type_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def match?(event)
|
94
|
+
result = filter.blank? ? true : Visitor.new(filter, event, driver.session).visit
|
95
|
+
Tengine.logger.debug("match?(#{event.event_type_name.inspect}) => #{result.inspect}")
|
96
|
+
result
|
97
|
+
end
|
98
|
+
|
99
|
+
# HashとArrayで入れ子になったfilterのツリーをルートから各Leafの方向に辿っていくVisitorです。
|
100
|
+
# 正確にはVisitorパターンではないのですが、似ているのでメタファとしてVisitorとしました。
|
101
|
+
class Visitor
|
102
|
+
def initialize(filter, event, session)
|
103
|
+
@filter = filter
|
104
|
+
@event = event
|
105
|
+
@session = Tengine::Core::SessionWrapper.new(session)
|
106
|
+
@current = @filter
|
107
|
+
end
|
108
|
+
|
109
|
+
def visit
|
110
|
+
Tengine.logger.debug("visiting #{@current.inspect}")
|
111
|
+
send(@current['method'])
|
112
|
+
end
|
113
|
+
|
114
|
+
def backup_current(node)
|
115
|
+
backup = @current
|
116
|
+
@current = node
|
117
|
+
begin
|
118
|
+
return yield
|
119
|
+
ensure
|
120
|
+
@current = backup
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def and
|
125
|
+
children = @current["children"]
|
126
|
+
# children.all?{|child| backup_current(child){ visit }} # これだと全てのchildrenについて評価せずfalseがあったら処理を抜けてしまいます。
|
127
|
+
children.map{|child| backup_current(child){ visit }}.all?
|
128
|
+
end
|
129
|
+
|
130
|
+
def find_or_mark_in_session
|
131
|
+
name = @current['pattern'].to_s
|
132
|
+
key = "mark_#{name}"
|
133
|
+
if name == @event.event_type_name
|
134
|
+
unless @session.system_properties[key]
|
135
|
+
@session.system_update(key => true)
|
136
|
+
Tengine.logger.debug("session.system_updated #{@session.system_properties.inspect}")
|
137
|
+
end
|
138
|
+
return true
|
139
|
+
else
|
140
|
+
return @session.system_properties[key]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def match_source_name?
|
145
|
+
pattern = @current['pattern']
|
146
|
+
@event.source_name.include?(pattern)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'tengine/core'
|
2
|
+
|
3
|
+
class Tengine::Core::HandlerPath
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongoid::Timestamps
|
6
|
+
|
7
|
+
field :event_type_name, :type => String
|
8
|
+
field :handler_id, :type => Object
|
9
|
+
|
10
|
+
belongs_to :driver, :index => true, :class_name => "Tengine::Core::Driver"
|
11
|
+
|
12
|
+
scope(:event_type_name, lambda{|v| where(:event_type_name => v)})
|
13
|
+
|
14
|
+
index([ [:event_type_name, Mongo::ASCENDING], [:_id, Mongo::ASCENDING], ])
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def find_handlers(event_type_name)
|
18
|
+
paths = self.event_type_name(event_type_name).to_a
|
19
|
+
driver_id_to_handler_id = paths.inject({}) do |d, path|
|
20
|
+
d[path.driver_id] ||= []
|
21
|
+
d[path.driver_id] << path.handler_id
|
22
|
+
d
|
23
|
+
end
|
24
|
+
drivers = Tengine::Core::Driver.any_in(:_id => paths.map(&:driver_id)).and(:enabled => true, :version => default_driver_version)
|
25
|
+
drivers.map do |driver|
|
26
|
+
driver.handlers.any_in(:_id => driver_id_to_handler_id[driver.id])
|
27
|
+
end.flatten
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :default_driver_version
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'daemons'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'mongoid'
|
5
|
+
require 'uuid'
|
6
|
+
require 'active_support/core_ext/hash/keys'
|
7
|
+
|
8
|
+
$LOAD_PATH.push File.expand_path("../../../../lib/", __FILE__)
|
9
|
+
|
10
|
+
require 'tengine/core'
|
11
|
+
require 'tengine/event'
|
12
|
+
require 'tengine/mq'
|
13
|
+
|
14
|
+
# explicit loading
|
15
|
+
require_relative 'config/heartbeat_watcher'
|
16
|
+
require_relative 'method_traceable'
|
17
|
+
require_relative 'mongoid_fix'
|
18
|
+
|
19
|
+
class Tengine::Core::HeartbeatWatcher
|
20
|
+
|
21
|
+
def initialize argv
|
22
|
+
@uuid = UUID.new.generate
|
23
|
+
@config = Tengine::Core::Config::HeartbeatWatcher.parse argv
|
24
|
+
@daemonize_options = {
|
25
|
+
:app_name => 'tengine_heartbeat_watcher',
|
26
|
+
:ARGV => [@config[:action]],
|
27
|
+
:ontop => !@config[:process][:daemon],
|
28
|
+
:multiple => true,
|
29
|
+
:dir_mode => :normal,
|
30
|
+
:dir => File.expand_path(@config[:process][:pid_dir]),
|
31
|
+
}
|
32
|
+
Tengine::Core::MethodTraceable.disabled = !@config[:verbose]
|
33
|
+
rescue Exception
|
34
|
+
puts "[#{$!.class.name}] #{$!.message}\n " << $!.backtrace.join("\n ")
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
|
38
|
+
def pid
|
39
|
+
@pid ||= sprintf "process:%s/%d", ENV["MM_SERVER_NAME"], Process.pid
|
40
|
+
end
|
41
|
+
|
42
|
+
def sender
|
43
|
+
@sender ||= Tengine::Event::Sender.new Tengine::Mq::Suite.new(@config[:event_queue])
|
44
|
+
end
|
45
|
+
|
46
|
+
def send_last_event
|
47
|
+
sender.fire "finished.process.hbw.tengine", :key => @uuid, :source_name => pid, :sender_name => pid, :occurred_at => Time.now, :level_key => :info, :keep_connection => true
|
48
|
+
sender.stop
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_periodic_event
|
52
|
+
sender.fire "hbw.heartbeat.tengine", :key => @uuid, :source_name => pid, :sender_name => pid, :occurred_at => Time.now, :level_key => :debug, :keep_connection => true, :retry_count => 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_invalidate_event type, e0
|
56
|
+
obj = e0.as_document.symbolize_keys
|
57
|
+
Tengine.logger.info "Heartbeat expiration detected! for #{e0.event_type_name} of #{e0.source_name}: last seen #{e0.occurred_at} (#{(Time.now - e0.occurred_at).to_f} secs before)"
|
58
|
+
obj.delete :_id
|
59
|
+
obj.delete :confirmed
|
60
|
+
obj.delete :updated_at
|
61
|
+
obj.delete :created_at
|
62
|
+
obj.delete :lock_version
|
63
|
+
obj[:event_type_name] = type
|
64
|
+
obj[:level] = Tengine::Event::LEVELS_INV[:error]
|
65
|
+
e1 = Tengine::Event.new obj
|
66
|
+
sender.fire e1, :keep_connection => true
|
67
|
+
end
|
68
|
+
|
69
|
+
def search_for_invalid_heartbeat
|
70
|
+
t = Time.now
|
71
|
+
a = @config[:heartbeat].to_hash.each_pair.map do |e, h|
|
72
|
+
Tengine::Core::Event.where(
|
73
|
+
:event_type_name => "#{e}.heartbeat.tengine",
|
74
|
+
:occurred_at.lte => t - h[:expire]
|
75
|
+
)
|
76
|
+
end
|
77
|
+
a.flatten.each_next_tick do |i|
|
78
|
+
yield i if i
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def run(__file__)
|
83
|
+
case @config[:action].to_sym
|
84
|
+
when :start
|
85
|
+
start_daemon(__file__)
|
86
|
+
when :stop
|
87
|
+
stop_daemon(__file__)
|
88
|
+
when :restart
|
89
|
+
stop_daemon(__file__)
|
90
|
+
start_daemon(__file__)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def start_daemon(__file__)
|
95
|
+
pdir = File.expand_path @config[:process][:pid_dir]
|
96
|
+
fname = File.basename __file__
|
97
|
+
cwd = Dir.getwd
|
98
|
+
Daemons.run_proc(fname, @daemonize_options) do
|
99
|
+
Dir.chdir(cwd) { self.start }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def stop_daemon(__file__)
|
104
|
+
fname = File.basename __file__
|
105
|
+
Daemons.run_proc(fname, @daemonize_options)
|
106
|
+
end
|
107
|
+
|
108
|
+
def shutdown
|
109
|
+
EM.run do
|
110
|
+
EM.cancel_timer @invalidate if @invalidate
|
111
|
+
EM.cancel_timer @periodic if @periodic
|
112
|
+
send_last_event
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def start
|
117
|
+
@config.setup_loggers
|
118
|
+
|
119
|
+
Mongoid.config.from_hash @config[:db]
|
120
|
+
Mongoid.config.option :persist_in_safe_mode, :default => true
|
121
|
+
|
122
|
+
require 'amqp'
|
123
|
+
Mongoid.logger = AMQP::Session.logger = Tengine.logger
|
124
|
+
|
125
|
+
EM.run do
|
126
|
+
sender.wait_for_connection do
|
127
|
+
@invalidate = EM.add_periodic_timer 1 do # !!! MAGIC NUMBER
|
128
|
+
search_for_invalid_heartbeat do |obj|
|
129
|
+
type = case obj.event_type_name
|
130
|
+
when /job|core|hbw|resourcew|atd/ then
|
131
|
+
"expired.#$&.heartbeat.tengine"
|
132
|
+
end
|
133
|
+
EM.next_tick do
|
134
|
+
send_invalidate_event type, obj
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
int = @config[:heartbeat][:hbw][:interval].to_i
|
139
|
+
if int and int > 0
|
140
|
+
@periodic = EM.add_periodic_timer int do
|
141
|
+
send_periodic_event
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
extend Tengine::Core::MethodTraceable
|
149
|
+
method_trace(*instance_methods(false))
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Local Variables:
|
154
|
+
# mode: ruby
|
155
|
+
# coding: utf-8-unix
|
156
|
+
# indent-tabs-mode: nil
|
157
|
+
# tab-width: 4
|
158
|
+
# ruby-indent-level: 2
|
159
|
+
# fill-column: 79
|
160
|
+
# default-justification: full
|
161
|
+
# End:
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'tengine/core'
|
2
|
+
|
3
|
+
# see http://www.ruzee.com/blog/2006/11/redirecting-stdout-to-logger-with-ruby-on-rails
|
4
|
+
class Tengine::Core::IoToLogger
|
5
|
+
def initialize(logger, method_to_write = :info)
|
6
|
+
@logger = logger
|
7
|
+
@method_to_write = method_to_write
|
8
|
+
end
|
9
|
+
def puts(str)
|
10
|
+
@logger.send(@method_to_write, str.strip)
|
11
|
+
end
|
12
|
+
def write(str)
|
13
|
+
@logger.send(@method_to_write, str.strip)
|
14
|
+
end
|
15
|
+
alias_method :<<, :puts
|
16
|
+
|
17
|
+
def flush; end # ignore
|
18
|
+
|
19
|
+
alias_method :to_s, :inspect
|
20
|
+
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,510 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
require 'tengine/event'
|
5
|
+
require 'tengine/mq'
|
6
|
+
require 'eventmachine'
|
7
|
+
require 'selectable_attr'
|
8
|
+
|
9
|
+
class Tengine::Core::Kernel
|
10
|
+
include ::SelectableAttr::Base
|
11
|
+
include Tengine::Core::KernelRuntime
|
12
|
+
include Tengine::Core::EventExceptionReportable
|
13
|
+
|
14
|
+
attr_reader :config, :status
|
15
|
+
attr_accessor :before_delegate, :after_delegate
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@status = :initialized
|
19
|
+
@config = config
|
20
|
+
@processing_event = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
if block_given?
|
25
|
+
block = Proc.new
|
26
|
+
else
|
27
|
+
block = Proc.new do
|
28
|
+
self.stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
update_status(:starting)
|
32
|
+
bind
|
33
|
+
if config[:tengined][:wait_activation]
|
34
|
+
update_status(:waiting_activation)
|
35
|
+
wait_for_activation(&block)
|
36
|
+
else
|
37
|
+
activate(&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def stop(force = false)
|
42
|
+
if self.status == :running
|
43
|
+
update_status(:shutting_down)
|
44
|
+
mq.initiate_termination do
|
45
|
+
mq.unsubscribe do
|
46
|
+
EM.cancel_timer(@heartbeat_timer) if @heartbeat_timer
|
47
|
+
send_last_event do
|
48
|
+
close_if_shutting_down do
|
49
|
+
update_status(:terminated)
|
50
|
+
EM.stop
|
51
|
+
yield if block_given?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
update_status(:shutting_down)
|
58
|
+
# wait_for_actiontion中の処理を停止させる必要がある
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.top
|
63
|
+
@top ||= eval("self", TOPLEVEL_BINDING)
|
64
|
+
end
|
65
|
+
|
66
|
+
def dsl_context
|
67
|
+
unless @dsl_context
|
68
|
+
top = self.class.top
|
69
|
+
top.singleton_class.module_eval do
|
70
|
+
include Tengine::Core::DslLoader
|
71
|
+
end
|
72
|
+
top.__kernel__ = self
|
73
|
+
top.config = config
|
74
|
+
@dsl_context = top
|
75
|
+
end
|
76
|
+
@dsl_context
|
77
|
+
end
|
78
|
+
alias_method :context, :dsl_context
|
79
|
+
|
80
|
+
def evaluate
|
81
|
+
dsl_context.__evaluate__
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def bind
|
86
|
+
# dsl_context.__evaluate__
|
87
|
+
# Tengine::Core::stdout_logger.debug("Hanlder bindings:\n" << dsl_context.to_a.inspect)
|
88
|
+
# Tengine::Core::HandlerPath.default_driver_version = config.dsl_version
|
89
|
+
Tengine::Core::HandlerPath.default_driver_version = config.dsl_version
|
90
|
+
end
|
91
|
+
|
92
|
+
def wait_for_activation(&block)
|
93
|
+
activated = false
|
94
|
+
activation_file_name = "#{config[:tengined][:activation_dir]}\/tengined_#{Process.pid}.activation"
|
95
|
+
start_time = Time.now
|
96
|
+
while((Time.now - start_time).to_i <= config[:tengined][:activation_timeout].to_i) do
|
97
|
+
if File.exist?(activation_file_name)
|
98
|
+
# ファイルが見つかった
|
99
|
+
activated = true
|
100
|
+
break
|
101
|
+
end
|
102
|
+
sleep 1
|
103
|
+
end
|
104
|
+
if activated
|
105
|
+
File.delete(activation_file_name)
|
106
|
+
# activate開始
|
107
|
+
activate(&block)
|
108
|
+
else
|
109
|
+
update_status(:shutting_down)
|
110
|
+
raise Tengine::Core::ActivationTimeoutError, "activation file found timeout error."
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def activate
|
115
|
+
EM.run do
|
116
|
+
setup_mq_connection
|
117
|
+
subscribe_queue do
|
118
|
+
enable_heartbeat
|
119
|
+
yield(mq) if block_given? # このyieldは接続テストのための処理をTengine::Core:Bootstrapが定義するのに使われます。
|
120
|
+
em_setup_blocks.each{|block| block.call }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# subscribe to messages in the queue
|
126
|
+
def subscribe_queue
|
127
|
+
confirm = proc do |*|
|
128
|
+
# queueへの接続までできたら稼働中
|
129
|
+
# self.status_key = :running if mq.queue
|
130
|
+
update_status(:running)
|
131
|
+
yield if block_given?
|
132
|
+
end
|
133
|
+
mq.subscribe(:ack => true, :nowait => false, :confirm => confirm) do |headers, msg|
|
134
|
+
process_message(headers, msg)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [true] メッセージはイベントストアに保存された
|
139
|
+
# @return [それ以外] メッセージは保存されなかった。
|
140
|
+
def process_message(headers, msg)
|
141
|
+
safety_processing_event(headers) do
|
142
|
+
raw_event = parse_event(msg)
|
143
|
+
if raw_event.nil?
|
144
|
+
headers.ack
|
145
|
+
return false
|
146
|
+
end
|
147
|
+
if raw_event.key.blank?
|
148
|
+
Tengine.logger.warn("invalid event which has blank key: #{raw_event.inspect}")
|
149
|
+
headers.ack
|
150
|
+
return
|
151
|
+
end
|
152
|
+
|
153
|
+
delay = ((ENV['TENGINED_EVENT_DEBUG_DELAY'] || '0').to_f || 0.0)
|
154
|
+
sleep delay
|
155
|
+
|
156
|
+
begin
|
157
|
+
# ハートビートは *保存より前に* 特別扱いが必要
|
158
|
+
event = case raw_event.event_type_name
|
159
|
+
when /finished\.process\.([^.]+)\.tengine$/
|
160
|
+
save_heartbeat_ok(raw_event)
|
161
|
+
when /expired\.([^.]+)\.heartbeat\.tengine$/
|
162
|
+
save_heartbeat_ng(raw_event)
|
163
|
+
when /heartbeat\.tengine$/ # when の順番に注意
|
164
|
+
save_heartbeat_beat(raw_event)
|
165
|
+
when /(alert|stop)\.execution\.job\.tengine$/
|
166
|
+
save_scheduling_event(raw_event)
|
167
|
+
when /\.failed\.tengined$/
|
168
|
+
save_failed_event(raw_event)
|
169
|
+
else
|
170
|
+
save_event(raw_event)
|
171
|
+
end
|
172
|
+
|
173
|
+
rescue Mongo::OperationFailure, Mongoid::Errors::Validations => e
|
174
|
+
Tengine.logger.warn("failed to store an event.\n[#{e.class.name}] #{e.message}")
|
175
|
+
# Model.exists?だと上手くいかない時があるのでModel.whereを使っています
|
176
|
+
# fire_failed_event(raw_event) if Tengine::Core::Event.exists?(confitions: { key: raw_event.key, sender_name: raw_event.sender_name })
|
177
|
+
fire_failed_event(raw_event) if Tengine::Core::Event.where(:key => raw_event.key, :sender_name => raw_event.sender_name).count > 0
|
178
|
+
headers.ack
|
179
|
+
return false
|
180
|
+
|
181
|
+
rescue Exception => e
|
182
|
+
Tengine.logger.error("failed to save an event #{raw_event.inspect}\n[#{e.class.name}] #{e.message}")
|
183
|
+
headers.ack
|
184
|
+
return false
|
185
|
+
end
|
186
|
+
|
187
|
+
unless event
|
188
|
+
headers.ack
|
189
|
+
return false
|
190
|
+
end
|
191
|
+
event.kernel = self
|
192
|
+
|
193
|
+
ack_policy = ack_policy_for(event)
|
194
|
+
safety_processing_headers(headers, event, ack_policy) do
|
195
|
+
ack if ack_policy == :at_first
|
196
|
+
handlers = find_handlers(event)
|
197
|
+
safty_handlers(handlers) do
|
198
|
+
delegate(event, handlers)
|
199
|
+
headers.ack if all_submitted?
|
200
|
+
end
|
201
|
+
end
|
202
|
+
close_if_shutting_down
|
203
|
+
true
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
require 'uuid'
|
208
|
+
HEARTBEAT_EVENT_TYPE_NAME = "core.heartbeat.tengine".freeze
|
209
|
+
HEARTBEAT_ATTRIBUTES = {
|
210
|
+
:key => UUID.new.generate,
|
211
|
+
:level => Tengine::Event::LEVELS_INV[:debug],
|
212
|
+
:source_name => sprintf("process:%s/%d", ENV["MM_SERVER_NAME"], Process.pid),
|
213
|
+
:sender_name => sprintf("process:%s/%d", ENV["MM_SERVER_NAME"], Process.pid),
|
214
|
+
:retry_count => 0,
|
215
|
+
}.freeze
|
216
|
+
|
217
|
+
def enable_heartbeat
|
218
|
+
n = config[:heartbeat][:core][:interval].to_i
|
219
|
+
if n and n > 0
|
220
|
+
EM.defer do
|
221
|
+
@heartbeat_timer = EM.add_periodic_timer(n) do
|
222
|
+
Tengine::Core.stdout_logger.debug("sending heartbeat") if config[:verbose]
|
223
|
+
sender.fire(HEARTBEAT_EVENT_TYPE_NAME, HEARTBEAT_ATTRIBUTES.dup)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def fire(*args, &block)
|
230
|
+
sender.fire(*args, &block)
|
231
|
+
end
|
232
|
+
|
233
|
+
def sender
|
234
|
+
unless @sender
|
235
|
+
@sender = Tengine::Event::Sender.new(mq)
|
236
|
+
@sender.default_keep_connection = true
|
237
|
+
end
|
238
|
+
@sender
|
239
|
+
end
|
240
|
+
|
241
|
+
def mq
|
242
|
+
@mq ||= Tengine::Mq::Suite.new(config[:event_queue])
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
def setup_mq_connection
|
248
|
+
mq.add_hook :'connection.on_tcp_connection_failure' do |set|
|
249
|
+
case @status when :terminated, :shutting_down then
|
250
|
+
raise "Could not properly shut down; MQ broker is missing."
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# see http://rdoc.info/github/ruby-amqp/amqp/master/file/docs/ErrorHandling.textile#Recovering_from_network_connection_failures
|
255
|
+
# mq.connection raiases AMQP::TCPConnectionFailed unless connects to MQ.
|
256
|
+
mq.add_hook :'connection.on_error' do |conn, connection_close|
|
257
|
+
Tengine::Core.stderr_logger.error("mq.connection.on_error connection_close: " << connection_close.inspect)
|
258
|
+
end
|
259
|
+
mq.add_hook :'connection.on_tcp_connection_loss' do |conn, settings|
|
260
|
+
Tengine::Core.stderr_logger.warn("mq.connection.on_tcp_connection_loss.")
|
261
|
+
end
|
262
|
+
mq.add_hook :'connection.after_recovery' do |session, settings|
|
263
|
+
Tengine::Core.stderr_logger.info("mq.connection.after_recovery: recovered successfully.")
|
264
|
+
end
|
265
|
+
# on_open, on_closedに渡されたブロックは、何度再接続をしても最初の一度だけしか呼び出されないが、
|
266
|
+
# after_recovery(on_recovery)に渡されたブロックは、再接続の度に呼び出されます。
|
267
|
+
# connection.on_open{ Tengine::Core.stderr_logger.info "mq.connection.on_open first time" }
|
268
|
+
# connection.on_closed{ Tengine::Core.stderr_logger.info "mq.connection.on_closed first time" }
|
269
|
+
|
270
|
+
mq.add_hook :'channel.on_error' do |ch, channel_close|
|
271
|
+
Tengine::Core.stderr_logger.error("mq.channel.on_error channel_close: " << channel_close.inspect)
|
272
|
+
# raise channel_close.reply_text
|
273
|
+
# channel_close.reuse # channel.on_error時にどのように振る舞うべき?
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def parse_event(msg)
|
278
|
+
raw_event = Tengine::Event.parse(msg)
|
279
|
+
Tengine.logger.debug("received an event #{raw_event.inspect}")
|
280
|
+
return raw_event
|
281
|
+
rescue Exception => e
|
282
|
+
Tengine.logger.error("failed to parse a message because of [#{e.class.name}] #{e.message}.\n#{msg}")
|
283
|
+
return nil
|
284
|
+
end
|
285
|
+
|
286
|
+
def fire_failed_event(raw_event)
|
287
|
+
EM.next_tick do
|
288
|
+
# failedということはraw_eventはぶっこわれている。あらゆる仮定は無意味だ。
|
289
|
+
event_attributes = {
|
290
|
+
:level => Tengine::Event::LEVELS_INV[:error],
|
291
|
+
:properties => { :original_event => raw_event }
|
292
|
+
}
|
293
|
+
case etn = raw_event.event_type_name
|
294
|
+
when Tengine::Core::Event::EVENT_TYPE_NAME.format
|
295
|
+
Tengine.logger.debug("sending #{raw_event.event_type_name}.failed.tengined event.") if config[:verbose]
|
296
|
+
sender.fire("#{raw_event.event_type_name}.failed.tengined", event_attributes)
|
297
|
+
else
|
298
|
+
Tengine.logger.debug("sending failed.tengined event.") if config[:verbose]
|
299
|
+
sender.fire("failed.tengined", event_attributes)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def save_failed_event(raw_event)
|
305
|
+
# これに失敗したときにさらに failed_event を fire してしまうと無限
|
306
|
+
# に fire が続いてしまうので NG.
|
307
|
+
event = Tengine::Core::Event.create!(
|
308
|
+
raw_event.attributes.update(:confirmed => (raw_event.level.to_i <= config.confirmation_threshold)))
|
309
|
+
Tengine.logger.debug("saved an event #{event.inspect}")
|
310
|
+
event
|
311
|
+
rescue Mongo::OperationFailure => e
|
312
|
+
Tengine.logger.error("failed to save an event #{raw_event.inspect}\n[#{e.class.name}] #{e.message}")
|
313
|
+
# FIXME!!
|
314
|
+
# このままではログに埋もれてしまうのでなんとかすべき。
|
315
|
+
# 案1 : root@にメールを投げる
|
316
|
+
# 案2 : プロセスが死ぬ
|
317
|
+
# 案3 : ...
|
318
|
+
return nil
|
319
|
+
end
|
320
|
+
|
321
|
+
# 受信したイベントを登録
|
322
|
+
def save_event(raw_event)
|
323
|
+
event = Tengine::Core::Event.create!(
|
324
|
+
raw_event.attributes.update(:confirmed => (raw_event.level.to_i <= config.confirmation_threshold)))
|
325
|
+
Tengine.logger.debug("saved an event #{event.inspect}")
|
326
|
+
event
|
327
|
+
end
|
328
|
+
|
329
|
+
def save_scheduling_event(raw_event)
|
330
|
+
cond = {
|
331
|
+
:event_type_name => raw_event.event_type_name,
|
332
|
+
:source_name => raw_event.source_name,
|
333
|
+
}
|
334
|
+
event = Tengine::Core::Event.find_or_create_then_update_with_block cond do |event|
|
335
|
+
if event.new_record?
|
336
|
+
event.write_attributes raw_event.attributes
|
337
|
+
event.confirmed = (raw_event.level.to_i <= config.confirmation_threshold)
|
338
|
+
else
|
339
|
+
nil
|
340
|
+
end
|
341
|
+
end
|
342
|
+
case event
|
343
|
+
when FalseClass
|
344
|
+
Tengine.logger.error("failed to save event (after several retries). #{raw_event.inspect}")
|
345
|
+
when NilClass
|
346
|
+
Tengine.logger.debug("this event is duplicated, ignoring now. #{raw_event.inspect}")
|
347
|
+
else
|
348
|
+
Tengine.logger.debug("saved an event #{event.inspect}")
|
349
|
+
end
|
350
|
+
return event
|
351
|
+
end
|
352
|
+
|
353
|
+
def save_heartbeat_beat(raw_event)
|
354
|
+
event = Tengine::Core::Event.find_or_create_then_update_with_block :key => raw_event.key do |event|
|
355
|
+
# beatを保存していいのは、
|
356
|
+
# * 以前にひとつも登録がないとき
|
357
|
+
# * もうbeatが保存されているとき
|
358
|
+
# beatを保存してはいけないのは、
|
359
|
+
# * もうokが保存されているとき
|
360
|
+
# * もうngが保存されているとき
|
361
|
+
if event.new_record? or event.event_type_name == raw_event.event_type_name
|
362
|
+
event.write_attributes raw_event.attributes.update(:confirmed => (raw_event.level.to_i <= config.confirmation_threshold))
|
363
|
+
else
|
364
|
+
nil
|
365
|
+
end
|
366
|
+
end
|
367
|
+
case event
|
368
|
+
when FalseClass
|
369
|
+
Tengine.logger.error("failed to save event (after several retries). #{raw_event.inspect}")
|
370
|
+
when NilClass
|
371
|
+
Tengine.logger.debug("this event is duplicated, ignoring now. #{raw_event.inspect}")
|
372
|
+
else
|
373
|
+
Tengine.logger.debug("saved an event #{event.inspect}")
|
374
|
+
end
|
375
|
+
return event
|
376
|
+
end
|
377
|
+
|
378
|
+
def save_heartbeat_ng(raw_event)
|
379
|
+
event = Tengine::Core::Event.find_or_create_then_update_with_block :key => raw_event.key do |event|
|
380
|
+
# ngを保存していいのは、
|
381
|
+
# * 以前にひとつも登録がないとき
|
382
|
+
# * もうbeatが保存されているとき
|
383
|
+
# * もうokが保存されているとき
|
384
|
+
# ngを保存してはいけないのは、
|
385
|
+
# * もうngが保存されているとき
|
386
|
+
if event.new_record? or event.event_type_name != raw_event.event_type_name
|
387
|
+
event.write_attributes raw_event.attributes.update(:confirmed => (raw_event.level.to_i <= config.confirmation_threshold))
|
388
|
+
else
|
389
|
+
nil
|
390
|
+
end
|
391
|
+
end
|
392
|
+
case event
|
393
|
+
when FalseClass
|
394
|
+
Tengine.logger.error("failed to save event (after several retries). #{raw_event.inspect}")
|
395
|
+
when NilClass
|
396
|
+
Tengine.logger.debug("this event is duplicated, ignoring now. #{raw_event.inspect}")
|
397
|
+
else
|
398
|
+
Tengine.logger.debug("saved an event #{event.inspect}")
|
399
|
+
end
|
400
|
+
return event
|
401
|
+
end
|
402
|
+
|
403
|
+
def save_heartbeat_ok(raw_event)
|
404
|
+
event = Tengine::Core::Event.find_or_create_then_update_with_block :key => raw_event.key do |event|
|
405
|
+
# okを保存していいのは、
|
406
|
+
# * 以前にひとつも登録がないとき
|
407
|
+
# * もうbeatが保存されているとき
|
408
|
+
# okを保存してはいけないのは、
|
409
|
+
# * もうokが保存されているとき
|
410
|
+
# * もうngが保存されているとき
|
411
|
+
beat_type_name = raw_event.event_type_name.sub(/^finished\.process\.(.+?)\.tengine$/, "\\1.heartbeat.tengine")
|
412
|
+
if event.new_record? or event.event_type_name == beat_type_name
|
413
|
+
event.write_attributes raw_event.attributes.update(:confirmed => (raw_event.level.to_i <= config.confirmation_threshold))
|
414
|
+
else
|
415
|
+
nil
|
416
|
+
end
|
417
|
+
end
|
418
|
+
case event
|
419
|
+
when FalseClass
|
420
|
+
Tengine.logger.error("failed to save event (after several retries). #{raw_event.inspect}")
|
421
|
+
when NilClass
|
422
|
+
Tengine.logger.debug("this event is duplicated, ignoring now. #{raw_event.inspect}")
|
423
|
+
else
|
424
|
+
Tengine.logger.debug("saved an event #{event.inspect}")
|
425
|
+
end
|
426
|
+
return event
|
427
|
+
end
|
428
|
+
|
429
|
+
# イベントハンドラの取得
|
430
|
+
def find_handlers(event)
|
431
|
+
handlers = Tengine::Core::HandlerPath.find_handlers(event.event_type_name)
|
432
|
+
Tengine.logger.debug("handlers found for #{event.event_type_name.inspect}: " << handlers.map{|h| "#{h.driver.name} #{h.id.to_s}"}.join(", "))
|
433
|
+
handlers
|
434
|
+
end
|
435
|
+
|
436
|
+
def delegate(event, handlers)
|
437
|
+
before_delegate.call if before_delegate.respond_to?(:call)
|
438
|
+
handlers.each do |handler|
|
439
|
+
safety_handler(handler) do
|
440
|
+
# block = dsl_context.__block_for__(handler)
|
441
|
+
report_on_exception(dsl_context, event) do
|
442
|
+
# handler.process_event(event, &block)
|
443
|
+
if handler.match?(event)
|
444
|
+
handler.process_event(event)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
after_delegate.call if after_delegate.respond_to?(:call)
|
450
|
+
ActiveSupport::Dependencies.clear unless config.tengined.cache_drivers
|
451
|
+
end
|
452
|
+
|
453
|
+
def close_if_shutting_down
|
454
|
+
# unsubscribed されている場合は安全な停止を行う
|
455
|
+
# return if mq.queue.default_consumer
|
456
|
+
case status when :shutting_down, :terminated then
|
457
|
+
Tengine::Core.stdout_logger.warn("connection closing...")
|
458
|
+
mq.stop do
|
459
|
+
yield if block_given?
|
460
|
+
EM.stop
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
STATUS_LIST = [
|
466
|
+
:initialized, # 初期化済み
|
467
|
+
:starting, # 起動中
|
468
|
+
:waiting_activation, # 稼働要求待ち
|
469
|
+
:running, # 稼働中
|
470
|
+
:shutting_down, # 停止中
|
471
|
+
:terminated, # 停止済
|
472
|
+
].freeze
|
473
|
+
|
474
|
+
# TODO 状態遷移図、状態遷移表に基づいたチェックを入れるべき
|
475
|
+
# https://cacoo.com/diagrams/hwYJGxDuumYsmFzP#EBF87
|
476
|
+
def update_status(status)
|
477
|
+
Tengine::Core.stdout_logger.info("#{self.class.name}#update_status #{@status.inspect} ==================> #{status.inspect}")
|
478
|
+
raise ArgumentError, "Unkown status #{status.inspect}" unless STATUS_LIST.include?(status)
|
479
|
+
@status_filepath ||= File.expand_path("tengined_#{Process.pid}.status", config.status_dir)
|
480
|
+
@status = status
|
481
|
+
File.open(@status_filepath, "w"){|f| f.write(status.to_s)}
|
482
|
+
rescue Exception => e
|
483
|
+
Tengine::Core.stderr_logger.error("#{self.class.name}#update_status failure. [#{e.class.name}] #{e.message}\n " << e.backtrace.join("\n "))
|
484
|
+
raise e
|
485
|
+
end
|
486
|
+
|
487
|
+
def send_last_event
|
488
|
+
argh = HEARTBEAT_ATTRIBUTES.dup
|
489
|
+
argh[:level] = Tengine::Event::LEVELS_INV[:info]
|
490
|
+
argh.delete :retry_count # use default
|
491
|
+
sender.fire "finished.process.core.tengine", argh do
|
492
|
+
# 他のデーモンと違ってfinishedをfireしたからといってsender.stopし
|
493
|
+
# てよいとは限らない(裏でまだイベント処理中かも)
|
494
|
+
yield
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# 自動でログ出力する
|
499
|
+
extend Tengine::Core::MethodTraceable
|
500
|
+
method_trace(:start, :stop, :bind, :wait_for_activation, :activate,
|
501
|
+
:setup_mq_connection, :subscribe_queue, # :update_status, # update_statusは別途ログ出力します
|
502
|
+
:process_message, :parse_event, :fire_failed_event, :save_event,
|
503
|
+
:find_handlers, :delegate, :close_if_shutting_down, :enable_heartbeat
|
504
|
+
)
|
505
|
+
end
|
506
|
+
|
507
|
+
|
508
|
+
class Tengine::Core::ActivationTimeoutError < StandardError
|
509
|
+
end
|
510
|
+
|