ipc_transit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.test_files = FileList['test/t*.rb']
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
data/bin/trlist ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipc_transit'
3
+
4
+
5
+ IPCTransit.all_queues.each_pair do |qname, info|
6
+ puts "#{qname} = #{info['count']}"
7
+ end
8
+
9
+ #{"tqueue"=>{"qid"=>3, "count"=>0}, "test_qname"=>{"qid"=>2, "count"=>0}}
10
+ #
data/bin/trrecv ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipc_transit'
3
+ require 'json'
4
+
5
+ qname = ARGV[0]
6
+ nowait = ARGV[1]
7
+
8
+
9
+ #puts qname
10
+ #puts message
11
+
12
+ while ret = IPCTransit.receive('qname' => qname, 'nowait' => 1)
13
+ puts ret
14
+ end
15
+
data/bin/trsend ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipc_transit'
3
+ require 'json'
4
+
5
+ qname = ARGV[0]
6
+ message = JSON.parse(ARGV[1])
7
+
8
+
9
+ ret = IPCTransit.send('message' => message, 'qname' => qname)
10
+ puts ret
@@ -0,0 +1,253 @@
1
+ require 'json'
2
+ require 'SysVIPC'
3
+ include SysVIPC
4
+
5
+ ##
6
+ # Fast, serverless message queueing
7
+ #
8
+ # Author:: Dana M. Diederich (diederich@gmail.com)
9
+ # Copyright:: Copyright (c) 2012 Dana M. Diederich
10
+ # License:: Distributes under the same terms as Ruby
11
+
12
+ class IPCTransit
13
+ @@queues = {}
14
+
15
+ @@ipc_transit_wire_header_args = {
16
+ 'e' => {
17
+ 'json' => 1,
18
+ 'yaml' => 1,
19
+ },
20
+ 'c' => {
21
+ 'zlib' => 1,
22
+ 'snappy' => 1,
23
+ 'none' => 1,
24
+ },
25
+ }
26
+ @@ipc_transit_std_args = {
27
+ 'message' => 1,
28
+ 'qname' => 1,
29
+ 'nowait' => 1,
30
+ }
31
+
32
+
33
+ ##
34
+ # Send message to a queue
35
+ #
36
+ # Arguments:
37
+ # message - hash reference
38
+ # qname - name of queue to send to
39
+ # nowait - do not block if the queue is full (optional)
40
+
41
+ def self.send(args)
42
+ ret = nil
43
+ flags = IPC_NOWAIT
44
+ begin
45
+ key = self.get_queue_id(args)
46
+ mq = MessageQueue.new(key, IPC_CREAT | 0666)
47
+ ret = mq.snd(1, pack_message(args), flags)
48
+ rescue Exception => msg
49
+ puts "Exception: #{msg}"
50
+ end
51
+ return ret
52
+ end
53
+ ##
54
+ # Receive a message from a queue
55
+ #
56
+ # Arguments:
57
+ # qname - name of queue to send to
58
+ # nowait - do not block if the queue is full (optional)
59
+ # raw - return the full meta-data (optional)
60
+ #
61
+ # Returns:
62
+ # Normally: message
63
+ # Raw: the message and its meta data
64
+
65
+ def self.receive(args)
66
+ ret = nil
67
+ flags = 0
68
+ if args['nowait']
69
+ flags = IPC_NOWAIT
70
+ end
71
+ begin
72
+ key = self.get_queue_id(args)
73
+ mq = MessageQueue.new(key, IPC_CREAT | 0666)
74
+ args['serialized_wire_data'] = mq.receive(0, 10000, flags)
75
+ self.unpack_data(args)
76
+ rescue Exception => msg
77
+ # puts "Exception: #{msg}"
78
+ end
79
+ if args['raw']
80
+ ret = args;
81
+ else
82
+ ret = args['message']
83
+ end
84
+ return ret
85
+ end
86
+ def self.all_queue_info()
87
+ self.gather_queue_info()
88
+ return @@queues
89
+ end
90
+
91
+ def self.all_queues()
92
+ ret = {}
93
+ self.all_queue_info().each_pair do |qname,v|
94
+ qid = v['qid']
95
+ x = MessageQueue.new(qid, IPC_CREAT | 0666)
96
+ y = x.ipc_stat
97
+ ct = y.msg_qnum
98
+ ret[qname] = {
99
+ 'qid' => qid,
100
+ 'count' => ct,
101
+ }
102
+ end
103
+ return ret
104
+ end
105
+
106
+ private
107
+ def self.get_next_id
108
+ new_id = 1
109
+ @@queues.each_pair do |k,v|
110
+ if v['qid'] > new_id
111
+ new_id = v['qid']
112
+ end
113
+ end
114
+ return new_id + 1
115
+ end
116
+ def self.get_queue_id(args)
117
+ qname = args['qname']
118
+ self.mk_queue_dir()
119
+ if @@queues[qname]
120
+ return @@queues[qname]['qid']
121
+ end
122
+ self.gather_queue_info()
123
+ if @@queues[qname]
124
+ return @@queues[qname]['qid']
125
+ end
126
+ begin
127
+ self.lock_dir()
128
+ file = File.open("/tmp/transit/#{qname}", 'w')
129
+ new_qid = get_next_id
130
+ file.puts("qid=#{new_qid}")
131
+ file.puts("qname=#{qname}")
132
+ file.close
133
+ rescue Exception => msg
134
+ self.unlock_dir()
135
+ raise msg
136
+ end
137
+ self.unlock_dir()
138
+ self.gather_queue_info()
139
+ return @@queues[qname]['qid']
140
+ end
141
+
142
+ def self.lock_dir()
143
+ File.open('/tmp/transit.lock', File::WRONLY|File::EXCL|File::CREAT, 0666)
144
+ end
145
+ def self.unlock_dir()
146
+ File.delete('/tmp/transit.lock')
147
+ end
148
+
149
+ def self.gather_queue_info()
150
+ self.mk_queue_dir()
151
+ Dir.glob('/tmp/transit/*').each do |filename|
152
+ info = {}
153
+ file = File.new(filename, 'r')
154
+ while (line = file.gets)
155
+ line.chomp!
156
+ (key, value) = line.split('=')
157
+ info[key] = value
158
+ end
159
+ if not info['qid']
160
+ raise "required key 'qid' not found"
161
+ end
162
+ if not info['qname']
163
+ raise "required key 'qname' not found"
164
+ end
165
+ info['qid'] = Integer(info['qid'])
166
+ @@queues[info['qname']] = info
167
+ end
168
+ end
169
+
170
+ def self.mk_queue_dir()
171
+ begin
172
+ Dir.mkdir('/tmp/transit', 0777)
173
+ rescue
174
+ end
175
+ end
176
+
177
+
178
+ def self.transit_freeze(args)
179
+ return args['message'].to_json
180
+ end
181
+
182
+ def self.transit_thaw(args)
183
+ return JSON.parse(args['serialized_message'])
184
+ end
185
+
186
+
187
+
188
+ #returns a scalar, ready to be sent on the wire
189
+ #it takes message and wire_meta_data
190
+ def self.pack_message(args)
191
+ args['message']['.ipc_transit_meta'] = {}
192
+ args.keys.each do |k|
193
+ next if @@ipc_transit_wire_header_args[k]
194
+ next if @@ipc_transit_std_args[k]
195
+ args['message']['.ipc_transit_meta'][k] = args[k]
196
+ end
197
+ args['serialized_message'] = self.transit_freeze(args)
198
+ self.serialize_wire_meta(args)
199
+ l = args['serialized_wire_meta_data'].length
200
+ ret = "#{l}:#{args['serialized_wire_meta_data']}#{args['serialized_message']}"
201
+ return ret
202
+ end
203
+
204
+ def self.serialize_wire_meta(args)
205
+ s = ''
206
+ args.keys.each do |k|
207
+ if @@ipc_transit_wire_header_args[k]
208
+ #make sure a valid value is passed in
209
+ if @@ipc_transit_wire_header_args[k] == 1
210
+ s = "#{s}#{k}=#{args[k]},"
211
+ elsif @@ipc_transit_wire_header_args[k][args[k]]
212
+ s = "#{s}#{k}=#{args[k]},"
213
+ else
214
+ raise "passed wire argument #{k} had value #{args[k]} not of allowed type"
215
+ end
216
+ end
217
+ end
218
+ if s
219
+ s = s[0..-2] #teh google says this is the way to remove last character
220
+ end
221
+ args['serialized_wire_meta_data'] = s
222
+ end
223
+
224
+ #NB: I know this is all hideously inefficient. I'm still learning Ruby,
225
+ #and I'm focusing on getting this correct first.
226
+ #
227
+ #returns a serialized_message and wire_meta_data
228
+ #takes serialized_wire_data
229
+ def self.unpack_data(args)
230
+ stuff = args['serialized_wire_data'].split(':')
231
+ offset = Integer(stuff.shift)
232
+ header_and_message = stuff.join(':')
233
+ if offset == 0
234
+ args['serialized_header'] = ''
235
+ else
236
+ args['serialized_header'] = header_and_message[0..offset-1]
237
+ self.thaw_wire_headers(args)
238
+ end
239
+ args['serialized_message'] = header_and_message[offset..header_and_message.length]
240
+ args['message'] = self.transit_thaw(args)
241
+ return true
242
+ end
243
+
244
+ def self.thaw_wire_headers(args)
245
+ h = args['serialized_header']
246
+ ret = {}
247
+ h.split(',').each do |val|
248
+ (k,v) = val.split('=')
249
+ ret[k] = v
250
+ end
251
+ args['wire_headers'] = ret
252
+ end
253
+ end
@@ -0,0 +1,76 @@
1
+ require 'test/unit'
2
+ require 'ipc_transit'
3
+
4
+ #kind of ghetto, but I don't have a better way right now
5
+ def drain_test_queue
6
+ begin
7
+ while ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
8
+ end
9
+ rescue Exception => msg
10
+ end
11
+ end
12
+
13
+ class TestIPCTransit < Test::Unit::TestCase
14
+ def test_typical
15
+ drain_test_queue()
16
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname')
17
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
18
+ assert(ret, 'IPCTransit.receive returned true')
19
+ assert_equal(ret['foo'], 'bar')
20
+ end
21
+
22
+ def test_wire_raw
23
+ drain_test_queue()
24
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname', 'e' => 'json', 'c' => 'none')
25
+ ret = IPCTransit.receive('qname' => 'test_qname', 'raw' => 1, 'nowait' => 1)
26
+ assert(ret, 'IPCTransit.receive returned true')
27
+ assert_equal(ret['message']['foo'], 'bar')
28
+ assert_equal(ret['wire_headers']['e'], 'json')
29
+ assert_equal(ret['wire_headers']['c'], 'none')
30
+ end
31
+ def test_message_meta
32
+ drain_test_queue()
33
+ IPCTransit.send( 'qname' => 'test_qname',
34
+ 'message' => { 'foo' => 'bar' },
35
+ 'e' => 'json',
36
+ 'c' => 'none',
37
+ 'something' => 'else',
38
+ 'x' => { 'this' => 'that' },
39
+ 'once' => ['more',2],
40
+ )
41
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
42
+ assert(ret, 'IPCTransit.receive returned true')
43
+ assert_equal(ret['foo'], 'bar')
44
+ assert_equal(ret['.ipc_transit_meta']['something'], 'else')
45
+ assert_equal(ret['.ipc_transit_meta']['x']['this'], 'that')
46
+ assert_equal(ret['.ipc_transit_meta']['once'][0], 'more')
47
+ assert_equal(ret['.ipc_transit_meta']['once'][1], 2)
48
+ end
49
+
50
+ def test_get_queue_info
51
+ drain_test_queue()
52
+ all_info = IPCTransit.all_queue_info()
53
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname')
54
+ assert(all_info, 'We received some queue info')
55
+
56
+ assert(all_info['test_qname']['qname'] == 'test_qname', 'test queue is in all_queue_info')
57
+ qid = all_info['test_qname']['qid']
58
+ assert(qid, 'queue_id for test_qname exists')
59
+
60
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
61
+ assert(ret, 'IPCTransit.receive returned true')
62
+ assert_equal(ret['foo'], 'bar')
63
+ end
64
+
65
+ def test_all_queues
66
+ drain_test_queue()
67
+ IPCTransit.send('message' => { 'foo' => 'bar' }, 'qname' => 'test_qname')
68
+ ret = IPCTransit.all_queues()
69
+ assert(ret, 'IPCTransit.all_queues returned true')
70
+ assert(ret['test_qname']['count'] == 1, 'exactly one message in test_qname')
71
+ ret = IPCTransit.receive('qname' => 'test_qname', 'nowait' => 1)
72
+ assert(ret, 'IPCTransit.receive returned true')
73
+ assert_equal(ret['foo'], 'bar')
74
+ end
75
+ end
76
+
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipc_transit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dana M. Diederich
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: SysVIPC
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Serverless Message Queue
47
+ email: diederich@gmail.com
48
+ executables:
49
+ - trrecv
50
+ - trsend
51
+ - trlist
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - Rakefile
56
+ - lib/ipc_transit.rb
57
+ - bin/trrecv
58
+ - bin/trsend
59
+ - bin/trlist
60
+ - test/tc_transit_simple.rb
61
+ homepage: http://rubygems.org/gems/ipc_transit
62
+ licenses: []
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 1.8.24
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Serverless, cross-language, fast message queue library
85
+ test_files:
86
+ - test/tc_transit_simple.rb