dsander-memcache-client 1.7.7.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/FAQ.rdoc +31 -0
- data/History.rdoc +241 -0
- data/LICENSE.txt +28 -0
- data/README.rdoc +51 -0
- data/Rakefile +39 -0
- data/VERSION.yml +5 -0
- data/lib/continuum_native.rb +41 -0
- data/lib/memcache.rb +1209 -0
- data/lib/memcache_util.rb +105 -0
- data/performance.txt +143 -0
- data/test/test_benchmark.rb +134 -0
- data/test/test_mem_cache.rb +1238 -0
- metadata +70 -0
data/FAQ.rdoc
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
= Memcache-client FAQ
|
2
|
+
|
3
|
+
== Does memcache-client work with Ruby 1.9?
|
4
|
+
|
5
|
+
Yes, Ruby 1.9 is supported. The test suite should pass completely on 1.8.6 and 1.9.1.
|
6
|
+
|
7
|
+
|
8
|
+
== I'm seeing "execution expired" or "time's up!" errors, what's that all about?
|
9
|
+
|
10
|
+
memcache-client 1.6.x+ now has socket operations timed out by default. This is to prevent
|
11
|
+
the Ruby process from hanging if memcached or starling get into a bad state, which has been
|
12
|
+
seen in production by both 37signals and FiveRuns. The default timeout is 0.5 seconds, which
|
13
|
+
should be more than enough time under normal circumstances. It's possible to hit a storm of
|
14
|
+
concurrent events which cause this timer to expire: a large Ruby VM can cause the GC to take
|
15
|
+
a while, while also storing a large (500k-1MB value), for example.
|
16
|
+
|
17
|
+
You can increase the timeout or disable them completely with the following configuration:
|
18
|
+
|
19
|
+
Rails:
|
20
|
+
config.cache_store = :mem_cache_store, 'server1', 'server2', { :timeout => nil } # no timeout
|
21
|
+
|
22
|
+
native:
|
23
|
+
MemCache.new ['server1', 'server2'], { :timeout => 1.0 } # 1 second timeout
|
24
|
+
|
25
|
+
|
26
|
+
== Isn't Evan Weaver's memcached gem faster?
|
27
|
+
|
28
|
+
The latest version of memcached-client is anywhere from 33% to 100% slower than memcached in various benchmarks. Keep in mind this means that 10,000 get requests take 1.8 sec instead of 1.2 seconds.
|
29
|
+
In practice, memcache-client is unlikely to be a bottleneck in your system but there is always going
|
30
|
+
to be an overhead to pure Ruby. memcache-client does have the advantage of built-in integration into
|
31
|
+
Rails and should work on non-MRI platforms: JRuby, MacRuby, etc.
|
data/History.rdoc
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
= 1.7.6 (2009-11-03)
|
2
|
+
|
3
|
+
* Reworked socket timeout code due to several customer complaints about timeouts not
|
4
|
+
working 100% of the time since 1.7.3.
|
5
|
+
* Add option to configure the namespace separator string, for interop with Perl
|
6
|
+
which does not use a separator character:
|
7
|
+
MemCache.new(servers, :namespace_separator => '')
|
8
|
+
* Move to jeweler and gemcutter for RubyGem support.
|
9
|
+
|
10
|
+
= 1.7.5 (2009-09-09)
|
11
|
+
|
12
|
+
* Fix ruby warnings (josh)
|
13
|
+
* Make megabyte value size limit optional since Tokyo Tyrant can accept values larger than 1MB.
|
14
|
+
Use :check_size => false to disable the size check. (jsl)
|
15
|
+
* Ruby 1.9 support for recent I/O changes.
|
16
|
+
* Fix duplicate value marshalling on server error. (rajiv)
|
17
|
+
* Added option :autofix_keys (disabled by default) to replace long keys with md5 hashes (sd)
|
18
|
+
|
19
|
+
= 1.7.4 (2009-06-09)
|
20
|
+
|
21
|
+
* Fix issue with raising timeout errors.
|
22
|
+
|
23
|
+
= 1.7.3 (2009-06-06)
|
24
|
+
|
25
|
+
* Remove SystemTimer support, refactor I/O to use nonblocking operations. Speeds up
|
26
|
+
performance approx 100%. Timeouts basically have no overhead now! (tenderlove)
|
27
|
+
* Update load logic to support SystemTimer running in Ruby Enterprise Edition. Thanks
|
28
|
+
to splattael on github for the comment.
|
29
|
+
|
30
|
+
= 1.7.2 (2009-04-12)
|
31
|
+
|
32
|
+
* Rollback socket timeout optimization. It does not work on all operating systems
|
33
|
+
and was a support headache.
|
34
|
+
|
35
|
+
= 1.7.1 (2009-03-28)
|
36
|
+
|
37
|
+
* Performance optimizations:
|
38
|
+
* Rely on higher performance operating system socket timeouts for low-level socket
|
39
|
+
read/writes where possible, instead of the (slower) SystemTimer or (slowest,
|
40
|
+
unreliable) Timeout libraries.
|
41
|
+
* the native binary search is back! The recent performance tuning made the binary search
|
42
|
+
a bottleneck again so it had to return. It uses RubyInline to compile the native extension and
|
43
|
+
silently falls back to pure Ruby if anything fails. Make sure you run:
|
44
|
+
`gem install RubyInline` if you want ultimate performance.
|
45
|
+
* the changes make memcache-client 100% faster than 1.7.0 in my performance test on Ruby 1.8.6:
|
46
|
+
15 sec -> 8 sec.
|
47
|
+
* Fix several logging issues.
|
48
|
+
|
49
|
+
= 1.7.0 (2009-03-08)
|
50
|
+
|
51
|
+
* Go through the memcached protocol document and implement any commands not already implemented:
|
52
|
+
- cas
|
53
|
+
- append
|
54
|
+
- prepend
|
55
|
+
- replace
|
56
|
+
|
57
|
+
Append and prepend only work with raw data since it makes no sense to concatenate two Marshalled
|
58
|
+
values together. The cas functionality should be considered a prototype. Since I don't have an
|
59
|
+
application which uses +cas+, I'm not sure what semantic sugar the API should provide. Should it
|
60
|
+
retry if the value was changed? Should it massage the returned string into true/false? Feedback
|
61
|
+
would be appreciated.
|
62
|
+
|
63
|
+
* Add fetch method which provides a method very similar to ActiveSupport::Cache::Store#fetch,
|
64
|
+
basically a wrapper around get and add. (djanowski)
|
65
|
+
|
66
|
+
* Implement the flush_all delay parameter, to allow a large memcached farm to be flushed gradually.
|
67
|
+
|
68
|
+
* Implement the noreply flag, which tells memcached not to reply in operations which don't
|
69
|
+
need a reply, i.e. set/add/delete/flush_all.
|
70
|
+
|
71
|
+
* The only known functionality not implemented anymore is the <flags> parameter to the storage
|
72
|
+
commands. This would require modification of the API method signatures. If someone can come
|
73
|
+
up with a clean way to implement it, I would be happy to consider including it.
|
74
|
+
|
75
|
+
= 1.6.5 (2009-02-27)
|
76
|
+
|
77
|
+
* Change memcache-client to multithreaded by default. The mutex does not add significant
|
78
|
+
overhead and it is far too easy, now that Sinatra, Rails and Merb are all thread-safe, to
|
79
|
+
use memcache-client in a thread-unsafe manner. Remove some unnecessary mutexing and add
|
80
|
+
a test to verify heavily multithreaded usage does not act unexpectedly.
|
81
|
+
|
82
|
+
* Add optional support for the SystemTimer gem when running on Ruby 1.8.x. This gem is
|
83
|
+
highly recommended - it ensures timeouts actually work and halves the overhead of using
|
84
|
+
timeouts. Using this gem, Ruby 1.8.x is actually faster in my performance tests
|
85
|
+
than Ruby 1.9.x. Just "gem install SystemTimer" and it should be picked up automatically.
|
86
|
+
|
87
|
+
= 1.6.4 (2009-02-19)
|
88
|
+
|
89
|
+
* Remove native code altogether. The speedup was only 10% on Ruby 1.8.6 and did not work
|
90
|
+
on Ruby 1.9.1.
|
91
|
+
|
92
|
+
* Removed memcache_util.rb from the distribution. If you are using it, please copy the code
|
93
|
+
into your own project. The file will live in the github repository for a few more months
|
94
|
+
for this purposes. http://github.com/mperham/memcache-client/raw/7a276089aa3c914e47e3960f9740ac7377204970/lib/memcache_util.rb
|
95
|
+
|
96
|
+
* Roll continuum.rb into memcache.rb. The project is again a single Ruby file, with no dependencies.
|
97
|
+
|
98
|
+
= 1.6.3 (2009-02-14)
|
99
|
+
|
100
|
+
* Remove gem native extension in preference to RubyInline. This allows the gem to install
|
101
|
+
and work on JRuby and Ruby 1.8.5 when the native code fails to compile.
|
102
|
+
|
103
|
+
= 1.6.2 (2009-02-04)
|
104
|
+
|
105
|
+
* Validate that values are less than one megabyte in size.
|
106
|
+
|
107
|
+
* Refactor error handling in get_multi to handle server failures and return what values
|
108
|
+
we could successfully retrieve.
|
109
|
+
|
110
|
+
* Add optional logging parameter for debugging and tracing.
|
111
|
+
|
112
|
+
* First official release since 1.5.0. Thanks to Eric Hodel for turning over the project to me!
|
113
|
+
New project home page: http://github.com/mperham/memcache-client
|
114
|
+
|
115
|
+
= 1.6.1 (2009-01-28)
|
116
|
+
|
117
|
+
* Add option to disable socket timeout support. Socket timeout has a significant performance
|
118
|
+
penalty (approx 3x slower than without in Ruby 1.8.6). You can turn off the timeouts if you
|
119
|
+
need absolute performance, but by default timeouts are enabled. The performance
|
120
|
+
penalty is much lower in Ruby 1.8.7, 1.9 and JRuby. (mperham)
|
121
|
+
|
122
|
+
* Add option to disable server failover. Failover can lead to "split-brain" caches that
|
123
|
+
return stale data. (mperham)
|
124
|
+
|
125
|
+
* Implement continuum binary search in native code for performance reasons. Pure ruby
|
126
|
+
is available for platforms like JRuby or Rubinius which can't use C extensions. (mperham)
|
127
|
+
|
128
|
+
* Fix #add with raw=true (iamaleksey)
|
129
|
+
|
130
|
+
= 1.6.0
|
131
|
+
|
132
|
+
* Implement a consistent hashing algorithm, as described in libketama.
|
133
|
+
This dramatically reduces the cost of adding or removing servers dynamically
|
134
|
+
as keys are much more likely to map to the same server.
|
135
|
+
|
136
|
+
Take a scenario where we add a fourth server. With a naive modulo algorithm, about
|
137
|
+
25% of the keys will map to the same server. In other words, 75% of your memcached
|
138
|
+
content suddenly becomes invalid. With a consistent algorithm, 75% of the keys
|
139
|
+
will map to the same server as before - only 25% will be invalidated. (mperham)
|
140
|
+
|
141
|
+
* Implement socket timeouts, should fix rare cases of very bad things happening
|
142
|
+
in production at 37signals and FiveRuns. (jseirles)
|
143
|
+
|
144
|
+
= 1.5.0.5
|
145
|
+
|
146
|
+
* Remove native C CRC32_ITU_T extension in favor of Zlib's crc32 method.
|
147
|
+
memcache-client is now pure Ruby again and will work with JRuby and Rubinius.
|
148
|
+
|
149
|
+
= 1.5.0.4
|
150
|
+
|
151
|
+
* Get test suite working again (packagethief)
|
152
|
+
* Ruby 1.9 compatiblity fixes (packagethief, mperham)
|
153
|
+
* Consistently return server responses and check for errors (packagethief)
|
154
|
+
* Properly calculate CRC in Ruby 1.9 strings (mperham)
|
155
|
+
* Drop rspec in favor of test/unit, for 1.9 compat (mperham)
|
156
|
+
|
157
|
+
= 1.5.0.3 (FiveRuns fork)
|
158
|
+
|
159
|
+
* Integrated ITU-T CRC32 operation in native C extension for speed. Thanks to Justin Balthrop!
|
160
|
+
|
161
|
+
= 1.5.0.2 (FiveRuns fork)
|
162
|
+
|
163
|
+
* Add support for seamless failover between servers. If one server connection dies,
|
164
|
+
the client will retry the operation on another server before giving up.
|
165
|
+
|
166
|
+
* Merge Will Bryant's socket retry patch.
|
167
|
+
http://willbryant.net/software/2007/12/21/ruby-memcache-client-reconnect-and-retry
|
168
|
+
|
169
|
+
= 1.5.0.1 (FiveRuns fork)
|
170
|
+
|
171
|
+
* Fix set not handling client disconnects.
|
172
|
+
http://dev.twitter.com/2008/02/solving-case-of-missing-updates.html
|
173
|
+
|
174
|
+
= 1.5.0
|
175
|
+
|
176
|
+
* Add MemCache#flush_all command. Patch #13019 and bug #10503. Patches
|
177
|
+
submitted by Sebastian Delmont and Rick Olson.
|
178
|
+
* Type-cast data returned by MemCache#stats. Patch #10505 submitted by
|
179
|
+
Sebastian Delmont.
|
180
|
+
|
181
|
+
= 1.4.0
|
182
|
+
|
183
|
+
* Fix bug #10371, #set does not check response for server errors.
|
184
|
+
Submitted by Ben VandenBos.
|
185
|
+
* Fix bug #12450, set TCP_NODELAY socket option. Patch by Chris
|
186
|
+
McGrath.
|
187
|
+
* Fix bug #10704, missing #add method. Patch by Jamie Macey.
|
188
|
+
* Fix bug #10371, handle socket EOF in cache_get. Submitted by Ben
|
189
|
+
VandenBos.
|
190
|
+
|
191
|
+
= 1.3.0
|
192
|
+
|
193
|
+
* Apply patch #6507, add stats command. Submitted by Tyler Kovacs.
|
194
|
+
* Apply patch #6509, parallel implementation of #get_multi. Submitted
|
195
|
+
by Tyler Kovacs.
|
196
|
+
* Validate keys. Disallow spaces in keys or keys that are too long.
|
197
|
+
* Perform more validation of server responses. MemCache now reports
|
198
|
+
errors if the socket was not in an expected state. (Please file
|
199
|
+
bugs if you find some.)
|
200
|
+
* Add #incr and #decr.
|
201
|
+
* Add raw argument to #set and #get to retrieve #incr and #decr
|
202
|
+
values.
|
203
|
+
* Also put on MemCacheError when using Cache::get with block.
|
204
|
+
* memcache.rb no longer sets $TESTING to a true value if it was
|
205
|
+
previously defined. Bug #8213 by Matijs van Zuijlen.
|
206
|
+
|
207
|
+
= 1.2.1
|
208
|
+
|
209
|
+
* Fix bug #7048, MemCache#servers= referenced changed local variable.
|
210
|
+
Submitted by Justin Dossey.
|
211
|
+
* Fix bug #7049, MemCache#initialize resets @buckets. Submitted by
|
212
|
+
Justin Dossey.
|
213
|
+
* Fix bug #6232, Make Cache::Get work with a block only when nil is
|
214
|
+
returned. Submitted by Jon Evans.
|
215
|
+
* Moved to the seattlerb project.
|
216
|
+
|
217
|
+
= 1.2.0
|
218
|
+
|
219
|
+
NOTE: This version will store keys in different places than previous
|
220
|
+
versions! Be prepared for some thrashing while memcached sorts itself
|
221
|
+
out!
|
222
|
+
|
223
|
+
* Fixed multithreaded operations, bug 5994 and 5989.
|
224
|
+
Thanks to Blaine Cook, Erik Hetzner, Elliot Smith, Dave Myron (and
|
225
|
+
possibly others I have forgotten).
|
226
|
+
* Made memcached interoperable with other memcached libraries, bug
|
227
|
+
4509. Thanks to anonymous.
|
228
|
+
* Added get_multi to match Perl/etc APIs
|
229
|
+
|
230
|
+
= 1.1.0
|
231
|
+
|
232
|
+
* Added some tests
|
233
|
+
* Sped up non-multithreaded and multithreaded operation
|
234
|
+
* More Ruby-memcache compatibility
|
235
|
+
* More RDoc
|
236
|
+
* Switched to Hoe
|
237
|
+
|
238
|
+
= 1.0.0
|
239
|
+
|
240
|
+
Birthday!
|
241
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Copyright 2005-2009 Bob Cottrell, Eric Hodel, Mike Perham.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
|
8
|
+
1. Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
12
|
+
documentation and/or other materials provided with the distribution.
|
13
|
+
3. Neither the names of the authors nor the names of their contributors
|
14
|
+
may be used to endorse or promote products derived from this software
|
15
|
+
without specific prior written permission.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
18
|
+
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
21
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
22
|
+
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
23
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
24
|
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
26
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
27
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
= memcache-client
|
2
|
+
|
3
|
+
A ruby library for accessing memcached.
|
4
|
+
|
5
|
+
Source:
|
6
|
+
|
7
|
+
http://github.com/mperham/memcache-client
|
8
|
+
|
9
|
+
== Installing memcache-client
|
10
|
+
|
11
|
+
Just install the gem:
|
12
|
+
|
13
|
+
$ sudo gem install memcache-client
|
14
|
+
|
15
|
+
== Using memcache-client
|
16
|
+
|
17
|
+
With one server:
|
18
|
+
|
19
|
+
CACHE = MemCache.new 'localhost:11211'
|
20
|
+
|
21
|
+
Or with multiple servers:
|
22
|
+
|
23
|
+
CACHE = MemCache.new %w[one.example.com:11211 two.example.com:11211]
|
24
|
+
|
25
|
+
|
26
|
+
== Tuning memcache-client
|
27
|
+
|
28
|
+
The MemCache.new method takes a number of options which can be useful at times. Please
|
29
|
+
read the source comments there for an overview. If you are using Ruby 1.8.x and using
|
30
|
+
multiple memcached servers, you should install the RubyInline gem for ultimate performance.
|
31
|
+
|
32
|
+
|
33
|
+
== Using memcache-client with Rails
|
34
|
+
|
35
|
+
Rails 2.1+ includes memcache-client 1.5.0 out of the box. See ActiveSupport::Cache::MemCacheStore
|
36
|
+
and the Rails.cache method for more details. Rails 2.3+ will use the latest memcache-client
|
37
|
+
gem installed.
|
38
|
+
|
39
|
+
|
40
|
+
== Questions?
|
41
|
+
|
42
|
+
memcache-client is maintained by Mike Perham and was originally written by Bob Cottrell,
|
43
|
+
Eric Hodel and the seattle.rb crew.
|
44
|
+
|
45
|
+
Email:: mailto:mperham@gmail.com
|
46
|
+
Twitter:: mperham[http://twitter.com/mperham]
|
47
|
+
WWW:: http://mikeperham.com
|
48
|
+
|
49
|
+
If my work on memcache-client is something you support, please take a moment to
|
50
|
+
recommend me at WWR[http://workingwithrails.com/person/10797-mike-perham]. I'm not
|
51
|
+
asking for money, just a electronic "thumbs up".
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# vim: syntax=Ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |s|
|
9
|
+
s.name = "dsander-memcache-client"
|
10
|
+
s.summary = s.description = "A Ruby library for accessing memcached."
|
11
|
+
s.email = "mperham@gmail.com"
|
12
|
+
s.homepage = "http://github.com/mperham/memcache-client"
|
13
|
+
s.authors = ['Eric Hodel', 'Robert Cottrell', 'Mike Perham']
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.files = FileList["[A-Z]*", "{lib,test}/**/*", 'performance.txt']
|
16
|
+
s.test_files = FileList["test/test_*.rb"]
|
17
|
+
s.rubyforge_project = 'seattlerb'
|
18
|
+
end
|
19
|
+
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
Rake::RDocTask.new do |rd|
|
26
|
+
rd.main = "README.rdoc"
|
27
|
+
rd.rdoc_files.include("README.rdoc", "FAQ.rdoc", "History.rdoc", "lib/memcache.rb")
|
28
|
+
rd.rdoc_dir = 'doc'
|
29
|
+
end
|
30
|
+
|
31
|
+
Rake::TestTask.new do |t|
|
32
|
+
t.warning = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
task :rcov do
|
38
|
+
`rcov -Ilib test/*.rb`
|
39
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Continuum
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# Native extension to perform the binary search within the continuum
|
6
|
+
# space. There's a pure ruby version in memcache.rb so this is purely
|
7
|
+
# optional for performance and only necessary if you are using multiple
|
8
|
+
# memcached servers.
|
9
|
+
begin
|
10
|
+
require 'inline'
|
11
|
+
inline do |builder|
|
12
|
+
builder.c <<-EOM
|
13
|
+
int binary_search(VALUE ary, unsigned int r) {
|
14
|
+
int upper = RARRAY_LEN(ary) - 1;
|
15
|
+
int lower = 0;
|
16
|
+
int idx = 0;
|
17
|
+
ID value = rb_intern("value");
|
18
|
+
|
19
|
+
while (lower <= upper) {
|
20
|
+
idx = (lower + upper) / 2;
|
21
|
+
|
22
|
+
VALUE continuumValue = rb_funcall(RARRAY_PTR(ary)[idx], value, 0);
|
23
|
+
unsigned int l = NUM2UINT(continuumValue);
|
24
|
+
if (l == r) {
|
25
|
+
return idx;
|
26
|
+
}
|
27
|
+
else if (l > r) {
|
28
|
+
upper = idx - 1;
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
lower = idx + 1;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
return upper;
|
35
|
+
}
|
36
|
+
EOM
|
37
|
+
end
|
38
|
+
rescue Exception => e
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|