jruby-memcached 0.4.1 → 0.5.0
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.
- data/Gemfile +2 -0
- data/Gemfile.lock +3 -1
- data/Rakefile +14 -0
- data/benchmark.rb +2 -4
- data/lib/memcached/version.rb +1 -1
- data/pom.xml +1 -1
- data/spec/memcached_spec.rb +96 -14
- data/src/main/java/com/openfeint/memcached/Memcached.java +234 -71
- data/src/main/java/com/openfeint/memcached/MemcachedService.java +1 -0
- data/src/main/java/com/openfeint/memcached/error/ATimeoutOccurred.java +7 -0
- data/src/main/java/com/openfeint/memcached/error/Error.java +4 -0
- data/src/main/java/com/openfeint/memcached/transcoder/MarshalTranscoder.java +42 -14
- data/src/main/java/com/openfeint/memcached/transcoder/MarshalZlibTranscoder.java +44 -51
- data/target/spymemcached-ext-0.0.1.jar +0 -0
- metadata +3 -2
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
jruby-memcached (0.
|
4
|
+
jruby-memcached (0.5.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
@@ -10,6 +10,7 @@ GEM
|
|
10
10
|
metaclass (0.0.1)
|
11
11
|
mocha (0.12.1)
|
12
12
|
metaclass (~> 0.0.1)
|
13
|
+
rake (0.9.2.2)
|
13
14
|
rspec (2.11.0)
|
14
15
|
rspec-core (~> 2.11.0)
|
15
16
|
rspec-expectations (~> 2.11.0)
|
@@ -25,4 +26,5 @@ PLATFORMS
|
|
25
26
|
DEPENDENCIES
|
26
27
|
jruby-memcached!
|
27
28
|
mocha
|
29
|
+
rake
|
28
30
|
rspec
|
data/Rakefile
CHANGED
@@ -1,2 +1,16 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require "rake"
|
5
|
+
require "rspec"
|
6
|
+
require "rspec/core/rake_task"
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
9
|
+
require "memcached/version"
|
10
|
+
|
11
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
12
|
+
spec.pattern = "spec/**/*_spec.rb"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => :spec
|
16
|
+
task :test => :spec
|
data/benchmark.rb
CHANGED
@@ -52,10 +52,8 @@ dalli = Dalli::Client.new(['localhost:11211'])
|
|
52
52
|
}
|
53
53
|
}
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
spymemcached.shutdown
|
58
|
-
end
|
55
|
+
memcached.quit
|
56
|
+
spymemcached.shutdown if JRUBY
|
59
57
|
dalli.close
|
60
58
|
|
61
59
|
# I run benchmark for each client 3 times, but only list the last result for each.
|
data/lib/memcached/version.rb
CHANGED
data/pom.xml
CHANGED
data/spec/memcached_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Memcached do
|
4
4
|
context "localhost" do
|
5
5
|
before(:all) { @memcached = Memcached.new(["127.0.0.1:11211"]) }
|
6
|
-
after(:all) { @memcached.
|
6
|
+
after(:all) { @memcached.quit }
|
7
7
|
|
8
8
|
it "should get all servers" do
|
9
9
|
@memcached.set "foo", "bar"
|
@@ -124,14 +124,51 @@ describe Memcached do
|
|
124
124
|
@memcached.delete "key" rescue nil
|
125
125
|
lambda { @memcached.delete "key" }.should raise_error(Memcached::NotFound)
|
126
126
|
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "increment" do
|
130
|
+
it "should increment to default value" do
|
131
|
+
@memcached.delete "intkey" rescue nil
|
132
|
+
@memcached.increment "intkey"
|
133
|
+
@memcached.get("intkey").should == 1
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should increment by 1" do
|
137
|
+
@memcached.delete "intkey" rescue nil
|
138
|
+
@memcached.increment "intkey"
|
139
|
+
@memcached.increment "intkey"
|
140
|
+
@memcached.get("intkey").should == 2
|
141
|
+
end
|
127
142
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
143
|
+
it "should increment by 10" do
|
144
|
+
@memcached.delete "intkey" rescue nil
|
145
|
+
@memcached.increment "intkey"
|
146
|
+
@memcached.increment "intkey", 10
|
147
|
+
@memcached.get("intkey").should == 11
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "decrement" do
|
152
|
+
it "should decrement to default value" do
|
153
|
+
@memcached.delete "intkey" rescue nil
|
154
|
+
@memcached.decrement "intkey"
|
155
|
+
@memcached.get("intkey").should == 0
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should decrement by 1" do
|
159
|
+
@memcached.delete "intkey" rescue nil
|
160
|
+
2.times { @memcached.increment "intkey" }
|
161
|
+
@memcached.decrement "intkey"
|
162
|
+
@memcached.get("intkey").should == 1
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should decrement by 10" do
|
166
|
+
@memcached.delete "intkey" rescue nil
|
167
|
+
@memcached.increment "intkey"
|
168
|
+
@memcached.increment "intkey", 20
|
169
|
+
@memcached.decrement "intkey", 10
|
170
|
+
@memcached.get("intkey").should == 11
|
171
|
+
end
|
135
172
|
end
|
136
173
|
|
137
174
|
context "flush" do
|
@@ -163,12 +200,6 @@ describe Memcached do
|
|
163
200
|
@memcached.get("jrubykey").should == "value"
|
164
201
|
end
|
165
202
|
|
166
|
-
#it "should incr/decr with prefix_key" do
|
167
|
-
#@prefix_memcached.incr "intkey"
|
168
|
-
#@prefix_memcached.decr "intkey"
|
169
|
-
#@memcached.get("jrubyintkey").should == 0
|
170
|
-
#end
|
171
|
-
|
172
203
|
it "should add/replace with prefix_key" do
|
173
204
|
@prefix_memcached.add "newkey", "value"
|
174
205
|
@prefix_memcached.replace "newkey", "new_value"
|
@@ -186,6 +217,57 @@ describe Memcached do
|
|
186
217
|
@prefix_memcached.set "key2", "value2"
|
187
218
|
@prefix_memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
|
188
219
|
end
|
220
|
+
|
221
|
+
it "should increment/decrement with prefix_key" do
|
222
|
+
@prefix_memcached.delete "intkey" rescue nil
|
223
|
+
@prefix_memcached.increment "intkey"
|
224
|
+
@prefix_memcached.increment "intkey", 10
|
225
|
+
@prefix_memcached.decrement "intkey", 5
|
226
|
+
@memcached.get("jrubyintkey").should == 6
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "timeout" do
|
232
|
+
before(:all) do
|
233
|
+
@memcached = Memcached.new("127.0.0.1:11211")
|
234
|
+
@timeout_memcached = Memcached.new("127.0.0.1:11211", :timeout => 1, :exception_retry_limit => 0)
|
235
|
+
end
|
236
|
+
after(:all) do
|
237
|
+
@timeout_memcached.quit
|
238
|
+
@memcached.quit
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should set timeout" do
|
242
|
+
lambda { @timeout_memcached.set "key", "new_value" }.should raise_error(Memcached::ATimeoutOccurred)
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should add timeout" do
|
246
|
+
@memcached.delete "key" rescue nil
|
247
|
+
lambda { @timeout_memcached.add "key", "new_value" }.should raise_error(Memcached::ATimeoutOccurred)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should replace timeout" do
|
251
|
+
@memcached.set "key", "value"
|
252
|
+
lambda { @timeout_memcached.replace "key", "new_value" }.should raise_error(Memcached::ATimeoutOccurred)
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should delete timeout" do
|
256
|
+
@memcached.set "key", "value"
|
257
|
+
lambda { @timeout_memcached.delete "key" }.should raise_error(Memcached::ATimeoutOccurred)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should get timeout" do
|
261
|
+
@memcached.set "key", "value"
|
262
|
+
lambda { @timeout_memcached.get "key" }.should raise_error(Memcached::ATimeoutOccurred)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should increment timeout" do
|
266
|
+
lambda { @timeout_memcached.increment "intkey" }.should raise_error(Memcached::ATimeoutOccurred)
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should decrement timeout" do
|
270
|
+
lambda { @timeout_memcached.decrement "intkey" }.should raise_error(Memcached::ATimeoutOccurred)
|
189
271
|
end
|
190
272
|
end
|
191
273
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
package com.openfeint.memcached;
|
2
2
|
|
3
3
|
import com.openfeint.memcached.error.Error;
|
4
|
+
import com.openfeint.memcached.error.NotFound;
|
4
5
|
import com.openfeint.memcached.transcoder.MarshalTranscoder;
|
5
6
|
import com.openfeint.memcached.transcoder.MarshalZlibTranscoder;
|
6
7
|
import net.spy.memcached.AddrUtil;
|
@@ -9,6 +10,7 @@ import net.spy.memcached.ConnectionFactoryBuilder.Locator;
|
|
9
10
|
import net.spy.memcached.ConnectionFactoryBuilder.Protocol;
|
10
11
|
import net.spy.memcached.DefaultHashAlgorithm;
|
11
12
|
import net.spy.memcached.MemcachedClient;
|
13
|
+
import net.spy.memcached.OperationTimeoutException;
|
12
14
|
import net.spy.memcached.transcoders.Transcoder;
|
13
15
|
import org.jruby.Ruby;
|
14
16
|
import org.jruby.RubyArray;
|
@@ -19,6 +21,7 @@ import org.jruby.RubyObject;
|
|
19
21
|
import org.jruby.RubyString;
|
20
22
|
import org.jruby.anno.JRubyClass;
|
21
23
|
import org.jruby.anno.JRubyMethod;
|
24
|
+
import org.jruby.exceptions.RaiseException;
|
22
25
|
import org.jruby.runtime.ThreadContext;
|
23
26
|
import org.jruby.runtime.builtin.IRubyObject;
|
24
27
|
|
@@ -34,16 +37,22 @@ import java.util.concurrent.ExecutionException;
|
|
34
37
|
public class Memcached extends RubyObject {
|
35
38
|
private MemcachedClient client;
|
36
39
|
|
37
|
-
private Transcoder
|
40
|
+
private Transcoder transcoder;
|
38
41
|
|
39
42
|
private int ttl;
|
40
43
|
|
44
|
+
private int timeout;
|
45
|
+
|
46
|
+
private int exceptionRetryLimit;
|
47
|
+
|
41
48
|
private String prefixKey;
|
42
49
|
|
43
50
|
public Memcached(final Ruby ruby, RubyClass rubyClass) {
|
44
51
|
super(ruby, rubyClass);
|
45
52
|
|
46
53
|
ttl = 604800;
|
54
|
+
timeout = -1;
|
55
|
+
exceptionRetryLimit = 5;
|
47
56
|
prefixKey = "";
|
48
57
|
}
|
49
58
|
|
@@ -89,17 +98,41 @@ public class Memcached extends RubyObject {
|
|
89
98
|
Ruby ruby = context.getRuntime();
|
90
99
|
String key = getFullKey(args[0].toString());
|
91
100
|
IRubyObject value = args[1];
|
92
|
-
int
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
101
|
+
int expiry = getExpiry(args);
|
102
|
+
int retry = 0;
|
103
|
+
while (true) {
|
104
|
+
try {
|
105
|
+
boolean result = (Boolean) client.add(key, expiry, value, transcoder).get();
|
106
|
+
if (result == false) {
|
107
|
+
throw Error.newNotStored(ruby, "not stored");
|
108
|
+
}
|
109
|
+
return context.nil;
|
110
|
+
} catch (RaiseException e) {
|
111
|
+
throw e;
|
112
|
+
} catch (ExecutionException e) {
|
113
|
+
if ("net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
114
|
+
if (retry == exceptionRetryLimit) {
|
115
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
116
|
+
}
|
117
|
+
retry++;
|
118
|
+
continue;
|
119
|
+
} else {
|
120
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
121
|
+
}
|
122
|
+
} catch (RuntimeException e) {
|
123
|
+
if (e.getCause() != null &&
|
124
|
+
"net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
125
|
+
if (retry == exceptionRetryLimit) {
|
126
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
127
|
+
}
|
128
|
+
retry++;
|
129
|
+
continue;
|
130
|
+
} else {
|
131
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
132
|
+
}
|
133
|
+
} catch (InterruptedException e) {
|
134
|
+
throw ruby.newThreadError(e.getLocalizedMessage());
|
97
135
|
}
|
98
|
-
return context.nil;
|
99
|
-
} catch (ExecutionException ee) {
|
100
|
-
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
101
|
-
} catch (InterruptedException ie) {
|
102
|
-
throw ruby.newThreadError(ie.getLocalizedMessage());
|
103
136
|
}
|
104
137
|
}
|
105
138
|
|
@@ -108,17 +141,41 @@ public class Memcached extends RubyObject {
|
|
108
141
|
Ruby ruby = context.getRuntime();
|
109
142
|
String key = getFullKey(args[0].toString());
|
110
143
|
IRubyObject value = args[1];
|
111
|
-
int
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
144
|
+
int expiry = getExpiry(args);
|
145
|
+
int retry = 0;
|
146
|
+
while (true) {
|
147
|
+
try {
|
148
|
+
boolean result = (Boolean) client.replace(key, expiry, value, transcoder).get();
|
149
|
+
if (result == false) {
|
150
|
+
throw Error.newNotStored(ruby, "not stored");
|
151
|
+
}
|
152
|
+
return context.nil;
|
153
|
+
} catch (RaiseException e) {
|
154
|
+
throw e;
|
155
|
+
} catch (ExecutionException e) {
|
156
|
+
if ("net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
157
|
+
if (retry == exceptionRetryLimit) {
|
158
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
159
|
+
}
|
160
|
+
retry++;
|
161
|
+
continue;
|
162
|
+
} else {
|
163
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
164
|
+
}
|
165
|
+
} catch (RuntimeException e) {
|
166
|
+
if (e.getCause() != null &&
|
167
|
+
"net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
168
|
+
if (retry == exceptionRetryLimit) {
|
169
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
170
|
+
}
|
171
|
+
retry++;
|
172
|
+
continue;
|
173
|
+
} else {
|
174
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
175
|
+
}
|
176
|
+
} catch (InterruptedException e) {
|
177
|
+
throw ruby.newThreadError(e.getLocalizedMessage());
|
116
178
|
}
|
117
|
-
return context.nil;
|
118
|
-
} catch (ExecutionException ee) {
|
119
|
-
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
120
|
-
} catch (InterruptedException ie) {
|
121
|
-
throw ruby.newThreadError(ie.getLocalizedMessage());
|
122
179
|
}
|
123
180
|
}
|
124
181
|
|
@@ -127,17 +184,41 @@ public class Memcached extends RubyObject {
|
|
127
184
|
Ruby ruby = context.getRuntime();
|
128
185
|
String key = getFullKey(args[0].toString());
|
129
186
|
IRubyObject value = args[1];
|
130
|
-
int
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
187
|
+
int expiry = getExpiry(args);
|
188
|
+
int retry = 0;
|
189
|
+
while (true) {
|
190
|
+
try {
|
191
|
+
boolean result = (Boolean) client.set(key, expiry, value, transcoder).get();
|
192
|
+
if (result == false) {
|
193
|
+
throw Error.newNotStored(ruby, "not stored");
|
194
|
+
}
|
195
|
+
return context.nil;
|
196
|
+
} catch (RaiseException e) {
|
197
|
+
throw e;
|
198
|
+
} catch (ExecutionException e) {
|
199
|
+
if ("net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
200
|
+
if (retry == exceptionRetryLimit) {
|
201
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
202
|
+
}
|
203
|
+
retry++;
|
204
|
+
continue;
|
205
|
+
} else {
|
206
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
207
|
+
}
|
208
|
+
} catch (RuntimeException e) {
|
209
|
+
if (e.getCause() != null &&
|
210
|
+
"net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
211
|
+
if (retry == exceptionRetryLimit) {
|
212
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
213
|
+
}
|
214
|
+
retry++;
|
215
|
+
continue;
|
216
|
+
} else {
|
217
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
218
|
+
}
|
219
|
+
} catch (InterruptedException e) {
|
220
|
+
throw ruby.newThreadError(e.getLocalizedMessage());
|
135
221
|
}
|
136
|
-
return context.nil;
|
137
|
-
} catch (ExecutionException ee) {
|
138
|
-
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
139
|
-
} catch (InterruptedException ie) {
|
140
|
-
throw ruby.newThreadError(ie.getLocalizedMessage());
|
141
222
|
}
|
142
223
|
}
|
143
224
|
|
@@ -145,59 +226,122 @@ public class Memcached extends RubyObject {
|
|
145
226
|
public IRubyObject get(ThreadContext context, IRubyObject[] args) {
|
146
227
|
Ruby ruby = context.getRuntime();
|
147
228
|
IRubyObject keys = args[0];
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
229
|
+
int retry = 0;
|
230
|
+
while (true) {
|
231
|
+
try {
|
232
|
+
if (keys instanceof RubyString) {
|
233
|
+
Object ret = client.get(getFullKey(keys.toString()), transcoder);
|
234
|
+
if (ret == null) {
|
235
|
+
throw Error.newNotFound(ruby, "not found");
|
236
|
+
}
|
237
|
+
IRubyObject value;
|
238
|
+
if (ret instanceof IRubyObject) {
|
239
|
+
value = (IRubyObject) ret;
|
240
|
+
} else {
|
241
|
+
value = ruby.newFixnum((Long) ret);
|
242
|
+
}
|
243
|
+
return value;
|
244
|
+
} else if (keys instanceof RubyArray) {
|
245
|
+
RubyHash results = RubyHash.newHash(ruby);
|
246
|
+
|
247
|
+
Map<String, IRubyObject> bulkResults = client.getBulk(getFullKeys(keys.convertToArray()), transcoder);
|
248
|
+
for (String key : (List<String>) keys.convertToArray()) {
|
249
|
+
if (bulkResults.containsKey(getFullKey(key))) {
|
250
|
+
results.put(key, (IRubyObject) bulkResults.get(getFullKey(key)));
|
251
|
+
}
|
252
|
+
}
|
253
|
+
return results;
|
161
254
|
}
|
255
|
+
} catch (RaiseException e) {
|
256
|
+
throw e;
|
257
|
+
} catch (OperationTimeoutException e) {
|
258
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
259
|
+
} catch (RuntimeException e) {
|
260
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
162
261
|
}
|
163
|
-
return
|
262
|
+
return context.nil;
|
164
263
|
}
|
165
|
-
return context.nil;
|
166
264
|
}
|
167
265
|
|
168
|
-
@JRubyMethod(name = "incr", required = 1, optional = 2)
|
266
|
+
@JRubyMethod(name = { "increment", "incr" }, required = 1, optional = 2)
|
169
267
|
public IRubyObject incr(ThreadContext context, IRubyObject[] args) {
|
170
268
|
Ruby ruby = context.getRuntime();
|
171
269
|
String key = getFullKey(args[0].toString());
|
172
270
|
int by = getIncrDecrBy(args);
|
173
|
-
int
|
174
|
-
|
175
|
-
|
271
|
+
int expiry = getExpiry(args);
|
272
|
+
int retry = 0;
|
273
|
+
while (true) {
|
274
|
+
try {
|
275
|
+
long result = client.incr(key, by, 1, expiry);
|
276
|
+
return ruby.newFixnum(result);
|
277
|
+
} catch (OperationTimeoutException e) {
|
278
|
+
if (retry == exceptionRetryLimit) {
|
279
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
280
|
+
}
|
281
|
+
retry++;
|
282
|
+
continue;
|
283
|
+
}
|
284
|
+
}
|
176
285
|
}
|
177
286
|
|
178
|
-
@JRubyMethod(name = "decr", required = 1, optional = 2)
|
287
|
+
@JRubyMethod(name = { "decrement", "decr" }, required = 1, optional = 2)
|
179
288
|
public IRubyObject decr(ThreadContext context, IRubyObject[] args) {
|
180
289
|
Ruby ruby = context.getRuntime();
|
181
290
|
String key = getFullKey(args[0].toString());
|
182
291
|
int by = getIncrDecrBy(args);
|
183
|
-
int
|
184
|
-
|
185
|
-
|
292
|
+
int expiry = getExpiry(args);
|
293
|
+
int retry = 0;
|
294
|
+
while (true) {
|
295
|
+
try {
|
296
|
+
long result = client.decr(key, by, 0, expiry);
|
297
|
+
return ruby.newFixnum(result);
|
298
|
+
} catch (OperationTimeoutException e) {
|
299
|
+
if (retry == exceptionRetryLimit) {
|
300
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
301
|
+
}
|
302
|
+
retry++;
|
303
|
+
continue;
|
304
|
+
}
|
305
|
+
}
|
186
306
|
}
|
187
307
|
|
188
|
-
@JRubyMethod
|
308
|
+
@JRubyMethod(name = "delete")
|
189
309
|
public IRubyObject delete(ThreadContext context, IRubyObject key) {
|
190
310
|
Ruby ruby = context.getRuntime();
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
311
|
+
int retry = 0;
|
312
|
+
while (true) {
|
313
|
+
try {
|
314
|
+
boolean result = client.delete(getFullKey(key.toString())).get();
|
315
|
+
if (result == false) {
|
316
|
+
throw Error.newNotFound(ruby, "not found");
|
317
|
+
}
|
318
|
+
return context.nil;
|
319
|
+
} catch (RaiseException e) {
|
320
|
+
throw e;
|
321
|
+
} catch (ExecutionException e) {
|
322
|
+
if ("net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
323
|
+
if (retry == exceptionRetryLimit) {
|
324
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
325
|
+
}
|
326
|
+
retry++;
|
327
|
+
continue;
|
328
|
+
} else {
|
329
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
330
|
+
}
|
331
|
+
} catch (RuntimeException e) {
|
332
|
+
if (e.getCause() != null &&
|
333
|
+
"net.spy.memcached.internal.CheckedOperationTimeoutException".equals(e.getCause().getClass().getName())) {
|
334
|
+
if (retry == exceptionRetryLimit) {
|
335
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
336
|
+
}
|
337
|
+
retry++;
|
338
|
+
continue;
|
339
|
+
} else {
|
340
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
341
|
+
}
|
342
|
+
} catch (InterruptedException e) {
|
343
|
+
throw ruby.newThreadError(e.getLocalizedMessage());
|
195
344
|
}
|
196
|
-
return context.nil;
|
197
|
-
} catch (ExecutionException ee) {
|
198
|
-
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
199
|
-
} catch (InterruptedException ie) {
|
200
|
-
throw ruby.newThreadError(ie.getLocalizedMessage());
|
201
345
|
}
|
202
346
|
}
|
203
347
|
|
@@ -207,10 +351,12 @@ public class Memcached extends RubyObject {
|
|
207
351
|
try {
|
208
352
|
client.flush().get();
|
209
353
|
return context.nil;
|
210
|
-
} catch (
|
211
|
-
throw
|
212
|
-
} catch (
|
213
|
-
throw ruby.
|
354
|
+
} catch (OperationTimeoutException e) {
|
355
|
+
throw Error.newATimeoutOccurred(ruby, e.getLocalizedMessage());
|
356
|
+
} catch (ExecutionException e) {
|
357
|
+
throw ruby.newRuntimeError(e.getLocalizedMessage());
|
358
|
+
} catch (InterruptedException e) {
|
359
|
+
throw ruby.newThreadError(e.getLocalizedMessage());
|
214
360
|
}
|
215
361
|
}
|
216
362
|
|
@@ -248,6 +394,7 @@ public class Memcached extends RubyObject {
|
|
248
394
|
String distributionValue = "ketama";
|
249
395
|
String hashValue = "fnv1_32";
|
250
396
|
RubyBoolean binaryValue = ruby.getFalse();
|
397
|
+
RubyBoolean shouldOptimize = ruby.getFalse();
|
251
398
|
String transcoderValue = null;
|
252
399
|
if (!options.isEmpty()) {
|
253
400
|
RubyHash opts = options.convertToHash();
|
@@ -260,9 +407,18 @@ public class Memcached extends RubyObject {
|
|
260
407
|
if (opts.containsKey(ruby.newSymbol("binary_protocol"))) {
|
261
408
|
binaryValue = (RubyBoolean) opts.get(ruby.newSymbol("binary_protocol"));
|
262
409
|
}
|
410
|
+
if (opts.containsKey(ruby.newSymbol("should_optimize"))) {
|
411
|
+
shouldOptimize = (RubyBoolean) opts.get(ruby.newSymbol("should_optimize"));
|
412
|
+
}
|
263
413
|
if (opts.containsKey(ruby.newSymbol("default_ttl"))) {
|
264
414
|
ttl = Integer.parseInt(opts.get(ruby.newSymbol("default_ttl")).toString());
|
265
415
|
}
|
416
|
+
if (opts.containsKey(ruby.newSymbol("timeout"))) {
|
417
|
+
timeout = Integer.parseInt(opts.get(ruby.newSymbol("timeout")).toString());
|
418
|
+
}
|
419
|
+
if (opts.containsKey(ruby.newSymbol("exception_retry_limit"))) {
|
420
|
+
exceptionRetryLimit = Integer.parseInt(opts.get(ruby.newSymbol("exception_retry_limit")).toString());
|
421
|
+
}
|
266
422
|
if (opts.containsKey(ruby.newSymbol("namespace"))) {
|
267
423
|
prefixKey = opts.get(ruby.newSymbol("namespace")).toString();
|
268
424
|
}
|
@@ -302,15 +458,22 @@ public class Memcached extends RubyObject {
|
|
302
458
|
if (ruby.getTrue() == binaryValue) {
|
303
459
|
builder.setProtocol(Protocol.BINARY);
|
304
460
|
}
|
461
|
+
if (ruby.getTrue() == shouldOptimize) {
|
462
|
+
builder.setShouldOptimize(true);
|
463
|
+
}
|
305
464
|
|
465
|
+
if (timeout != -1) {
|
466
|
+
builder.setOpTimeout(timeout);
|
467
|
+
}
|
306
468
|
builder.setDaemon(true);
|
307
|
-
client = new MemcachedClient(builder.build(), addresses);
|
308
|
-
|
309
469
|
if ("marshal_zlib".equals(transcoderValue)) {
|
310
470
|
transcoder = new MarshalZlibTranscoder(ruby);
|
311
471
|
} else {
|
312
472
|
transcoder = new MarshalTranscoder(ruby);
|
313
473
|
}
|
474
|
+
builder.setTranscoder(transcoder);
|
475
|
+
|
476
|
+
client = new MemcachedClient(builder.build(), addresses);
|
314
477
|
} catch (IOException ioe) {
|
315
478
|
throw ruby.newIOErrorFromException(ioe);
|
316
479
|
}
|
@@ -318,7 +481,7 @@ public class Memcached extends RubyObject {
|
|
318
481
|
return context.nil;
|
319
482
|
}
|
320
483
|
|
321
|
-
private int
|
484
|
+
private int getExpiry(IRubyObject[] args) {
|
322
485
|
if (args.length > 2) {
|
323
486
|
return (int) args[2].convertToInteger().getLongValue();
|
324
487
|
}
|
@@ -29,6 +29,7 @@ public class MemcachedService implements BasicLibraryService {
|
|
29
29
|
memcached.defineClassUnder("NotFound", memcachedError, memcachedError.getAllocator());
|
30
30
|
memcached.defineClassUnder("NotStored", memcachedError, memcachedError.getAllocator());
|
31
31
|
memcached.defineClassUnder("NotSupport", memcachedError, memcachedError.getAllocator());
|
32
|
+
memcached.defineClassUnder("ATimeoutOccurred", memcachedError, memcachedError.getAllocator());
|
32
33
|
return true;
|
33
34
|
}
|
34
35
|
}
|
@@ -20,6 +20,10 @@ public class Error {
|
|
20
20
|
return newMemcachedError(ruby, "NotSupport", message);
|
21
21
|
}
|
22
22
|
|
23
|
+
public static RaiseException newATimeoutOccurred(Ruby ruby, String message) {
|
24
|
+
return newMemcachedError(ruby, "ATimeoutOccurred", message);
|
25
|
+
}
|
26
|
+
|
23
27
|
private static RaiseException newMemcachedError(Ruby ruby, String klass, String message) {
|
24
28
|
RubyClass errorClass = ruby.getModule("Memcached").getClass(klass);
|
25
29
|
return new RaiseException(RubyException.newException(ruby, errorClass, message), true);
|
@@ -3,6 +3,7 @@ package com.openfeint.memcached.transcoder;
|
|
3
3
|
import net.spy.memcached.CachedData;
|
4
4
|
import net.spy.memcached.transcoders.Transcoder;
|
5
5
|
import org.jruby.Ruby;
|
6
|
+
import org.jruby.exceptions.RaiseException;
|
6
7
|
import org.jruby.runtime.builtin.IRubyObject;
|
7
8
|
import org.jruby.runtime.marshal.MarshalStream;
|
8
9
|
import org.jruby.runtime.marshal.UnmarshalStream;
|
@@ -16,8 +17,8 @@ import java.io.IOException;
|
|
16
17
|
* MarshalTranscoder does marshaling and unmarshaling.
|
17
18
|
*
|
18
19
|
*/
|
19
|
-
public class MarshalTranscoder implements Transcoder
|
20
|
-
|
20
|
+
public class MarshalTranscoder implements Transcoder {
|
21
|
+
protected Ruby ruby;
|
21
22
|
private int flags;
|
22
23
|
|
23
24
|
public MarshalTranscoder(Ruby ruby) {
|
@@ -33,23 +34,29 @@ public class MarshalTranscoder implements Transcoder<IRubyObject> {
|
|
33
34
|
return false;
|
34
35
|
}
|
35
36
|
|
36
|
-
public CachedData encode(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
public CachedData encode(Object o) {
|
38
|
+
if (o instanceof IRubyObject) {
|
39
|
+
try {
|
40
|
+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
41
|
+
MarshalStream marshal = new MarshalStream(ruby, baos, Integer.MAX_VALUE);
|
42
|
+
marshal.dumpObject((IRubyObject) o);
|
43
|
+
byte[] bytes = baos.toByteArray();
|
44
|
+
return new CachedData(getFlags(), bytes, bytes.length);
|
45
|
+
} catch (IOException e) {
|
46
|
+
throw ruby.newIOErrorFromException(e);
|
47
|
+
}
|
48
|
+
} else {
|
49
|
+
return encodeNumber(o);
|
45
50
|
}
|
46
51
|
}
|
47
52
|
|
48
|
-
public
|
53
|
+
public Object decode(CachedData d) {
|
49
54
|
try {
|
50
55
|
return new UnmarshalStream(ruby, new ByteArrayInputStream(d.getData()), null, false, false).unmarshalObject();
|
51
|
-
} catch (
|
52
|
-
|
56
|
+
} catch (RaiseException e) {
|
57
|
+
return decodeNumber(d, e);
|
58
|
+
} catch (IOException e) {
|
59
|
+
return decodeNumber(d, e);
|
53
60
|
}
|
54
61
|
}
|
55
62
|
|
@@ -60,4 +67,25 @@ public class MarshalTranscoder implements Transcoder<IRubyObject> {
|
|
60
67
|
public int getFlags() {
|
61
68
|
return flags;
|
62
69
|
}
|
70
|
+
|
71
|
+
protected CachedData encodeNumber(Object o) {
|
72
|
+
byte[] bytes = o.toString().getBytes();
|
73
|
+
return new CachedData(getFlags(), bytes, bytes.length);
|
74
|
+
}
|
75
|
+
|
76
|
+
protected Long decodeNumber(CachedData d, RaiseException originalException) {
|
77
|
+
try {
|
78
|
+
return Long.valueOf(new String(d.getData()).trim());
|
79
|
+
} catch (NumberFormatException e) {
|
80
|
+
throw ruby.newRuntimeError(originalException.getLocalizedMessage());
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
protected Long decodeNumber(CachedData d, IOException originalException) {
|
85
|
+
try {
|
86
|
+
return Long.valueOf(new String(d.getData()).trim());
|
87
|
+
} catch (NumberFormatException e) {
|
88
|
+
throw ruby.newIOErrorFromException(originalException);
|
89
|
+
}
|
90
|
+
}
|
63
91
|
}
|
@@ -6,6 +6,7 @@ import com.jcraft.jzlib.JZlib;
|
|
6
6
|
import net.spy.memcached.CachedData;
|
7
7
|
import net.spy.memcached.transcoders.Transcoder;
|
8
8
|
import org.jruby.Ruby;
|
9
|
+
import org.jruby.exceptions.RaiseException;
|
9
10
|
import org.jruby.runtime.builtin.IRubyObject;
|
10
11
|
import org.jruby.runtime.marshal.MarshalStream;
|
11
12
|
import org.jruby.runtime.marshal.UnmarshalStream;
|
@@ -22,77 +23,69 @@ import java.io.IOException;
|
|
22
23
|
* MarshalZlibTranscoder do marshaling/unmarshaling and compressing/decompressing with zlib.
|
23
24
|
*
|
24
25
|
*/
|
25
|
-
public class MarshalZlibTranscoder
|
26
|
-
|
27
|
-
private int flags;
|
26
|
+
public class MarshalZlibTranscoder extends MarshalTranscoder {
|
27
|
+
static final int COMPRESS_FLAG = 1;
|
28
28
|
|
29
29
|
public MarshalZlibTranscoder(Ruby ruby) {
|
30
|
-
|
30
|
+
super(ruby, COMPRESS_FLAG);
|
31
31
|
}
|
32
32
|
|
33
33
|
public MarshalZlibTranscoder(Ruby ruby, int flags) {
|
34
|
-
|
35
|
-
this.flags = flags;
|
34
|
+
super(ruby, flags);
|
36
35
|
}
|
37
36
|
|
38
|
-
public
|
39
|
-
|
40
|
-
|
37
|
+
public CachedData encode(Object o) {
|
38
|
+
if (o instanceof IRubyObject) {
|
39
|
+
try {
|
40
|
+
ByteArrayOutputStream out1 = new ByteArrayOutputStream();
|
41
|
+
MarshalStream marshal = new MarshalStream(ruby, out1, Integer.MAX_VALUE);
|
42
|
+
marshal.dumpObject((IRubyObject) o);
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
byte[] bytes;
|
45
|
+
if (getFlags() == COMPRESS_FLAG) {
|
46
|
+
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
|
47
|
+
ZOutputStream zout = new ZOutputStream(out2, JZlib.Z_DEFAULT_COMPRESSION);
|
48
|
+
zout.write(out1.toByteArray());
|
49
|
+
zout.close();
|
50
|
+
bytes = out2.toByteArray();
|
51
|
+
} else {
|
52
|
+
bytes = out1.toByteArray();
|
53
|
+
}
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
ZOutputStream zout = new ZOutputStream(out2, JZlib.Z_DEFAULT_COMPRESSION);
|
52
|
-
zout.write(out1.toByteArray());
|
53
|
-
zout.close();
|
54
|
-
bytes = out2.toByteArray();
|
55
|
-
} else {
|
56
|
-
bytes = out1.toByteArray();
|
55
|
+
return new CachedData(super.getFlags(), bytes, bytes.length);
|
56
|
+
} catch (IOException e) {
|
57
|
+
throw ruby.newIOErrorFromException(e);
|
57
58
|
}
|
58
|
-
|
59
|
-
return
|
60
|
-
} catch (IOException ioe) {
|
61
|
-
throw ruby.newIOErrorFromException(ioe);
|
59
|
+
} else {
|
60
|
+
return super.encodeNumber(o);
|
62
61
|
}
|
63
62
|
}
|
64
63
|
|
65
|
-
public
|
64
|
+
public Object decode(CachedData d) {
|
66
65
|
try {
|
67
66
|
byte[] bytes;
|
68
|
-
if (d.getFlags() ==
|
69
|
-
|
70
|
-
|
67
|
+
if (d.getFlags() == COMPRESS_FLAG) {
|
68
|
+
ByteArrayInputStream in = new ByteArrayInputStream(d.getData());
|
69
|
+
ZInputStream zin = new ZInputStream(in);
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
71
|
+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
72
|
+
int nRead;
|
73
|
+
byte[] data = new byte[1024];
|
74
|
+
while ((nRead = zin.read(data, 0, data.length)) != -1) {
|
75
|
+
buffer.write(data, 0, nRead);
|
76
|
+
}
|
77
|
+
buffer.flush();
|
78
|
+
bytes = buffer.toByteArray();
|
79
|
+
zin.close();
|
81
80
|
} else {
|
82
|
-
|
81
|
+
bytes = d.getData();
|
83
82
|
}
|
84
83
|
|
85
84
|
return new UnmarshalStream(ruby, new ByteArrayInputStream(bytes), null, false, false).unmarshalObject();
|
86
|
-
} catch (
|
87
|
-
|
85
|
+
} catch (RaiseException e) {
|
86
|
+
return super.decodeNumber(d, e);
|
87
|
+
} catch (IOException e) {
|
88
|
+
return super.decodeNumber(d, e);
|
88
89
|
}
|
89
90
|
}
|
90
|
-
|
91
|
-
public int getMaxSize() {
|
92
|
-
return CachedData.MAX_SIZE;
|
93
|
-
}
|
94
|
-
|
95
|
-
public int getFlags() {
|
96
|
-
return flags;
|
97
|
-
}
|
98
91
|
}
|
Binary file
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jruby-memcached
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Richard Huang
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-08-
|
13
|
+
date: 2012-08-22 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -63,6 +63,7 @@ files:
|
|
63
63
|
- src/main/java/com/openfeint/memcached/Memcached.java
|
64
64
|
- src/main/java/com/openfeint/memcached/MemcachedService.java
|
65
65
|
- src/main/java/com/openfeint/memcached/Rails.java
|
66
|
+
- src/main/java/com/openfeint/memcached/error/ATimeoutOccurred.java
|
66
67
|
- src/main/java/com/openfeint/memcached/error/Error.java
|
67
68
|
- src/main/java/com/openfeint/memcached/error/NotFound.java
|
68
69
|
- src/main/java/com/openfeint/memcached/error/NotStored.java
|