rims 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +35 -2
- data/LICENSE.txt +17 -18
- data/lib/rims.rb +1 -0
- data/lib/rims/channel.rb +157 -0
- data/lib/rims/cmd.rb +3 -3
- data/lib/rims/mail_store.rb +26 -90
- data/lib/rims/pool.rb +15 -18
- data/lib/rims/protocol/decoder.rb +4 -4
- data/lib/rims/protocol/parser.rb +2 -2
- data/lib/rims/test.rb +39 -7
- data/lib/rims/version.rb +1 -1
- data/test/test_channel.rb +131 -0
- data/test/test_cksum_kvs.rb +4 -2
- data/test/test_config.rb +1 -1
- data/test/test_db.rb +10 -5
- data/test/test_db_recovery.rb +6 -3
- data/test/test_lock.rb +4 -2
- data/test/test_mail_store.rb +22 -6
- data/test/test_protocol_decoder.rb +2 -1
- data/test/test_protocol_fetch.rb +7 -3
- data/test/test_protocol_search.rb +66 -53
- data/test/test_rfc822.rb +1 -1
- metadata +5 -2
data/lib/rims/pool.rb
CHANGED
@@ -14,8 +14,8 @@ module RIMS
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# optional block is called when a mail store is closed.
|
17
|
-
def return_pool(
|
18
|
-
@object_pool.put(self,
|
17
|
+
def return_pool(&block) # yields:
|
18
|
+
@object_pool.put(self, &block)
|
19
19
|
nil
|
20
20
|
end
|
21
21
|
end
|
@@ -35,27 +35,25 @@ module RIMS
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def initialize(&object_factory) # yields: object_pool, object_key, object_lock
|
38
|
+
@mutex = Thread::Mutex.new
|
38
39
|
@object_factory = object_factory
|
39
|
-
@
|
40
|
-
@pool_lock = Mutex.new
|
41
|
-
@object_lock_map = Hash.new{|hash, key| hash[key] = ReadWriteLock.new }
|
40
|
+
@pool = {}
|
42
41
|
end
|
43
42
|
|
44
43
|
def empty?
|
45
|
-
@
|
44
|
+
@mutex.synchronize{ @pool.empty? }
|
46
45
|
end
|
47
46
|
|
48
47
|
# optional block is called when a new object is added to an object pool.
|
49
|
-
def get(object_key
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
ref_count = @pool_lock.synchronize{ @pool_map[object_key] }
|
48
|
+
def get(object_key) # yields:
|
49
|
+
@mutex.synchronize{
|
50
|
+
if (@pool.key? object_key) then
|
51
|
+
ref_count = @pool[object_key]
|
54
52
|
else
|
55
53
|
yield if block_given?
|
56
|
-
object_holder = @object_factory.call(self, object_key
|
54
|
+
object_holder = @object_factory.call(self, object_key)
|
57
55
|
ref_count = ReferenceCount.new(0, object_holder)
|
58
|
-
@
|
56
|
+
@pool[object_key] = ref_count
|
59
57
|
end
|
60
58
|
ref_count.count >= 0 or raise 'internal error'
|
61
59
|
ref_count.count += 1
|
@@ -64,15 +62,14 @@ module RIMS
|
|
64
62
|
end
|
65
63
|
|
66
64
|
# optional block is called when an object is deleted from an object pool.
|
67
|
-
def put(object_holder
|
68
|
-
|
69
|
-
|
70
|
-
ref_count = @pool_lock.synchronize{ @pool_map[object_holder.object_key] } or raise 'internal error'
|
65
|
+
def put(object_holder) # yields:
|
66
|
+
@mutex.synchronize{
|
67
|
+
ref_count = @pool[object_holder.object_key] or raise 'internal error'
|
71
68
|
ref_count.object_holder.equal? object_holder or raise 'internal error'
|
72
69
|
ref_count.count > 0 or raise 'internal error'
|
73
70
|
ref_count.count -= 1
|
74
71
|
if (ref_count.count == 0) then
|
75
|
-
@
|
72
|
+
@pool.delete(object_holder.object_key)
|
76
73
|
ref_count.object_destroy
|
77
74
|
yield if block_given?
|
78
75
|
end
|
@@ -156,7 +156,7 @@ module RIMS
|
|
156
156
|
@logger.error($!)
|
157
157
|
res << "#{tag} BAD client command syntax error\r\n"
|
158
158
|
rescue
|
159
|
-
raise if ($!.name =~ /AssertionFailedError/)
|
159
|
+
raise if ($!.class.name =~ /AssertionFailedError/)
|
160
160
|
@logger.error('internal server error.')
|
161
161
|
@logger.error($!)
|
162
162
|
res << "#{tag} BAD internal server error\r\n"
|
@@ -206,7 +206,7 @@ module RIMS
|
|
206
206
|
unique_user_id = Authentication.unique_user_id(username)
|
207
207
|
logger.debug("unique user ID: #{username} -> #{unique_user_id}") if logger.debug?
|
208
208
|
|
209
|
-
mail_store_holder = mail_store_pool.get(unique_user_id
|
209
|
+
mail_store_holder = mail_store_pool.get(unique_user_id) {
|
210
210
|
logger.info("open mail store: #{unique_user_id} [ #{username} ]")
|
211
211
|
}
|
212
212
|
|
@@ -510,7 +510,7 @@ module RIMS
|
|
510
510
|
end
|
511
511
|
tmp_mail_store_holder = @mail_store_holder
|
512
512
|
ReadWriteLock.write_lock_timeout_detach(@cleanup_write_lock_timeout_seconds, @write_lock_timeout_seconds, logger: @logger) {|timeout_seconds|
|
513
|
-
tmp_mail_store_holder.return_pool
|
513
|
+
tmp_mail_store_holder.return_pool{
|
514
514
|
@logger.info("close mail store: #{tmp_mail_store_holder.unique_user_id}")
|
515
515
|
}
|
516
516
|
}
|
@@ -1272,7 +1272,7 @@ module RIMS
|
|
1272
1272
|
@last_user_cache_key_username = nil
|
1273
1273
|
@last_user_cache_value_mail_store_holder = nil
|
1274
1274
|
ReadWriteLock.write_lock_timeout_detach(@cleanup_write_lock_timeout_seconds, @write_lock_timeout_seconds, logger: @logger) {|timeout_seconds|
|
1275
|
-
mail_store_holder.return_pool
|
1275
|
+
mail_store_holder.return_pool{
|
1276
1276
|
@logger.info("close cached mail store to deliver message: #{mail_store_holder.unique_user_id}")
|
1277
1277
|
}
|
1278
1278
|
}
|
data/lib/rims/protocol/parser.rb
CHANGED
@@ -631,9 +631,9 @@ module RIMS
|
|
631
631
|
unless (search_key.empty?) then
|
632
632
|
search_key = search_key.dup
|
633
633
|
factory = fetch_next_node(search_key)
|
634
|
-
|
634
|
+
_cond = factory.call(parse_cached(search_key))
|
635
635
|
else
|
636
|
-
|
636
|
+
_cond = end_of_cond
|
637
637
|
end
|
638
638
|
end
|
639
639
|
private :parse_cached
|
data/lib/rims/test.rb
CHANGED
@@ -235,6 +235,38 @@ EOF
|
|
235
235
|
end
|
236
236
|
end
|
237
237
|
|
238
|
+
def db_closed_error
|
239
|
+
RuntimeError
|
240
|
+
end
|
241
|
+
|
242
|
+
def db_closed_fetch_error
|
243
|
+
db_closed_error
|
244
|
+
end
|
245
|
+
|
246
|
+
def db_closed_store_error
|
247
|
+
db_closed_error
|
248
|
+
end
|
249
|
+
|
250
|
+
def db_closed_delete_error
|
251
|
+
db_closed_error
|
252
|
+
end
|
253
|
+
|
254
|
+
def db_closed_key_error
|
255
|
+
db_closed_error
|
256
|
+
end
|
257
|
+
|
258
|
+
def db_closed_each_key_error
|
259
|
+
db_closed_error
|
260
|
+
end
|
261
|
+
|
262
|
+
def db_closed_each_value_error
|
263
|
+
db_closed_error
|
264
|
+
end
|
265
|
+
|
266
|
+
def db_closed_each_pair_error
|
267
|
+
db_closed_error
|
268
|
+
end
|
269
|
+
|
238
270
|
def setup
|
239
271
|
@base_dir = 'kvs_test_dir'
|
240
272
|
FileUtils.mkdir_p(@base_dir)
|
@@ -336,13 +368,13 @@ EOF
|
|
336
368
|
assert_equal(true, db_closed?)
|
337
369
|
|
338
370
|
# closed exception
|
339
|
-
assert_raise(
|
340
|
-
assert_raise(
|
341
|
-
assert_raise(
|
342
|
-
assert_raise(
|
343
|
-
assert_raise(
|
344
|
-
assert_raise(
|
345
|
-
assert_raise(
|
371
|
+
assert_raise(db_closed_fetch_error) { @kvs['foo'] }
|
372
|
+
assert_raise(db_closed_store_error) { @kvs['foo'] = 'apple' }
|
373
|
+
assert_raise(db_closed_delete_error) { @kvs.delete('foo') }
|
374
|
+
assert_raise(db_closed_key_error) { @kvs.key? 'foo' }
|
375
|
+
assert_raise(db_closed_each_key_error) { @kvs.each_key.to_a }
|
376
|
+
assert_raise(db_closed_each_value_error) { @kvs.each_value.to_a }
|
377
|
+
assert_raise(db_closed_each_pair_error) { @kvs.each_pair.to_a }
|
346
378
|
end
|
347
379
|
end
|
348
380
|
|
data/lib/rims/version.rb
CHANGED
@@ -0,0 +1,131 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rims'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
module RIMS::Test
|
7
|
+
class ServerResponseChannelTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@channel = RIMS::ServerResponseChannel.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_pub_sub_fetch_message
|
13
|
+
pub1, sub1 = @channel.make_pub_sub_pair(0)
|
14
|
+
pub2, sub2 = @channel.make_pub_sub_pair(0)
|
15
|
+
pub3, sub3 = @channel.make_pub_sub_pair(0)
|
16
|
+
|
17
|
+
pub1.publish('msg1')
|
18
|
+
assert_equal(false, sub1.message?)
|
19
|
+
assert_equal(true, sub2.message?)
|
20
|
+
assert_equal(true, sub3.message?)
|
21
|
+
|
22
|
+
pub2.publish('msg2')
|
23
|
+
assert_equal(true, sub1.message?)
|
24
|
+
assert_equal(true, sub2.message?)
|
25
|
+
assert_equal(true, sub3.message?)
|
26
|
+
|
27
|
+
pub3.publish('msg3')
|
28
|
+
assert_equal(true, sub1.message?)
|
29
|
+
assert_equal(true, sub2.message?)
|
30
|
+
assert_equal(true, sub3.message?)
|
31
|
+
|
32
|
+
assert_equal(%w[ msg2 msg3 ], sub1.enum_for(:fetch).to_a)
|
33
|
+
assert_equal(%w[ msg1 msg3 ], sub2.enum_for(:fetch).to_a)
|
34
|
+
assert_equal(%w[ msg1 msg2 ], sub3.enum_for(:fetch).to_a)
|
35
|
+
|
36
|
+
assert_equal(false, sub1.message?)
|
37
|
+
assert_equal(false, sub2.message?)
|
38
|
+
assert_equal(false, sub3.message?)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_pub_sub_fetch_no_message
|
42
|
+
_, sub = @channel.make_pub_sub_pair(0)
|
43
|
+
assert_equal(false, sub.message?)
|
44
|
+
assert_equal([], sub.enum_for(:fetch).to_a)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_pub_sub_detach
|
48
|
+
pub1, sub1 = @channel.make_pub_sub_pair(0)
|
49
|
+
pub2, sub2 = @channel.make_pub_sub_pair(0)
|
50
|
+
pub3, sub3 = @channel.make_pub_sub_pair(0)
|
51
|
+
|
52
|
+
pub3.detach
|
53
|
+
sub3.detach
|
54
|
+
|
55
|
+
pub1.publish('msg1')
|
56
|
+
pub2.publish('msg2')
|
57
|
+
|
58
|
+
error = assert_raise(RuntimeError) { pub3.publish('msg3') }
|
59
|
+
assert_match(/detached/, error.message)
|
60
|
+
|
61
|
+
assert_equal(%w[ msg2 ], sub1.enum_for(:fetch).to_a)
|
62
|
+
assert_equal(%w[ msg1 ], sub2.enum_for(:fetch).to_a)
|
63
|
+
assert_equal([], sub3.enum_for(:fetch).to_a)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_pub_sub_different_mailboxes
|
67
|
+
mbox0_pub1, mbox0_sub1 = @channel.make_pub_sub_pair(0)
|
68
|
+
mbox0_pub2, mbox0_sub2 = @channel.make_pub_sub_pair(0)
|
69
|
+
mbox1_pub1, mbox1_sub1 = @channel.make_pub_sub_pair(1)
|
70
|
+
mbox1_pub2, mbox1_sub2 = @channel.make_pub_sub_pair(1)
|
71
|
+
|
72
|
+
mbox0_pub1.publish('mbox0:msg1')
|
73
|
+
mbox0_pub2.publish('mbox0:msg2')
|
74
|
+
|
75
|
+
assert_equal(%w[ mbox0:msg2 ], mbox0_sub1.enum_for(:fetch).to_a)
|
76
|
+
assert_equal(%w[ mbox0:msg1 ], mbox0_sub2.enum_for(:fetch).to_a)
|
77
|
+
assert_equal([], mbox1_sub1.enum_for(:fetch).to_a)
|
78
|
+
assert_equal([], mbox1_sub2.enum_for(:fetch).to_a)
|
79
|
+
|
80
|
+
mbox1_pub1.publish('mbox1:msg1')
|
81
|
+
mbox1_pub2.publish('mbox1:msg2')
|
82
|
+
|
83
|
+
assert_equal([], mbox0_sub1.enum_for(:fetch).to_a)
|
84
|
+
assert_equal([], mbox0_sub2.enum_for(:fetch).to_a)
|
85
|
+
assert_equal(%w[ mbox1:msg2 ], mbox1_sub1.enum_for(:fetch).to_a)
|
86
|
+
assert_equal(%w[ mbox1:msg1 ], mbox1_sub2.enum_for(:fetch).to_a)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_pub_sub_idle
|
90
|
+
pub, _ = @channel.make_pub_sub_pair(0)
|
91
|
+
_, sub = @channel.make_pub_sub_pair(0)
|
92
|
+
|
93
|
+
pub.publish('msg1')
|
94
|
+
sub.idle_interrupt
|
95
|
+
assert_equal([ %w[ msg1 ] ], sub.enum_for(:idle_wait).to_a)
|
96
|
+
|
97
|
+
pub.publish('msg2')
|
98
|
+
pub.publish('msg3')
|
99
|
+
sub.idle_interrupt
|
100
|
+
assert_equal([ %w[ msg2 msg3 ] ], sub.enum_for(:idle_wait).to_a)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_pub_sub_idle_chunks
|
104
|
+
pub, _ = @channel.make_pub_sub_pair(0)
|
105
|
+
_, sub = @channel.make_pub_sub_pair(0)
|
106
|
+
|
107
|
+
t = Thread.new{ sub.enum_for(:idle_wait).to_a }
|
108
|
+
|
109
|
+
pub.publish('msg1')
|
110
|
+
t.wakeup
|
111
|
+
sleep(0.1)
|
112
|
+
|
113
|
+
pub.publish('msg2')
|
114
|
+
pub.publish('msg3')
|
115
|
+
sub.idle_interrupt
|
116
|
+
|
117
|
+
assert_equal([ %w[ msg1 ], %w[ msg2 msg3 ] ], t.value)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_pub_sub_idle_no_message
|
121
|
+
_, sub = @channel.make_pub_sub_pair(0)
|
122
|
+
sub.idle_interrupt
|
123
|
+
assert_equal([], sub.enum_for(:idle_wait).to_a)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Local Variables:
|
129
|
+
# mode: Ruby
|
130
|
+
# indent-tabs-mode: nil
|
131
|
+
# End:
|
data/test/test_cksum_kvs.rb
CHANGED
@@ -107,10 +107,12 @@ module RIMS::Test
|
|
107
107
|
|
108
108
|
s = @db['foo']
|
109
109
|
@db['foo'] = s.chop
|
110
|
-
assert_raise(RuntimeError) { @kvs['foo'] }
|
110
|
+
error = assert_raise(RuntimeError) { @kvs['foo'] }
|
111
|
+
assert_match(/checksum error/, error.message)
|
111
112
|
|
112
113
|
@db['foo'] = 'Hello world.'
|
113
|
-
assert_raise(RuntimeError) { @kvs['foo'] }
|
114
|
+
error = assert_raise(RuntimeError) { @kvs['foo'] }
|
115
|
+
assert_match(/checksum format error/, error.message)
|
114
116
|
end
|
115
117
|
end
|
116
118
|
end
|
data/test/test_config.rb
CHANGED
@@ -108,7 +108,7 @@ module RIMS::Test
|
|
108
108
|
begin
|
109
109
|
assert_config(load_libraries: %w[ prime ]) {|conf|
|
110
110
|
conf.setup_load_libraries
|
111
|
-
assert($LOADED_FEATURES.any?{|name| name =~ /prime/})
|
111
|
+
assert($LOADED_FEATURES.any?{|name| name =~ /prime/})
|
112
112
|
}
|
113
113
|
rescue
|
114
114
|
exit!(1)
|
data/test/test_db.rb
CHANGED
@@ -448,7 +448,8 @@ module RIMS::Test
|
|
448
448
|
assert_equal(false, (@db.msg_exist? 1))
|
449
449
|
assert_equal(true, (@db.msg_exist? 2))
|
450
450
|
|
451
|
-
assert_raise(RuntimeError) { @db.del_msg(1) }
|
451
|
+
error = assert_raise(RuntimeError) { @db.del_msg(1) }
|
452
|
+
assert_match(/not found a message text/, error.message)
|
452
453
|
end
|
453
454
|
end
|
454
455
|
|
@@ -531,8 +532,10 @@ module RIMS::Test
|
|
531
532
|
assert_equal(deleted, @db.msg_flag_deleted(uid))
|
532
533
|
end
|
533
534
|
|
534
|
-
assert_raise(RuntimeError) { @db.expunge_msg(1) }
|
535
|
-
|
535
|
+
error = assert_raise(RuntimeError) { @db.expunge_msg(1) }
|
536
|
+
assert_match(/not found a message uid/, error.message)
|
537
|
+
error = assert_raise(RuntimeError) { @db.expunge_msg(2) }
|
538
|
+
assert_match(/not deleted flag/, error.message)
|
536
539
|
end
|
537
540
|
end
|
538
541
|
|
@@ -572,12 +575,13 @@ module RIMS::Test
|
|
572
575
|
@cksum_kvs['baz'] = 'orange'
|
573
576
|
|
574
577
|
count = 0
|
575
|
-
assert_raise(RuntimeError) {
|
578
|
+
error = assert_raise(RuntimeError) {
|
576
579
|
@db.test_read_all{|read_error|
|
577
580
|
assert_kind_of(RuntimeError, read_error)
|
578
581
|
count += 1
|
579
582
|
}
|
580
583
|
}
|
584
|
+
assert_match(/checksum format error/, error.message)
|
581
585
|
assert_equal(1, count)
|
582
586
|
|
583
587
|
@cksum_kvs['foo'] = 'apple'; @kvs['foo'] = 'apple'
|
@@ -585,12 +589,13 @@ module RIMS::Test
|
|
585
589
|
@cksum_kvs['baz'] = 'orange'; @kvs['baz'] = 'orange'
|
586
590
|
|
587
591
|
count = 0
|
588
|
-
assert_raise(RuntimeError) {
|
592
|
+
error = assert_raise(RuntimeError) {
|
589
593
|
@db.test_read_all{|read_error|
|
590
594
|
assert_kind_of(RuntimeError, read_error)
|
591
595
|
count += 1
|
592
596
|
}
|
593
597
|
}
|
598
|
+
assert_match(/checksum format error/, error.message)
|
594
599
|
assert_equal(2, count)
|
595
600
|
end
|
596
601
|
end
|
data/test/test_db_recovery.rb
CHANGED
@@ -73,7 +73,8 @@ module RIMS::Test
|
|
73
73
|
|
74
74
|
@kvs['meta'].delete('msg_id2date-1')
|
75
75
|
assert_instance_of(Time, @meta_db.msg_date(0))
|
76
|
-
assert_raise(RuntimeError) { @meta_db.msg_date(1) }
|
76
|
+
error = assert_raise(RuntimeError) { @meta_db.msg_date(1) }
|
77
|
+
assert_match(/not found a message date/, error.message)
|
77
78
|
assert_instance_of(Time, @meta_db.msg_date(2))
|
78
79
|
|
79
80
|
@meta_db.recovery_phase1_msg_scan(@msg_db, logger: @logger)
|
@@ -635,7 +636,8 @@ module RIMS::Test
|
|
635
636
|
|
636
637
|
# recovery phase 2
|
637
638
|
assert_equal({}, @meta_db.msg_mbox_uid_mapping(3))
|
638
|
-
assert_raise(RuntimeError) { @meta_db.msg_date(3) }
|
639
|
+
error = assert_raise(RuntimeError) { @meta_db.msg_date(3) }
|
640
|
+
assert_match(/not found a message date/, error.message)
|
639
641
|
|
640
642
|
# recovery phase 3
|
641
643
|
assert_equal(5, @meta_db.uidvalidity)
|
@@ -672,7 +674,8 @@ module RIMS::Test
|
|
672
674
|
# recovery phase 1,8
|
673
675
|
@msg_db.add_msg(3, 'apple')
|
674
676
|
assert_equal({}, @meta_db.msg_mbox_uid_mapping(3))
|
675
|
-
assert_raise(RuntimeError) { @meta_db.msg_date(3) }
|
677
|
+
error = assert_raise(RuntimeError) { @meta_db.msg_date(3) }
|
678
|
+
assert_match(/not found a message date/, error.message)
|
676
679
|
|
677
680
|
# recovery phase 2,5,8
|
678
681
|
mbox_uid_map = Marshal.load(@kvs['meta']['msg_id2mbox-0'])
|
data/test/test_lock.rb
CHANGED
@@ -34,19 +34,21 @@ module RIMS::Test
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_read_lock_timeout
|
37
|
-
assert_raise(RIMS::ReadLockTimeoutError) {
|
37
|
+
error = assert_raise(RIMS::ReadLockTimeoutError) {
|
38
38
|
@lock.write_synchronize{
|
39
39
|
@lock.read_synchronize(0) {}
|
40
40
|
}
|
41
41
|
}
|
42
|
+
assert_equal('read-lock wait timeout', error.message)
|
42
43
|
end
|
43
44
|
|
44
45
|
def test_write_lock_timeout
|
45
|
-
assert_raise(RIMS::WriteLockTimeoutError) {
|
46
|
+
error = assert_raise(RIMS::WriteLockTimeoutError) {
|
46
47
|
@lock.write_synchronize{
|
47
48
|
@lock.write_synchronize(0) {}
|
48
49
|
}
|
49
50
|
}
|
51
|
+
assert_equal('write-lock wait timeout', error.message)
|
50
52
|
end
|
51
53
|
|
52
54
|
def calculate_threa_work_seconds
|