jjp-memcache-client 1.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,173 @@
1
+ # Extensions for using memcache-client with EventMachine
2
+
3
+ raise "memcache/event_machine requires Ruby 1.9" if RUBY_VERSION < '1.9'
4
+
5
+ require 'memcache'
6
+ require 'eventmachine'
7
+ require 'fiber'
8
+
9
+ class MemCache
10
+
11
+ # Since we are working in a single Thread, multiple Fiber environment,
12
+ # disable the multithread Mutex as it will not work.
13
+ # DEFAULT_OPTIONS[:multithread] = false
14
+
15
+ module EventedServer
16
+
17
+ def fiber_key
18
+ @fiber_key ||= "memcached-#{@host}-#{@port}"
19
+ end
20
+
21
+ def socket
22
+ sock = Thread.current[fiber_key]
23
+ return sock if sock and not sock.closed?
24
+
25
+ Thread.current[fiber_key] = nil
26
+
27
+ # If the host was dead, don't retry for a while.
28
+ return if @retry and @retry > Time.now
29
+
30
+ Thread.current[fiber_key] ||= begin
31
+ sock = EM::SocketConnection.connect(@host, @port, @timeout)
32
+ yielding = true
33
+ fiber = Fiber.current
34
+ sock.callback do
35
+ @status = 'CONNECTED'
36
+ @retry = nil
37
+ yielding = false
38
+ fiber.resume if Fiber.current != fiber
39
+ end
40
+ sock.errback do
41
+ sock = nil
42
+ yielding = false
43
+ fiber.resume if Fiber.current != fiber
44
+ end
45
+ Fiber.yield if yielding
46
+ sock
47
+ end
48
+ end
49
+
50
+ def close
51
+ sock = Thread.current[fiber_key]
52
+ if sock
53
+ sock.close if !sock.closed?
54
+ Thread.current[fiber_key] = nil
55
+ end
56
+ @retry = nil
57
+ @status = "NOT CONNECTED"
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ module EM
64
+ module SocketConnection
65
+ include EM::Deferrable
66
+
67
+ def self.connect(host, port, timeout)
68
+ EM.connect(host, port, self) do |conn|
69
+ conn.pending_connect_timeout = timeout
70
+ end
71
+ end
72
+
73
+ def initialize
74
+ @connected = false
75
+ @index = 0
76
+ @buf = ''
77
+ end
78
+
79
+ def closed?
80
+ !@connected
81
+ end
82
+
83
+ def close
84
+ @connected = false
85
+ close_connection(true)
86
+ end
87
+
88
+ def write(buf)
89
+ send_data(buf)
90
+ end
91
+
92
+ def read(size)
93
+ if can_read?(size)
94
+ yank(size)
95
+ else
96
+ fiber = Fiber.current
97
+ @size = size
98
+ @callback = proc { |data|
99
+ fiber.resume(data)
100
+ }
101
+ # TODO Can leak fiber if the connection dies while
102
+ # this fiber is yielded, waiting for data
103
+ Fiber.yield
104
+ end
105
+ end
106
+
107
+ SEP = "\r\n"
108
+
109
+ def gets
110
+ while true
111
+ # Read to ensure we have some data in the buffer
112
+ line = read(2)
113
+ # Reset the buffer index to zero
114
+ @buf = @buf.slice(@index..-1)
115
+ @index = 0
116
+ if eol = @buf.index(SEP)
117
+ line << yank(eol + SEP.size)
118
+ break
119
+ else
120
+ # EOL not in the current buffer
121
+ line << yank(@buf.size)
122
+ end
123
+ end
124
+ line
125
+ end
126
+
127
+ def can_read?(size)
128
+ @buf.size >= @index + size
129
+ end
130
+
131
+ # EM callbacks
132
+
133
+ def receive_data(data)
134
+ @buf << data
135
+
136
+ if @callback and can_read?(@size)
137
+ callback = @callback
138
+ data = yank(@size)
139
+ @callback = @size = nil
140
+ callback.call(data)
141
+ end
142
+ end
143
+
144
+ def post_init
145
+ @connected = true
146
+ succeed
147
+ end
148
+
149
+ def unbind
150
+ if @connected
151
+ @connected = false
152
+ else
153
+ fail
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ BUFFER_SIZE = 4096
160
+
161
+ def yank(len)
162
+ data = @buf.slice(@index, len)
163
+ @index += len
164
+ @index = @buf.size if @index > @buf.size
165
+ if @index >= BUFFER_SIZE
166
+ @buf = @buf.slice(@index..-1)
167
+ @index = 0
168
+ end
169
+ data
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,6 @@
1
+ class MemCache
2
+ ##
3
+ # The version of MemCache you are using.
4
+
5
+ VERSION = "1.8.5"
6
+ end
@@ -0,0 +1,105 @@
1
+ ##
2
+ # A utility wrapper around the MemCache client to simplify cache access. All
3
+ # methods silently ignore MemCache errors.
4
+ #
5
+ # This API is deprecated, please use the Rails.cache API or your own wrapper API
6
+ # around MemCache.
7
+
8
+ module Cache
9
+
10
+ ##
11
+ # Try to return a logger object that does not rely
12
+ # on ActiveRecord for logging.
13
+ def self.logger
14
+ @logger ||= if defined? Rails.logger # Rails 2.1 +
15
+ Rails.logger
16
+ elsif defined? RAILS_DEFAULT_LOGGER # Rails 1.2.2 +
17
+ RAILS_DEFAULT_LOGGER
18
+ else
19
+ ActiveRecord::Base.logger # ... very old Rails.
20
+ end
21
+ end
22
+ ##
23
+ # Returns the object at +key+ from the cache if successful, or nil if either
24
+ # the object is not in the cache or if there was an error attermpting to
25
+ # access the cache.
26
+ #
27
+ # If there is a cache miss and a block is given the result of the block will
28
+ # be stored in the cache with optional +expiry+, using the +add+ method rather
29
+ # than +set+.
30
+
31
+ def self.get(key, expiry = 0)
32
+ start_time = Time.now
33
+ value = CACHE.get key
34
+ elapsed = Time.now - start_time
35
+ logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
36
+ if value.nil? and block_given? then
37
+ value = yield
38
+ add key, value, expiry
39
+ end
40
+ value
41
+ rescue MemCache::MemCacheError => err
42
+ logger.debug "MemCache Error: #{err.message}"
43
+ if block_given? then
44
+ value = yield
45
+ put key, value, expiry
46
+ end
47
+ value
48
+ end
49
+
50
+ ##
51
+ # Sets +value+ in the cache at +key+, with an optional +expiry+ time in
52
+ # seconds.
53
+
54
+ def self.put(key, value, expiry = 0)
55
+ start_time = Time.now
56
+ CACHE.set key, value, expiry
57
+ elapsed = Time.now - start_time
58
+ logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
59
+ value
60
+ rescue MemCache::MemCacheError => err
61
+ ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
62
+ nil
63
+ end
64
+
65
+ ##
66
+ # Sets +value+ in the cache at +key+, with an optional +expiry+ time in
67
+ # seconds. If +key+ already exists in cache, returns nil.
68
+
69
+ def self.add(key, value, expiry = 0)
70
+ start_time = Time.now
71
+ response = CACHE.add key, value, expiry
72
+ elapsed = Time.now - start_time
73
+ logger.debug('MemCache Add (%0.6f) %s' % [elapsed, key])
74
+ (response == "STORED\r\n") ? value : nil
75
+ rescue MemCache::MemCacheError => err
76
+ ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
77
+ nil
78
+ end
79
+
80
+ ##
81
+ # Deletes +key+ from the cache in +delay+ seconds.
82
+
83
+ def self.delete(key, delay = nil)
84
+ start_time = Time.now
85
+ CACHE.delete key, delay
86
+ elapsed = Time.now - start_time
87
+ logger.debug('MemCache Delete (%0.6f) %s' %
88
+ [elapsed, key])
89
+ nil
90
+ rescue MemCache::MemCacheError => err
91
+ logger.debug "MemCache Error: #{err.message}"
92
+ nil
93
+ end
94
+
95
+ ##
96
+ # Resets all connections to MemCache servers.
97
+
98
+ def self.reset
99
+ CACHE.reset
100
+ logger.debug 'MemCache Connections Reset'
101
+ nil
102
+ end
103
+
104
+ end
105
+
data/performance.txt ADDED
@@ -0,0 +1,143 @@
1
+ == 1.5.0, 1.8.6 (default in Rails 2.2 and lower)
2
+
3
+ user system total real
4
+ set:plain:memcache-client 41.550000 0.590000 42.140000 ( 43.740685)
5
+ set:ruby:memcache-client 41.540000 0.590000 42.130000 ( 43.733796)
6
+ get:plain:memcache-client 41.920000 0.610000 42.530000 ( 44.031005)
7
+ get:ruby:memcache-client 41.940000 0.600000 42.540000 ( 44.082447)
8
+ multiget:ruby:memcache-client 46.120000 0.440000 46.560000 ( 47.354041)
9
+ missing:ruby:memcache-client 41.490000 0.580000 42.070000 ( 43.610837)
10
+ mixed:ruby:memcache-client 83.820000 1.190000 85.010000 ( 88.117077)
11
+
12
+
13
+ == 1.7.0, timeout, 1.8.6 (closest to default in Rails 2.3)
14
+ user system total real
15
+ set:plain:memcache-client 4.320000 2.280000 6.600000 ( 7.102900)
16
+ set:ruby:memcache-client 4.400000 2.300000 6.700000 ( 6.856992)
17
+ get:plain:memcache-client 9.890000 6.830000 16.720000 ( 16.984208)
18
+ get:ruby:memcache-client 10.040000 6.890000 16.930000 ( 17.141128)
19
+ multiget:ruby:memcache-client 5.350000 4.110000 9.460000 ( 9.542898)
20
+ missing:ruby:memcache-client 4.710000 3.180000 7.890000 ( 8.030969)
21
+ mixed:ruby:memcache-client 14.540000 9.200000 23.740000 ( 24.121824)
22
+
23
+ == 1.7.0, timeout, system_timer, 1.8.6
24
+ user system total real
25
+ set:plain:memcache-client 3.840000 0.640000 4.480000 ( 4.643790)
26
+ set:ruby:memcache-client 3.930000 0.650000 4.580000 ( 4.731868)
27
+ get:plain:memcache-client 8.320000 1.290000 9.610000 ( 9.903877)
28
+ get:ruby:memcache-client 8.460000 1.310000 9.770000 ( 9.986694)
29
+ multiget:ruby:memcache-client 4.250000 0.560000 4.810000 ( 4.935326)
30
+ missing:ruby:memcache-client 3.840000 0.640000 4.480000 ( 4.569696)
31
+ mixed:ruby:memcache-client 12.400000 1.960000 14.360000 ( 14.857924)
32
+
33
+ == 1.7.0, timeout, 1.9.1
34
+ user system total real
35
+ set:plain:memcache-client 2.130000 2.150000 4.280000 ( 3.774238)
36
+ set:ruby:memcache-client 2.230000 2.230000 4.460000 ( 3.883686)
37
+ get:plain:memcache-client 4.030000 4.250000 8.280000 ( 6.702740)
38
+ get:ruby:memcache-client 4.090000 4.220000 8.310000 ( 6.749134)
39
+ multiget:ruby:memcache-client 1.960000 1.840000 3.800000 ( 3.089448)
40
+ missing:ruby:memcache-client 2.110000 2.210000 4.320000 ( 3.659019)
41
+ mixed:ruby:memcache-client 6.400000 6.560000 12.960000 ( 11.116317)
42
+
43
+ == 1.7.0, no timeout, 1.9.1
44
+ user system total real
45
+ set:plain:memcache-client 0.560000 0.320000 0.880000 ( 1.849380)
46
+ set:ruby:memcache-client 0.630000 0.320000 0.950000 ( 1.968208)
47
+ get:plain:memcache-client 0.640000 0.330000 0.970000 ( 1.962473)
48
+ get:ruby:memcache-client 0.690000 0.320000 1.010000 ( 2.002295)
49
+ multiget:ruby:memcache-client 0.460000 0.110000 0.570000 ( 0.885827)
50
+ missing:ruby:memcache-client 0.530000 0.320000 0.850000 ( 1.721371)
51
+ mixed:ruby:memcache-client 1.340000 0.660000 2.000000 ( 3.973213)
52
+
53
+ == 1.7.0, no timeout, 1.8.6
54
+ user system total real
55
+ set:plain:memcache-client 1.220000 0.310000 1.530000 ( 2.763310)
56
+ set:ruby:memcache-client 1.270000 0.300000 1.570000 ( 2.806251)
57
+ get:plain:memcache-client 1.400000 0.300000 1.700000 ( 2.944343)
58
+ get:ruby:memcache-client 1.450000 0.310000 1.760000 ( 2.997234)
59
+ multiget:ruby:memcache-client 1.120000 0.110000 1.230000 ( 1.665716)
60
+ missing:ruby:memcache-client 1.160000 0.300000 1.460000 ( 2.683376)
61
+ mixed:ruby:memcache-client 2.760000 0.610000 3.370000 ( 5.851047)
62
+
63
+ == 1.7.1, timeout, 1.8.6, raw + gets SystemTimer
64
+ user system total real
65
+ set:plain:memcache-client 2.670000 0.510000 3.180000 ( 3.489509)
66
+ set:ruby:memcache-client 2.810000 0.530000 3.340000 ( 3.675955)
67
+ get:plain:memcache-client 4.380000 0.720000 5.100000 ( 5.400587)
68
+ get:ruby:memcache-client 4.490000 0.730000 5.220000 ( 5.477543)
69
+ multiget:ruby:memcache-client 2.570000 0.310000 2.880000 ( 3.034944)
70
+ missing:ruby:memcache-client 2.800000 0.530000 3.330000 ( 3.547073)
71
+ mixed:ruby:memcache-client 7.460000 1.250000 8.710000 ( 9.272177)
72
+
73
+ == 1.7.1, timeout, 1.9.1, raw + gets Timeout
74
+ user system total real
75
+ set:plain:memcache-client 1.370000 1.300000 2.670000 ( 2.708669)
76
+ set:ruby:memcache-client 1.400000 1.240000 2.640000 ( 2.713737)
77
+ get:plain:memcache-client 2.070000 2.020000 4.090000 ( 3.950879)
78
+ get:ruby:memcache-client 2.160000 2.090000 4.250000 ( 3.924613)
79
+ multiget:ruby:memcache-client 1.080000 0.820000 1.900000 ( 1.744107)
80
+ missing:ruby:memcache-client 1.330000 1.270000 2.600000 ( 2.547597)
81
+ mixed:ruby:memcache-client 3.540000 3.270000 6.810000 ( 6.735349)
82
+
83
+ == 1.7.1, timeout, 1.8.6, raw + gets SystemTimer, native binary search
84
+ user system total real
85
+ set:plain:memcache-client 1.840000 0.450000 2.290000 ( 2.651285)
86
+ set:ruby:memcache-client 1.960000 0.460000 2.420000 ( 2.712650)
87
+ get:plain:memcache-client 3.180000 0.630000 3.810000 ( 4.079930)
88
+ get:ruby:memcache-client 3.290000 0.640000 3.930000 ( 4.242648)
89
+ multiget:ruby:memcache-client 1.640000 0.250000 1.890000 ( 2.003687)
90
+ missing:ruby:memcache-client 1.940000 0.450000 2.390000 ( 2.619675)
91
+ mixed:ruby:memcache-client 5.360000 1.100000 6.460000 ( 7.040998)
92
+
93
+ == 1.7.2, timeout, 1.8.6, SystemTimer, native binary search
94
+ user system total real
95
+ set:plain:memcache-client 3.260000 0.590000 3.850000 ( 4.067382)
96
+ set:ruby:memcache-client 3.370000 0.590000 3.960000 ( 4.364004)
97
+ get:plain:memcache-client 6.740000 1.240000 7.980000 ( 8.586676)
98
+ get:ruby:memcache-client 6.780000 1.210000 7.990000 ( 8.423400)
99
+ multiget:ruby:memcache-client 3.480000 0.540000 4.020000 ( 4.288633)
100
+ missing:ruby:memcache-client 3.250000 0.590000 3.840000 ( 4.043602)
101
+ mixed:ruby:memcache-client 10.150000 1.810000 11.960000 ( 12.372054)
102
+
103
+ == 1.7.4, 1.8.6, buffered and non-blocking IO
104
+ user system total real
105
+ set:plain:memcache-client 2.450000 0.790000 3.240000 ( 3.397091)
106
+ set:ruby:memcache-client 2.490000 0.790000 3.280000 ( 3.555436)
107
+ get:plain:memcache-client 2.840000 0.810000 3.650000 ( 3.759695)
108
+ get:ruby:memcache-client 2.890000 0.790000 3.680000 ( 3.778011)
109
+ multiget:ruby:memcache-client 1.380000 0.280000 1.660000 ( 1.695290)
110
+ missing:ruby:memcache-client 2.380000 0.780000 3.160000 ( 3.251136)
111
+ mixed:ruby:memcache-client 5.360000 1.600000 6.960000 ( 7.189314)
112
+
113
+ == memcached 0.13 + libmemcached 0.25.4 versus memcache-client 1.7.4
114
+
115
+ user system total real
116
+ set:plain:noblock:memcached 0.090000 0.030000 0.120000 ( 0.277929)
117
+ set:plain:memcached 0.220000 0.270000 0.490000 ( 1.251547)
118
+ set:plain:memcache-client 0.610000 0.270000 0.880000 ( 1.670718)
119
+ set:ruby:noblock:memcached 0.150000 0.020000 0.170000 ( 0.309201)
120
+ set:ruby:memcached 0.300000 0.290000 0.590000 ( 1.390354)
121
+ set:ruby:memcache-client 0.670000 0.270000 0.940000 ( 1.713558)
122
+ get:plain:memcached 0.240000 0.270000 0.510000 ( 1.169909)
123
+ get:plain:memcache-client 0.850000 0.270000 1.120000 ( 1.885270)
124
+ get:ruby:memcached 0.270000 0.280000 0.550000 ( 1.229705)
125
+ get:ruby:memcache-client 0.890000 0.260000 1.150000 ( 1.861660)
126
+ multiget:ruby:memcached 0.190000 0.090000 0.280000 ( 0.396264)
127
+ multiget:ruby:memcache-client 0.530000 0.100000 0.630000 ( 0.901016)
128
+ missing:ruby:memcached 0.280000 0.290000 0.570000 ( 1.254400)
129
+ missing:ruby:memcached:inline 0.300000 0.290000 0.590000 ( 1.235122)
130
+ missing:ruby:memcache-client 0.570000 0.250000 0.820000 ( 1.461293)
131
+ mixed:ruby:noblock:memcached 0.540000 0.620000 1.160000 ( 2.429200)
132
+ mixed:ruby:memcached 0.580000 0.570000 1.150000 ( 2.610819)
133
+ mixed:ruby:memcache-client 1.580000 0.540000 2.120000 ( 3.632775)
134
+
135
+ == 1.7.6, 1.8.7 64-bit (Snow Leopard), SystemTimer
136
+ user system total real
137
+ set:plain:memcache-client 3.070000 0.380000 3.450000 ( 3.643275)
138
+ set:ruby:memcache-client 3.140000 0.370000 3.510000 ( 3.698602)
139
+ get:plain:memcache-client 3.480000 0.360000 3.840000 ( 3.983941)
140
+ get:ruby:memcache-client 3.540000 0.360000 3.900000 ( 4.034308)
141
+ multiget:ruby:memcache-client 1.690000 0.140000 1.830000 ( 1.889290)
142
+ missing:ruby:memcache-client 3.070000 0.360000 3.430000 ( 3.571754)
143
+ mixed:ruby:memcache-client 6.720000 0.750000 7.470000 ( 7.838771)
@@ -0,0 +1,142 @@
1
+ require 'rubygems'
2
+ require 'benchmark'
3
+ require 'test/unit'
4
+
5
+ $TESTING = true
6
+ require 'memcache'
7
+
8
+ class TestBenchmark < Test::Unit::TestCase
9
+
10
+ def setup
11
+ puts "Testing #{MemCache::VERSION}"
12
+ # We'll use a simple @value to try to avoid spending time in Marshal,
13
+ # which is a constant penalty that both clients have to pay
14
+ @value = []
15
+ @marshalled = Marshal.dump(@value)
16
+
17
+ @opts = [
18
+ ['127.0.0.1:11211', 'localhost:11211'],
19
+ {
20
+ :namespace => "namespace",
21
+ # :no_reply => true,
22
+ # :timeout => nil,
23
+ }
24
+ ]
25
+ @key1 = "Short"
26
+ @key2 = "Sym1-2-3::45"*8
27
+ @key3 = "Long"*40
28
+ @key4 = "Medium"*8
29
+ # 5 and 6 are only used for multiget miss test
30
+ @key5 = "Medium2"*8
31
+ @key6 = "Long3"*40
32
+ end
33
+
34
+ def test_em
35
+ return if RUBY_VERSION < '1.9'
36
+ require 'eventmachine'
37
+ require 'memcache/event_machine'
38
+ puts "with EventMachine"
39
+ EM.run do
40
+ Fiber.new do
41
+ test_benchmark
42
+ EM.stop
43
+ end.resume
44
+ end
45
+ end
46
+
47
+ def test_benchmark
48
+ Benchmark.bm(31) do |x|
49
+
50
+ n = 2500
51
+
52
+ @m = MemCache.new(*@opts)
53
+ x.report("set:plain:memcache-client") do
54
+ n.times do
55
+ @m.set @key1, @marshalled, 0, true
56
+ @m.set @key2, @marshalled, 0, true
57
+ @m.set @key3, @marshalled, 0, true
58
+ @m.set @key1, @marshalled, 0, true
59
+ @m.set @key2, @marshalled, 0, true
60
+ @m.set @key3, @marshalled, 0, true
61
+ end
62
+ end
63
+
64
+ @m = MemCache.new(*@opts)
65
+ x.report("set:ruby:memcache-client") do
66
+ n.times do
67
+ @m.set @key1, @value
68
+ @m.set @key2, @value
69
+ @m.set @key3, @value
70
+ @m.set @key1, @value
71
+ @m.set @key2, @value
72
+ @m.set @key3, @value
73
+ end
74
+ end
75
+
76
+ @m = MemCache.new(*@opts)
77
+ x.report("get:plain:memcache-client") do
78
+ n.times do
79
+ @m.get @key1, true
80
+ @m.get @key2, true
81
+ @m.get @key3, true
82
+ @m.get @key1, true
83
+ @m.get @key2, true
84
+ @m.get @key3, true
85
+ end
86
+ end
87
+
88
+ @m = MemCache.new(*@opts)
89
+ x.report("get:ruby:memcache-client") do
90
+ n.times do
91
+ @m.get @key1
92
+ @m.get @key2
93
+ @m.get @key3
94
+ @m.get @key1
95
+ @m.get @key2
96
+ @m.get @key3
97
+ end
98
+ end
99
+
100
+ @m = MemCache.new(*@opts)
101
+ x.report("multiget:ruby:memcache-client") do
102
+ n.times do
103
+ # We don't use the keys array because splat is slow
104
+ @m.get_multi @key1, @key2, @key3, @key4, @key5, @key6
105
+ end
106
+ end
107
+
108
+ @m = MemCache.new(*@opts)
109
+ x.report("missing:ruby:memcache-client") do
110
+ n.times do
111
+ begin @m.delete @key1; rescue; end
112
+ begin @m.get @key1; rescue; end
113
+ begin @m.delete @key2; rescue; end
114
+ begin @m.get @key2; rescue; end
115
+ begin @m.delete @key3; rescue; end
116
+ begin @m.get @key3; rescue; end
117
+ end
118
+ end
119
+
120
+ @m = MemCache.new(*@opts)
121
+ x.report("mixed:ruby:memcache-client") do
122
+ n.times do
123
+ @m.set @key1, @value
124
+ @m.set @key2, @value
125
+ @m.set @key3, @value
126
+ @m.get @key1
127
+ @m.get @key2
128
+ @m.get @key3
129
+ @m.set @key1, @value
130
+ @m.get @key1
131
+ @m.set @key2, @value
132
+ @m.get @key2
133
+ @m.set @key3, @value
134
+ @m.get @key3
135
+ end
136
+ end
137
+
138
+ assert true
139
+ end
140
+
141
+ end
142
+ end