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,91 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
module Tengine::Core::KernelRuntime # Kernelにincludeされます
|
5
|
+
|
6
|
+
def safety_processing_headers(headers, event, ack_policy)
|
7
|
+
@ack_called = false
|
8
|
+
@processing_headers, @event, @ack_policy = headers, event, ack_policy
|
9
|
+
begin
|
10
|
+
yield if block_given?
|
11
|
+
ensure
|
12
|
+
@processing_headers, @event, @ack_policy = nil, nil, nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def ack_policies
|
17
|
+
@ack_policies ||= { }
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_ack_policy(event_type_name, policy)
|
21
|
+
ack_policies[event_type_name.to_s] = policy.to_sym
|
22
|
+
end
|
23
|
+
|
24
|
+
def ack_policy_for(event)
|
25
|
+
Tengine.logger.debug("ack_policies: #{ack_policies.inspect}")
|
26
|
+
ack_policy = ack_policies[event.event_type_name.to_s] || :at_first
|
27
|
+
end
|
28
|
+
|
29
|
+
def ack
|
30
|
+
unless @ack_called
|
31
|
+
@ack_called = true
|
32
|
+
@processing_headers.ack
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def ack?
|
37
|
+
@ack_called
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def submit
|
42
|
+
if @submitted_handlers
|
43
|
+
@submitted_handlers << @handler
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def all_submitted?
|
48
|
+
return false if @submitted_handlers.nil? || @handlers.nil?
|
49
|
+
(@handlers - @submitted_handlers).empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def processing_event?; @processing_event; end
|
54
|
+
|
55
|
+
|
56
|
+
def em_setup_blocks
|
57
|
+
@em_setup_blocks ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def safety_processing_event(headers)
|
63
|
+
@processing_event = true
|
64
|
+
begin
|
65
|
+
yield if block_given?
|
66
|
+
ensure
|
67
|
+
@processing_event = false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def safty_handlers(handlers)
|
72
|
+
@handlers = handlers
|
73
|
+
@submitted_handlers = (@ack_policy == :after_all_handler_submit ? [] : nil)
|
74
|
+
begin
|
75
|
+
yield if block_given?
|
76
|
+
ensure
|
77
|
+
@handlers = nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def safety_handler(handler)
|
82
|
+
@handler = handler
|
83
|
+
@submitted_handler = nil
|
84
|
+
begin
|
85
|
+
yield if block_given?
|
86
|
+
ensure
|
87
|
+
@handler = nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'tengine/core'
|
2
|
+
|
3
|
+
module Tengine::Core::MethodTraceable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :disabled
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_trace(*symbols)
|
10
|
+
symbols.each do |symbol|
|
11
|
+
original_method = :"_unmethod_traceable_#{symbol}"
|
12
|
+
class_eval(<<-EOS, __FILE__, __LINE__ +1)
|
13
|
+
if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
|
14
|
+
raise "Already method_tracing #{symbol}" # raise "Already memoized mime_type"
|
15
|
+
end # end
|
16
|
+
alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
|
17
|
+
|
18
|
+
def #{symbol}(*args, &block)
|
19
|
+
disabled = Tengine::Core::MethodTraceable.disabled
|
20
|
+
begin
|
21
|
+
Tengine::Core::stdout_logger.info("\#{self.class.name}##{symbol} called") unless disabled
|
22
|
+
result = #{original_method}(*args, &block)
|
23
|
+
Tengine::Core::stdout_logger.info("\#{self.class.name}##{symbol} complete") unless disabled
|
24
|
+
return result
|
25
|
+
rescue Exception => e
|
26
|
+
unless e.instance_variable_get(:@__traced__) || disabled
|
27
|
+
Tengine::Core::stderr_logger.error("\#{self.class.name}##{symbol} failure. [\#{e.class.name}] \#{e.message}\n " << e.backtrace.join("\n "))
|
28
|
+
e.instance_variable_set(:@__traced__, true)
|
29
|
+
end
|
30
|
+
raise
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
EOS
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine_core'
|
3
|
+
require 'mongoid'
|
4
|
+
require 'mongoid/version'
|
5
|
+
require 'mongoid/cursor'
|
6
|
+
|
7
|
+
if Mongoid::VERSION <= "3.0.0"
|
8
|
+
class Mongoid::Cursor
|
9
|
+
# https://github.com/mongoid/mongoid/pull/1609
|
10
|
+
def each
|
11
|
+
loop do
|
12
|
+
retry_on_connection_failure do
|
13
|
+
return unless document = cursor.next
|
14
|
+
yield Mongoid::Factory.from_db(klass, document)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
# Tengine::Core::Mutexは(若干残念な実装の)分散ロック機構です。これを用
|
5
|
+
# いることで、とあるMutexをロックしているプロセスを同時にたかだか一つに
|
6
|
+
# 制限することが可能になります。
|
7
|
+
#
|
8
|
+
# mutex = Tengine::Core::Mutex.new "foo"
|
9
|
+
# mutex.synchronize do
|
10
|
+
# ...
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# #### 問題点 ####
|
14
|
+
#
|
15
|
+
# このクラスは正しく使わないと正しく使えません。
|
16
|
+
#
|
17
|
+
# * スケールしません。当然の話ですが一カ所につき一つのプロセスしか動か
|
18
|
+
# ないので、何個プロセスを並列に動かしても無駄です。
|
19
|
+
#
|
20
|
+
# * トランザクションではありません。あくまでプロセスが同時に動くのを止
|
21
|
+
# めているだけです。データの整合性に関していっさい感知できません。
|
22
|
+
#
|
23
|
+
# * スピンロックです。MongoDBの制限による。したがって非効率です。
|
24
|
+
#
|
25
|
+
# * ロックしているプロセスが何かの弾みで死ぬかもしれないわけです。そう
|
26
|
+
# すると誰もアンロックできなくて詰みます。これを避けるために、一定の
|
27
|
+
# 時間がたつとロックは勝手に外れるようになっています(一番残念な部分)。
|
28
|
+
# 長時間ロックするようなプロセスは勝手にロックが外れないようにときど
|
29
|
+
# きロックを更新する必要があります。これにはheartbeatを使います。
|
30
|
+
#
|
31
|
+
# mutex.synchronize do
|
32
|
+
# ...
|
33
|
+
# mutex.heartbeat
|
34
|
+
# ...
|
35
|
+
# mutex.heartbeat
|
36
|
+
# ...
|
37
|
+
# mutex.heartbeat
|
38
|
+
# ...
|
39
|
+
# end
|
40
|
+
|
41
|
+
################################################################################
|
42
|
+
|
43
|
+
# Poor man's distributed lock.
|
44
|
+
Tengine::Core::Mutex = Struct.new :mutex, :_id, :recursive
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
class Tengine::Core::Mutex::Mutex
|
48
|
+
|
49
|
+
include Mongoid::Document
|
50
|
+
|
51
|
+
field :ttl, :type => Float
|
52
|
+
field :waiters, :type => Array
|
53
|
+
|
54
|
+
def self.find_or_create name, ttl
|
55
|
+
collection.driver.update(
|
56
|
+
{ :_id => name },
|
57
|
+
{ "$set" => { :ttl => ttl, }, },
|
58
|
+
{ :upsert => true, :safe => true, :multiple => false, }
|
59
|
+
)
|
60
|
+
return find(name)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# 暫定対応[Bug]mongodbフェールオーバ中にtengine_resource_watchdが落ちてしまう
|
66
|
+
def _update q = {}, r
|
67
|
+
retry_count = 100
|
68
|
+
idx = 1
|
69
|
+
begin
|
70
|
+
self.class.collection.driver.update({ :_id => _id, }.update(q), r, {:safe=>true})
|
71
|
+
reload
|
72
|
+
rescue Mongo::ConnectionFailure => e
|
73
|
+
idx += 1
|
74
|
+
sleep 0.5
|
75
|
+
retry if retry_count > idx
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
public
|
80
|
+
|
81
|
+
# delete stale locks
|
82
|
+
def invalidate
|
83
|
+
# can this be done via standard mongoid queries?
|
84
|
+
_update("$pull" => { :waiters => { :timeout => { "$lt" => Time.now, }, }, } )
|
85
|
+
end
|
86
|
+
|
87
|
+
# attempt to gain lock
|
88
|
+
def lock id
|
89
|
+
_update("$push" => { :waiters => { :_id => id._id, :timeout => Time.now + ttl, }, })
|
90
|
+
end
|
91
|
+
|
92
|
+
# attempt to release lock
|
93
|
+
def unlock id
|
94
|
+
_update("$pull" => { :waiters => { :_id => id._id, }, })
|
95
|
+
end
|
96
|
+
|
97
|
+
# attempt to refresh lock
|
98
|
+
def heartbeat id
|
99
|
+
_update(
|
100
|
+
{ :_id => _id, "waiters._id" => id._id, },
|
101
|
+
{ "$set" => { "waiters.$.timeout" => Time.now + ttl, }, }
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Tengine::Core::Mutex
|
107
|
+
|
108
|
+
class << self
|
109
|
+
|
110
|
+
alias oldnew new
|
111
|
+
private :oldnew
|
112
|
+
|
113
|
+
# @param [String] name Mutex name. One process at once can gain a lock against a name.
|
114
|
+
# @param [Numeric] ttl Time to auto-release a gained lock.
|
115
|
+
# @return [Tengine::Core::Mutex] An instance.
|
116
|
+
def new name, ttl=2.048
|
117
|
+
t = 0.0 + ttl # type check
|
118
|
+
raise TypeError, "finite numeric expected (got #{t})" unless t.finite?
|
119
|
+
raise ArgumentError, "TTL doesn't make sense." unless t > 0
|
120
|
+
|
121
|
+
return oldnew(Tengine::Core::Mutex::Mutex.find_or_create(name, t), BSON::ObjectId.new, 0)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def lock_attempt
|
128
|
+
m = mutex
|
129
|
+
m.invalidate
|
130
|
+
m.lock self
|
131
|
+
return m.waiters.first["_id"] == _id
|
132
|
+
end
|
133
|
+
|
134
|
+
def lock
|
135
|
+
if lock_attempt
|
136
|
+
self.recursive += 1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def unlock
|
141
|
+
self.recursive -= 1
|
142
|
+
if self.recursive <= 0
|
143
|
+
mutex.unlock self
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
public
|
148
|
+
|
149
|
+
# delays until you get a lock.
|
150
|
+
def synchronize
|
151
|
+
raise ArgumentError, "no block given" unless block_given?
|
152
|
+
|
153
|
+
if lock
|
154
|
+
# OK, locked
|
155
|
+
EM.schedule do
|
156
|
+
begin
|
157
|
+
heartbeat
|
158
|
+
yield
|
159
|
+
ensure
|
160
|
+
unlock
|
161
|
+
end
|
162
|
+
end
|
163
|
+
else
|
164
|
+
# NG, try again later
|
165
|
+
EM.add_timer mutex.ttl do
|
166
|
+
synchronize do
|
167
|
+
yield
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# If you need to lock it longer than ttl, call this and you can refresh the ttl.
|
174
|
+
def heartbeat
|
175
|
+
mutex.heartbeat self
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
require 'active_support/core_ext/array/extract_options'
|
5
|
+
|
6
|
+
module Tengine::Core::OptimisticLock
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
cattr_accessor :lock_optimistically, :instance_writer => false
|
11
|
+
self.lock_optimistically = true
|
12
|
+
|
13
|
+
class << self
|
14
|
+
alias_method :locking_field=, :set_locking_field
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class RetryOverError < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_with_lock(options = {})
|
22
|
+
retry_count = options[:retry] || 5
|
23
|
+
idx = 1
|
24
|
+
while idx <= retry_count
|
25
|
+
yield
|
26
|
+
return if __find_and_modify__
|
27
|
+
reload
|
28
|
+
idx += 1
|
29
|
+
end
|
30
|
+
raise RetryOverError, "retried #{retry_count} times but failed to update"
|
31
|
+
end
|
32
|
+
|
33
|
+
def __find_and_modify__
|
34
|
+
lock_field_name = self.class.locking_field
|
35
|
+
current_version = self.send(lock_field_name)
|
36
|
+
hash = as_document.dup
|
37
|
+
new_version = current_version + 1
|
38
|
+
hash[lock_field_name] = new_version
|
39
|
+
result = self.class.collection.find_and_modify({
|
40
|
+
:query => {:_id => self.id, lock_field_name.to_sym => current_version},
|
41
|
+
:update => hash
|
42
|
+
})
|
43
|
+
send("#{lock_field_name}=", new_version)
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
# ActiveRecord::Locking::Optimistic::ClassMethods を参考に実装しています
|
48
|
+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locking/optimistic.rb
|
49
|
+
module ClassMethods
|
50
|
+
DEFAULT_LOCKING_FIELD = 'lock_version'.freeze
|
51
|
+
|
52
|
+
# Set the field to use for optimistic locking. Defaults to +lock_version+.
|
53
|
+
def set_locking_field(value = nil, &block)
|
54
|
+
define_attr_method :locking_field, value, &block
|
55
|
+
value
|
56
|
+
end
|
57
|
+
|
58
|
+
# The version field used for optimistic locking. Defaults to +lock_version+.
|
59
|
+
def locking_field
|
60
|
+
reset_locking_field
|
61
|
+
end
|
62
|
+
|
63
|
+
# Reset the field used for optimistic locking back to the +lock_version+ default.
|
64
|
+
def reset_locking_field
|
65
|
+
set_locking_field DEFAULT_LOCKING_FIELD
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'tengine/core'
|
3
|
+
|
4
|
+
class Tengine::Core::Plugins
|
5
|
+
attr_reader :modules
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@modules = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(plugin_module)
|
12
|
+
return if modules.include?(plugin_module)
|
13
|
+
Tengine::Core.stdout_logger.info("#{self.class.name}#add(#{plugin_module.name})")
|
14
|
+
modules << plugin_module
|
15
|
+
enable_plugin(plugin_module)
|
16
|
+
plugin_module
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify(sender, msg)
|
20
|
+
if block_given?
|
21
|
+
notify(sender, :"before_#{msg}")
|
22
|
+
yield
|
23
|
+
notify(sender, :"after_#{msg}")
|
24
|
+
else
|
25
|
+
modules.each do |m|
|
26
|
+
m.notify(sender, msg) if m.respond_to?(:notify)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# 自動でログ出力する
|
33
|
+
extend Tengine::Core::MethodTraceable
|
34
|
+
method_trace(:add)
|
35
|
+
|
36
|
+
private
|
37
|
+
def enable_plugin(plugin_module)
|
38
|
+
if loader = find_sub_module(plugin_module, :DslLoader, :dsl_loader)
|
39
|
+
# Tengine::Core::DslLoadingContext.send(:include, loader)
|
40
|
+
Tengine::Core::Kernel.top.singleton_class.send(:include, loader)
|
41
|
+
end
|
42
|
+
if binder = find_sub_module(plugin_module, :DslBinder, :dsl_binder)
|
43
|
+
# Tengine::Core::DslBindingContext.send(:include, binder)
|
44
|
+
# Tengine::Core::Kernel.top.singleton_class.send(:include, binder)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def find_sub_module(plugin_module, const_name, method_name)
|
50
|
+
plugin_module.const_defined?(const_name) ? plugin_module.const_get(const_name) :
|
51
|
+
plugin_module.respond_to?(method_name) ? plugin_module.send(method_name) : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|