ruote-beanstalk 2.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +90 -0
- data/TODO.txt +20 -0
- data/doc/storages.graffle/QuickLook/Preview.pdf +0 -0
- data/doc/storages.graffle/QuickLook/Thumbnail.tiff +0 -0
- data/doc/storages.graffle/data.plist +809 -0
- data/doc/storages.graffle/image1.png +0 -0
- data/doc/storages.graffle/image2.png +0 -0
- data/doc/storages.png +0 -0
- data/lib/ruote-beanstalk.rb +3 -0
- data/lib/ruote/beanstalk.rb +8 -0
- data/lib/ruote/beanstalk/fork.rb +56 -0
- data/lib/ruote/beanstalk/participant.rb +147 -0
- data/lib/ruote/beanstalk/receiver.rb +149 -0
- data/lib/ruote/beanstalk/storage.rb +308 -0
- data/lib/ruote/beanstalk/version.rb +7 -0
- data/ruote-beanstalk.gemspec +85 -0
- data/serve.rb +11 -0
- data/test/functional/base.rb +17 -0
- data/test/functional/ft_0_participant.rb +102 -0
- data/test/functional/ft_1_receiver.rb +112 -0
- data/test/functional/test.rb +9 -0
- data/test/functional_connection.rb +19 -0
- data/test/test.rb +9 -0
- metadata +165 -0
Binary file
|
Binary file
|
data/doc/storages.png
ADDED
Binary file
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Ruote
|
27
|
+
module Beanstalk
|
28
|
+
|
29
|
+
OPT_KEYS = { :address => 'l', :port => 'p', :binlog => 'b', :user => 'u' }
|
30
|
+
|
31
|
+
# :address
|
32
|
+
# :port
|
33
|
+
# :binlog
|
34
|
+
# :user
|
35
|
+
#
|
36
|
+
def self.fork (opts={})
|
37
|
+
|
38
|
+
quiet = opts.delete(:quiet)
|
39
|
+
no_kill = opts.delete(:no_kill_at_exit)
|
40
|
+
|
41
|
+
opts = opts.inject([]) { |a, (k, v)| a << "-#{OPT_KEYS[k]} #{v}" }.join(' ')
|
42
|
+
|
43
|
+
cpid = Process.fork do
|
44
|
+
puts "beanstalkd #{opts}" unless quiet
|
45
|
+
exec "beanstalkd #{opts}"
|
46
|
+
end
|
47
|
+
|
48
|
+
unless no_kill
|
49
|
+
at_exit { Process.kill(9, cpid) }
|
50
|
+
end
|
51
|
+
|
52
|
+
cpid
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'beanstalk-client'
|
26
|
+
|
27
|
+
#require 'ruote/part/local_participant'
|
28
|
+
|
29
|
+
|
30
|
+
module Ruote
|
31
|
+
module Beanstalk
|
32
|
+
|
33
|
+
#
|
34
|
+
# This participant emits workitems towards a beanstalk queue.
|
35
|
+
#
|
36
|
+
# engine.register_participant(
|
37
|
+
# :heavy_labour,
|
38
|
+
# :reply_by_default => true, :beanstalk => '127.0.0.1:11300')
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# == workitem format
|
42
|
+
#
|
43
|
+
# Workitems are encoded in the format
|
44
|
+
#
|
45
|
+
# [ 'workitem', workitem.to_h ]
|
46
|
+
#
|
47
|
+
# and then serialized as JSON strings.
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# == cancel items
|
51
|
+
#
|
52
|
+
# Like workitems, but the format is
|
53
|
+
#
|
54
|
+
# [ 'cancelitem', fei.to_h, flavour.to_s ]
|
55
|
+
#
|
56
|
+
# where fei is the FlowExpressionId of the expression getting cancelled
|
57
|
+
# (and whose workitems are to be retired) and flavour is either 'cancel' or
|
58
|
+
# 'kill'.
|
59
|
+
#
|
60
|
+
#
|
61
|
+
# == extending this participant
|
62
|
+
#
|
63
|
+
# Extend and overwrite encode_workitem and encode_cancelitem or
|
64
|
+
# simply re-open the class and change those methods.
|
65
|
+
#
|
66
|
+
#
|
67
|
+
# == :beanstalk
|
68
|
+
#
|
69
|
+
# Indicates which beanstalk to talk to
|
70
|
+
#
|
71
|
+
# engine.register_participant(
|
72
|
+
# 'alice'
|
73
|
+
# Ruote::Beanstalk::BsParticipant,
|
74
|
+
# 'beanstalk' => '127.0.0.1:11300')
|
75
|
+
#
|
76
|
+
#
|
77
|
+
# == :tube
|
78
|
+
#
|
79
|
+
# Most of the time, you want the workitems (or the cancelitems) to be
|
80
|
+
# emitted over/in a specific tube
|
81
|
+
#
|
82
|
+
# engine.register_participant(
|
83
|
+
# 'alice'
|
84
|
+
# Ruote::Beanstalk::BsParticipant,
|
85
|
+
# 'beanstalk' => '127.0.0.1:11300',
|
86
|
+
# 'tube' => 'ruote-workitems')
|
87
|
+
#
|
88
|
+
#
|
89
|
+
# == :reply_by_default
|
90
|
+
#
|
91
|
+
# If the participant is configured with 'reply_by_default' => true, the
|
92
|
+
# participant will dispatch the workitem over to Beanstalk and then
|
93
|
+
# immediately reply to its ruote engine (letting the flow resume).
|
94
|
+
#
|
95
|
+
# engine.register_participant(
|
96
|
+
# 'alice'
|
97
|
+
# Ruote::Beanstalk::BsParticipant,
|
98
|
+
# 'beanstalk' => '127.0.0.1:11300',
|
99
|
+
# 'reply_by_default' => true)
|
100
|
+
#
|
101
|
+
class BsParticipant
|
102
|
+
|
103
|
+
include Ruote::LocalParticipant
|
104
|
+
|
105
|
+
def initialize (opts)
|
106
|
+
|
107
|
+
@opts = opts
|
108
|
+
end
|
109
|
+
|
110
|
+
def consume (workitem)
|
111
|
+
|
112
|
+
connection.put(encode_workitem(workitem))
|
113
|
+
|
114
|
+
reply(workitem) if @opts['reply_by_default']
|
115
|
+
end
|
116
|
+
|
117
|
+
def cancel (fei, flavour)
|
118
|
+
|
119
|
+
connection.put(encode_cancelitem(fei, flavour))
|
120
|
+
end
|
121
|
+
|
122
|
+
def encode_workitem (workitem)
|
123
|
+
|
124
|
+
Rufus::Json.encode([ 'workitem', workitem.to_h ])
|
125
|
+
end
|
126
|
+
|
127
|
+
def encode_cancelitem (fei, flavour)
|
128
|
+
|
129
|
+
Rufus::Json.encode([ 'cancelitem', fei.to_h, flavour.to_s ])
|
130
|
+
end
|
131
|
+
|
132
|
+
protected
|
133
|
+
|
134
|
+
def connection
|
135
|
+
|
136
|
+
con = ::Beanstalk::Connection.new(@opts['beanstalk'])
|
137
|
+
|
138
|
+
if tube = @opts['tube']
|
139
|
+
con.use(tube)
|
140
|
+
end
|
141
|
+
|
142
|
+
con
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'beanstalk-client'
|
26
|
+
|
27
|
+
#require 'ruote/receiver/base'
|
28
|
+
|
29
|
+
|
30
|
+
module Ruote
|
31
|
+
module Beanstalk
|
32
|
+
|
33
|
+
#
|
34
|
+
# An error class for error emitted by the "remote side" and received here.
|
35
|
+
#
|
36
|
+
class BsReceiveError < RuntimeError
|
37
|
+
|
38
|
+
attr_reader :fei
|
39
|
+
|
40
|
+
def initialize (fei)
|
41
|
+
@fei = fei
|
42
|
+
super("for #{Ruote::FlowExpressionId.to_storage_id(fei)}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Whereas BsParticipant emits workitems (and cancelitems) to a Beanstalk
|
48
|
+
# queue, the receiver watches a Beanstalk queue/tube.
|
49
|
+
#
|
50
|
+
# An example initialization :
|
51
|
+
#
|
52
|
+
# Ruote::Beanstalk::BsReceiver.new(
|
53
|
+
# engine, '127.0.0.1:11300', :tube => 'out')
|
54
|
+
#
|
55
|
+
#
|
56
|
+
# == workitem format
|
57
|
+
#
|
58
|
+
# BsParticipant and BsReceiver share the same format :3
|
59
|
+
#
|
60
|
+
# [ 'workitem', workitem_as_a_hash ]
|
61
|
+
# # or
|
62
|
+
# [ 'error', error_details_as_a_string ]
|
63
|
+
#
|
64
|
+
#
|
65
|
+
# == extending this receiver
|
66
|
+
#
|
67
|
+
# Feel free to extend this class and override the listen or the process
|
68
|
+
# method.
|
69
|
+
#
|
70
|
+
#
|
71
|
+
# == :tube
|
72
|
+
#
|
73
|
+
# Indicates to the receiver which beanstalk tube it should listen to.
|
74
|
+
#
|
75
|
+
# Ruote::Beanstalk::BsReceiver.new(
|
76
|
+
# engine, '127.0.0.1:11300', :tube => 'out')
|
77
|
+
#
|
78
|
+
class BsReceiver < Ruote::Receiver
|
79
|
+
|
80
|
+
# cwes = context, worker, engine or storage
|
81
|
+
#
|
82
|
+
def initialize (cwes, beanstalk, options={})
|
83
|
+
|
84
|
+
super(cwes, options)
|
85
|
+
|
86
|
+
Thread.new do
|
87
|
+
listen(beanstalk, options['tube'] || options[:tube] || 'default')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
|
93
|
+
def listen (beanstalk, tube)
|
94
|
+
|
95
|
+
con = ::Beanstalk::Connection.new(beanstalk)
|
96
|
+
con.watch(tube)
|
97
|
+
con.ignore('default') unless tube == 'default'
|
98
|
+
|
99
|
+
loop do
|
100
|
+
|
101
|
+
job = con.reserve
|
102
|
+
job.delete
|
103
|
+
process(job)
|
104
|
+
end
|
105
|
+
|
106
|
+
rescue EOFError => ee
|
107
|
+
# over
|
108
|
+
end
|
109
|
+
|
110
|
+
# Is meant to return a hash with a first element that is either
|
111
|
+
# 'workitem', 'error' or 'launchitem' (a type).
|
112
|
+
# The second element depends on the type.
|
113
|
+
# It's mappend on Ruote::Beanstalk::BsParticipant anyway.
|
114
|
+
#
|
115
|
+
def decode (job)
|
116
|
+
|
117
|
+
Rufus::Json.decode(job.body)
|
118
|
+
end
|
119
|
+
|
120
|
+
def process (job)
|
121
|
+
|
122
|
+
type, data = decode(job)
|
123
|
+
|
124
|
+
if type == 'workitem'
|
125
|
+
|
126
|
+
# data holds a workitem (as a Hash)
|
127
|
+
|
128
|
+
reply(data)
|
129
|
+
|
130
|
+
elsif type == 'error'
|
131
|
+
|
132
|
+
# data holds a fei (FlowExpressionId) (as a Hash)
|
133
|
+
|
134
|
+
@context.error_handler.action_handle(
|
135
|
+
'dispatch', data, BsReceiveError.new(data))
|
136
|
+
|
137
|
+
elsif type == 'launchitem'
|
138
|
+
|
139
|
+
pdef, fields, variables = data
|
140
|
+
|
141
|
+
launch(pdef, fields, variables)
|
142
|
+
|
143
|
+
#else simply drop
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
@@ -0,0 +1,308 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'fileutils'
|
26
|
+
require 'beanstalk-client'
|
27
|
+
#require 'ruote/storage/base'
|
28
|
+
|
29
|
+
|
30
|
+
module Ruote
|
31
|
+
module Beanstalk
|
32
|
+
|
33
|
+
#
|
34
|
+
# An error class just for BsStorage.
|
35
|
+
#
|
36
|
+
class BsStorageError < RuntimeError
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# This ruote storage can be used in two modes : client and server.
|
41
|
+
#
|
42
|
+
# Beanstalk is the medium.
|
43
|
+
#
|
44
|
+
# == client
|
45
|
+
#
|
46
|
+
# The storage is pointed at a beanstalk queue
|
47
|
+
#
|
48
|
+
# engine = Ruote::Engine.new(
|
49
|
+
# Ruote::Worker.new(
|
50
|
+
# Ruote::Beanstalk::BsStorage.new('127.0.0.1:11300', opts)))
|
51
|
+
#
|
52
|
+
# All the operations (put, get, get_many, ...) of the storage are done
|
53
|
+
# by a server, connected to the same beanstalk queue.
|
54
|
+
#
|
55
|
+
# == server
|
56
|
+
#
|
57
|
+
# The storage point to a beanstalk queue and receives orders from clients
|
58
|
+
# via the queue.
|
59
|
+
#
|
60
|
+
# Ruote::Beanstalk::BsStorage.new(':11300', 'ruote_work', :fork => true)
|
61
|
+
#
|
62
|
+
# Note the directory passed as a string. When in server mode, this storage
|
63
|
+
# uses an embedded Ruote::FsStorage for the actual storage.
|
64
|
+
#
|
65
|
+
# The :fork => true lets the storage start and adjacent OS process containing
|
66
|
+
# the Beanstalk server. The storage takes care of stopping the beanstalk
|
67
|
+
# server when the Ruby process exits.
|
68
|
+
#
|
69
|
+
class BsStorage
|
70
|
+
|
71
|
+
include Ruote::StorageBase
|
72
|
+
|
73
|
+
def initialize (uri, directory=nil, options=nil)
|
74
|
+
|
75
|
+
@uri, address, port = split_uri(uri)
|
76
|
+
|
77
|
+
directory, @options = if directory.nil?
|
78
|
+
[ nil, {} ]
|
79
|
+
elsif directory.is_a?(Hash)
|
80
|
+
[ nil, directory ]
|
81
|
+
else
|
82
|
+
[ directory, options || {} ]
|
83
|
+
end
|
84
|
+
|
85
|
+
@cloche = nil
|
86
|
+
|
87
|
+
if directory
|
88
|
+
#
|
89
|
+
# run embedded Ruote::FsStorage
|
90
|
+
|
91
|
+
require 'rufus/cloche'
|
92
|
+
|
93
|
+
FileUtils.mkdir_p(directory)
|
94
|
+
|
95
|
+
@cloche = Rufus::Cloche.new(
|
96
|
+
:dir => directory, :nolock => @options['cloche_nolock'])
|
97
|
+
end
|
98
|
+
|
99
|
+
if fork_opts = @options[:fork]
|
100
|
+
#
|
101
|
+
# run beanstalk in a forked process
|
102
|
+
|
103
|
+
fork_opts = fork_opts.is_a?(Hash) ? fork_opts : {}
|
104
|
+
fork_opts = { :address => address, :port => port }.merge(fork_opts)
|
105
|
+
|
106
|
+
Ruote::Beanstalk.fork(fork_opts)
|
107
|
+
|
108
|
+
sleep 0.1
|
109
|
+
end
|
110
|
+
|
111
|
+
put_configuration
|
112
|
+
|
113
|
+
serve if @cloche
|
114
|
+
end
|
115
|
+
|
116
|
+
def put (doc, opts={})
|
117
|
+
|
118
|
+
doc.merge!('put_at' => Ruote.now_to_utc_s)
|
119
|
+
|
120
|
+
return @cloche.put(doc, opts) if @cloche
|
121
|
+
|
122
|
+
r = operate('put', [ doc ])
|
123
|
+
|
124
|
+
return r unless r.nil?
|
125
|
+
|
126
|
+
doc['_rev'] = (doc['_rev'] || -1) + 1 if opts[:update_rev]
|
127
|
+
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def get (type, key)
|
132
|
+
|
133
|
+
return @cloche.get(type, key) if @cloche
|
134
|
+
|
135
|
+
operate('get', [ type, key ])
|
136
|
+
end
|
137
|
+
|
138
|
+
def delete (doc)
|
139
|
+
|
140
|
+
return @cloche.delete(doc) if @cloche
|
141
|
+
|
142
|
+
operate('delete', [ doc ])
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_many (type, key=nil, opts={})
|
146
|
+
|
147
|
+
return @cloche.get_many(type, key, opts) if @cloche
|
148
|
+
|
149
|
+
operate('get_many', [ type, key, opts ])
|
150
|
+
end
|
151
|
+
|
152
|
+
def ids (type)
|
153
|
+
|
154
|
+
return @cloche.ids(type) if @cloche
|
155
|
+
|
156
|
+
operate('ids', [ type ])
|
157
|
+
end
|
158
|
+
|
159
|
+
def purge!
|
160
|
+
|
161
|
+
if @cloche
|
162
|
+
FileUtils.rm_rf(@cloche.dir)
|
163
|
+
else
|
164
|
+
operate('purge!', [])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def dump (type)
|
169
|
+
|
170
|
+
get_many(type)
|
171
|
+
end
|
172
|
+
|
173
|
+
def shutdown
|
174
|
+
|
175
|
+
Thread.list.each do |t|
|
176
|
+
t.keys.each do |k|
|
177
|
+
next unless k.match(/^BeanstalkConnection\_/)
|
178
|
+
t[k].close
|
179
|
+
t[k] = nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Mainly used by ruote's test/unit/ut_17_storage.rb
|
185
|
+
#
|
186
|
+
def add_type (type)
|
187
|
+
|
188
|
+
# nothing to do
|
189
|
+
end
|
190
|
+
|
191
|
+
# Nukes a db type and reputs it (losing all the documents that were in it).
|
192
|
+
#
|
193
|
+
def purge_type! (type)
|
194
|
+
|
195
|
+
if @cloche
|
196
|
+
@cloche.purge_type!(type)
|
197
|
+
else
|
198
|
+
operate('purge_type!', [ type ])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
protected
|
203
|
+
|
204
|
+
CONN_KEY = '__ruote_beanstalk_connection'
|
205
|
+
TUBE_NAME = 'ruote-storage-commands'
|
206
|
+
|
207
|
+
def split_uri (uri)
|
208
|
+
|
209
|
+
uri = ':' if uri == ''
|
210
|
+
|
211
|
+
address, port = uri.split(':')
|
212
|
+
address = '127.0.0.1' if address.strip == ''
|
213
|
+
port = 11300 if port.strip == ''
|
214
|
+
|
215
|
+
[ "#{address}:#{port}", address, port ]
|
216
|
+
end
|
217
|
+
|
218
|
+
def connection
|
219
|
+
|
220
|
+
c = Thread.current[CONN_KEY]
|
221
|
+
return c if c
|
222
|
+
|
223
|
+
c = ::Beanstalk::Connection.new(@uri, TUBE_NAME)
|
224
|
+
c.ignore('default')
|
225
|
+
|
226
|
+
Thread.current[CONN_KEY] = c
|
227
|
+
end
|
228
|
+
|
229
|
+
# Don't put configuration if it's already in
|
230
|
+
#
|
231
|
+
# (avoid storages from trashing configuration...)
|
232
|
+
#
|
233
|
+
def put_configuration
|
234
|
+
|
235
|
+
return if get('configurations', 'engine')
|
236
|
+
|
237
|
+
put({ '_id' => 'engine', 'type' => 'configurations' }.merge(@options))
|
238
|
+
end
|
239
|
+
|
240
|
+
def operate (command, params)
|
241
|
+
|
242
|
+
client_id = "BsStorage-#{Thread.current.object_id}-#{$$}"
|
243
|
+
timestamp = Time.now.to_f.to_s
|
244
|
+
|
245
|
+
con = connection
|
246
|
+
|
247
|
+
con.put(Rufus::Json.encode([ command, params, client_id, timestamp ]))
|
248
|
+
|
249
|
+
con.watch(client_id)
|
250
|
+
con.ignore(TUBE_NAME)
|
251
|
+
|
252
|
+
result = nil
|
253
|
+
|
254
|
+
# NOTE : what about a timeout ?
|
255
|
+
|
256
|
+
loop do
|
257
|
+
|
258
|
+
job = con.reserve
|
259
|
+
job.delete
|
260
|
+
|
261
|
+
result, ts = Rufus::Json.decode(job.body)
|
262
|
+
|
263
|
+
break if ts == timestamp # hopefully
|
264
|
+
end
|
265
|
+
|
266
|
+
if result.is_a?(Array) && result.first == 'error'
|
267
|
+
raise ArgumentError.new(result.last) if result[1] == 'ArgumentError'
|
268
|
+
raise BsStorageError.new(result.last)
|
269
|
+
end
|
270
|
+
|
271
|
+
result
|
272
|
+
end
|
273
|
+
|
274
|
+
COMMANDS = %w[ put get get_many delete ids purge! purge_type! dump ]
|
275
|
+
|
276
|
+
def serve
|
277
|
+
|
278
|
+
con = connection
|
279
|
+
|
280
|
+
loop do
|
281
|
+
|
282
|
+
job = con.reserve
|
283
|
+
job.delete
|
284
|
+
|
285
|
+
command, params, client_id, timestamp = Rufus::Json.decode(job.body)
|
286
|
+
|
287
|
+
result = begin
|
288
|
+
|
289
|
+
if COMMANDS.include?(command)
|
290
|
+
send(command, *params)
|
291
|
+
else
|
292
|
+
[ 'error', 'UnknownCommand', command ]
|
293
|
+
end
|
294
|
+
|
295
|
+
rescue Exception => e
|
296
|
+
#p e
|
297
|
+
#e.backtrace.each { |l| puts l }
|
298
|
+
[ 'error', e.class.to_s, e.to_s ]
|
299
|
+
end
|
300
|
+
|
301
|
+
con.use(client_id)
|
302
|
+
con.put(Rufus::Json.encode([ result, timestamp ]))
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|