jruby-memcached 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ target/classes
21
+ target/maven-archiver
22
+ target/surefire
23
+ target/dependency-reduced-pom.xml
24
+ target/original-spymemcached-ext-0.0.1.jar
25
+ target/original-xmemcached-ext-0.0.1.jar
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jruby-memcached.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jruby-memcached (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.3)
10
+ metaclass (0.0.1)
11
+ mocha (0.12.1)
12
+ metaclass (~> 0.0.1)
13
+ rspec (2.11.0)
14
+ rspec-core (~> 2.11.0)
15
+ rspec-expectations (~> 2.11.0)
16
+ rspec-mocks (~> 2.11.0)
17
+ rspec-core (2.11.1)
18
+ rspec-expectations (2.11.1)
19
+ diff-lcs (~> 1.1.3)
20
+ rspec-mocks (2.11.1)
21
+
22
+ PLATFORMS
23
+ java
24
+
25
+ DEPENDENCIES
26
+ jruby-memcached!
27
+ mocha
28
+ rspec
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ jruby-memcached
2
+ ===============
3
+
4
+ we used memcached gem before, but now we are migrating to jruby, but
5
+ memcached didn't support jruby platform, and we can't find any
6
+ ruby/jruby gem that compatible with memcached, have to write it by
7
+ ourselves.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/benchmark.rb ADDED
@@ -0,0 +1,93 @@
1
+ require 'benchmark'
2
+
3
+ JRUBY = defined?(JRUBY_VERSION)
4
+
5
+ if JRUBY
6
+ require 'lib/memcached'
7
+ else
8
+ require 'memcached'
9
+ end
10
+ require 'rubygems'
11
+ require 'dalli'
12
+
13
+ memcached = Memcached.new(['localhost:11211'])
14
+ dalli = Dalli::Client.new(['localhost:11211'])
15
+
16
+ 3.to_i.times {
17
+ Benchmark.bm(30) {|bm|
18
+ if JRUBY
19
+ bm.report("jruby-memcached set") {
20
+ 100_000.times { memcached.set('foo', 'bar').get }
21
+ }
22
+ bm.report("jruby-memcached get") {
23
+ 100_000.times { memcached.get('foo') }
24
+ }
25
+ else
26
+ bm.report("memcached set") {
27
+ 100_000.times { memcached.set('foo', 'bar') }
28
+ }
29
+ bm.report("memcached get") {
30
+ 100_000.times { memcached.get('foo') }
31
+ }
32
+ end
33
+ }
34
+ }
35
+
36
+ 3.times {
37
+ Benchmark.bm(30) {|bm|
38
+ bm.report("dalli set") {
39
+ 100_000.times { dalli.set('foo', 'bar') }
40
+ }
41
+ bm.report("dalli get") {
42
+ 100_000.times { dalli.get('foo') }
43
+ }
44
+ }
45
+ }
46
+
47
+ memcached.close
48
+ dalli.close
49
+
50
+
51
+ # MBP 2.8G i7
52
+ #
53
+ # ruby-1.9.3-p194
54
+ # ruby benchmark.rb
55
+ # user system total real
56
+ # memcached set 1.110000 1.010000 2.120000 ( 4.578121)
57
+ # memcached get 0.940000 0.960000 1.900000 ( 4.013941)
58
+ # user system total real
59
+ # memcached set 1.100000 1.010000 2.110000 ( 4.557462)
60
+ # memcached get 0.930000 0.950000 1.880000 ( 3.995192)
61
+ # user system total real
62
+ # memcached set 1.110000 1.020000 2.130000 ( 4.592509)
63
+ # memcached get 0.970000 1.000000 1.970000 ( 4.172170)
64
+ # user system total real
65
+ # dalli set 8.330000 1.570000 9.900000 ( 10.062159)
66
+ # dalli get 8.240000 1.630000 9.870000 ( 9.987921)
67
+ # user system total real
68
+ # dalli set 8.400000 1.580000 9.980000 ( 10.139169)
69
+ # dalli get 8.500000 1.680000 10.180000 ( 10.287153)
70
+ # user system total real
71
+ # dalli set 8.330000 1.560000 9.890000 ( 10.094499)
72
+ # dalli get 8.530000 1.680000 10.210000 ( 10.331083)
73
+ #
74
+ # jruby-1.6.7.2
75
+ # jruby --server -Ilib -S benchmark.rb
76
+ # user system total real
77
+ # jruby-memcached set 8.745000 0.000000 8.745000 ( 8.745000)
78
+ # jruby-memcached get 8.260000 0.000000 8.260000 ( 8.260000)
79
+ # user system total real
80
+ # jruby-memcached set 6.911000 0.000000 6.911000 ( 6.911000)
81
+ # jruby-memcached get 6.895000 0.000000 6.895000 ( 6.895000)
82
+ # user system total real
83
+ # jruby-memcached set 6.902000 0.000000 6.902000 ( 6.902000)
84
+ # jruby-memcached get 6.845000 0.000000 6.845000 ( 6.845000)
85
+ # user system total real
86
+ # dalli set 15.233000 0.000000 15.233000 ( 15.234000)
87
+ # dalli get 13.991000 0.000000 13.991000 ( 13.992000)
88
+ # user system total real
89
+ # dalli set 12.936000 0.000000 12.936000 ( 12.936000)
90
+ # dalli get 13.585000 0.000000 13.585000 ( 13.585000)
91
+ # user system total real
92
+ # dalli set 13.251000 0.000000 13.251000 ( 13.251000)
93
+ # dalli get 13.536000 0.000000 13.536000 ( 13.536000)
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "memcached/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "jruby-memcached"
7
+ s.version = Memcached::VERSION
8
+ s.authors = ["Richard Huang"]
9
+ s.email = ["flyerhzm@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{jruby compatible memcached client}
12
+ s.description = %q{jruby memcacached client which is compatible with memcached gem}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_development_dependency 'rspec'
20
+ s.add_development_dependency 'mocha'
21
+ end
@@ -0,0 +1,3 @@
1
+ class Memcached::Error < RuntimeError; end
2
+ class Memcached::NotFound < Memcached::Error; end
3
+ class Memcached::NotStored < Memcached::Error; end
@@ -0,0 +1,3 @@
1
+ class Memcached
2
+ VERSION = "0.1.0"
3
+ end
data/lib/memcached.rb ADDED
@@ -0,0 +1,114 @@
1
+ require 'java'
2
+ require 'memcached/version'
3
+ require 'memcached/exceptions'
4
+ require File.join(File.dirname(__FILE__), '../target/spymemcached-ext-0.0.1.jar')
5
+
6
+ class Memcached
7
+ include_class 'java.net.InetSocketAddress'
8
+ include_class 'net.spy.memcached.MemcachedClient'
9
+ include_class 'net.spy.memcached.ConnectionFactoryBuilder'
10
+ include_class 'net.spy.memcached.ConnectionFactoryBuilder$Locator'
11
+ include_class 'net.spy.memcached.DefaultHashAlgorithm'
12
+ include_class 'net.spy.memcached.FailureMode'
13
+ include_class 'net.spy.memcached.transcoders.SimpleTranscoder'
14
+ include_class 'net.spy.memcached.AddrUtil'
15
+
16
+ FLAGS = 0x0
17
+ DEFAULTS = {
18
+ :default_ttl => 604800,
19
+ :exception_retry_limit => 5
20
+ }
21
+
22
+ attr_reader :options
23
+ attr_reader :default_ttl
24
+
25
+ def initialize(addresses, options={})
26
+ builder = ConnectionFactoryBuilder.new.
27
+ setLocatorType(Locator::CONSISTENT).
28
+ setHashAlg(DefaultHashAlgorithm::FNV1_32_HASH)
29
+ @client = MemcachedClient.new builder.build, AddrUtil.getAddresses(Array(addresses).join(' '))
30
+
31
+ @options = DEFAULTS.merge(options)
32
+ @default_ttl = @options[:default_ttl]
33
+
34
+ @simple_transcoder = SimpleTranscoder.new
35
+ end
36
+
37
+ def set(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
38
+ with_retry do
39
+ value = encode(value, marshal, flags)
40
+ @simple_transcoder.setFlags(flags)
41
+ @client.set(key, ttl, value.to_java_bytes, @simple_transcoder)
42
+ end
43
+ end
44
+
45
+ def add(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
46
+ with_retry do
47
+ value = encode(value, marshal, flags)
48
+ @simple_transcoder.setFlags(flags)
49
+ if @client.add(key, ttl, value.to_java_bytes, @simple_transcoder).get === false
50
+ raise Memcached::NotStored
51
+ end
52
+ end
53
+ end
54
+
55
+ def replace(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
56
+ with_retry do
57
+ value = encode(value, marshal, flags)
58
+ @simple_transcoder.setFlags(flags)
59
+ if @client.replace(key, ttl, value.to_java_bytes, @simple_transcoder).get === false
60
+ raise Memcached::NotStored
61
+ end
62
+ end
63
+ end
64
+
65
+ def delete(key)
66
+ with_retry do
67
+ raise Memcached::NotFound if @client.delete(key).get === false
68
+ end
69
+ end
70
+
71
+ def get(key, marshal=true)
72
+ with_retry do
73
+ ret = @client.get(key, @simple_transcoder)
74
+ raise Memcached::NotFound if ret.nil?
75
+ flags, data = ret.flags, ret.data
76
+ value = String.from_java_bytes data
77
+ value = decode(value, marshal, flags)
78
+ end
79
+ end
80
+
81
+ def flush
82
+ @client.flush.get
83
+ end
84
+
85
+ def servers
86
+ @client.available_servers.map { |address| address.to_s.split("/").last }
87
+ end
88
+
89
+ def close
90
+ @client.shutdown
91
+ end
92
+
93
+ alias_method :quit, :close
94
+
95
+ private
96
+ def with_retry
97
+ begin
98
+ yield
99
+ rescue
100
+ tries ||= 0
101
+ raise unless tries < options[:exception_retry_limit]
102
+ tries += 1
103
+ retry
104
+ end
105
+ end
106
+
107
+ def encode(value, marshal, flags)
108
+ marshal ? Marshal.dump(value) : value
109
+ end
110
+
111
+ def decode(value, marshal, flags)
112
+ marshal ? Marshal.load(value) : value
113
+ end
114
+ end
data/pom.xml ADDED
@@ -0,0 +1,66 @@
1
+ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3
+ <modelVersion>4.0.0</modelVersion>
4
+
5
+ <groupId>net.spy</groupId>
6
+ <artifactId>spymemcached-ext</artifactId>
7
+ <version>0.0.1</version>
8
+ <packaging>jar</packaging>
9
+
10
+ <name>spymemcached-ext</name>
11
+ <url>http://maven.apache.org</url>
12
+
13
+ <properties>
14
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15
+ </properties>
16
+
17
+ <repositories>
18
+ <repository>
19
+ <id>spymemcached</id>
20
+ <name>spymemcached extention</name>
21
+ <layout>default</layout>
22
+ <url>http://files.couchbase.com/maven2/</url>
23
+ <snapshots>
24
+ <enabled>false</enabled>
25
+ </snapshots>
26
+ </repository>
27
+ </repositories>
28
+
29
+ <dependencies>
30
+ <dependency>
31
+ <groupId>org.jruby</groupId>
32
+ <artifactId>jruby</artifactId>
33
+ <version>1.6.7.2</version>
34
+ </dependency>
35
+ <dependency>
36
+ <groupId>spy</groupId>
37
+ <artifactId>spymemcached</artifactId>
38
+ <version>2.8.1</version>
39
+ </dependency>
40
+ </dependencies>
41
+
42
+ <build>
43
+ <plugins>
44
+ <plugin>
45
+ <groupId>org.apache.maven.plugins</groupId>
46
+ <artifactId>maven-shade-plugin</artifactId>
47
+ <version>1.4</version>
48
+ <executions>
49
+ <execution>
50
+ <phase>package</phase>
51
+ <goals>
52
+ <goal>shade</goal>
53
+ </goals>
54
+ <configuration>
55
+ <artifactSet>
56
+ <excludes>
57
+ <exclude>org.jruby:jruby</exclude>
58
+ </excludes>
59
+ </artifactSet>
60
+ </configuration>
61
+ </execution>
62
+ </executions>
63
+ </plugin>
64
+ </plugins>
65
+ </build>
66
+ </project>
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ describe Memcached do
4
+ context "localhost" do
5
+ before :all do
6
+ @memcached = Memcached.new("127.0.0.1:11211")
7
+ end
8
+
9
+ after :all do
10
+ @memcached.close
11
+ end
12
+
13
+ it "should initialize with options" do
14
+ @memcached.servers.should == ["127.0.0.1:11211"]
15
+ end
16
+
17
+ context "set/get" do
18
+ it "should set/get with plain text" do
19
+ @memcached.set "key", "value"
20
+ @memcached.get("key").should == "value"
21
+ end
22
+
23
+ it "should set/get with compressed text" do
24
+ @memcached.set "key", "x\234c?P?*?/?I\001\000\b8\002a"
25
+ @memcached.get("key").should == "x\234c?P?*?/?I\001\000\b8\002a"
26
+ end
27
+ end
28
+
29
+ context "get" do
30
+ it "should get nil" do
31
+ @memcached.set "key", nil, 0
32
+ @memcached.get("key").should be_nil
33
+ end
34
+
35
+ it "should get missing" do
36
+ @memcached.delete "key" rescue nil
37
+ lambda { @memcached.get "key" }.should raise_error(Memcached::NotFound)
38
+ end
39
+ end
40
+
41
+ context "set" do
42
+ it "should set expiry" do
43
+ @memcached.set "key", "value", 1
44
+ @memcached.get("key").should == "value"
45
+ sleep 1
46
+ lambda { @memcached.get("key") }.should raise_error(Memcached::NotFound)
47
+ end
48
+
49
+ it "should retry when set failure" do
50
+ Java::NetSpyMemcached::MemcachedClient.any_instance.stubs(:set).raises(Memcached::NotStored)
51
+ Java::NetSpyMemcachedTranscoders::SimpleTranscoder.any_instance.expects(:setFlags).times(6)
52
+ lambda { @memcached.set "key", "value" }.should raise_error(Memcached::NotStored)
53
+ end
54
+ end
55
+
56
+ context "add" do
57
+ it "should add new key" do
58
+ @memcached.delete "key" rescue nil
59
+ @memcached.add "key", "value"
60
+ @memcached.get("key").should == "value"
61
+ end
62
+
63
+ it "should not add existing key" do
64
+ @memcached.set "key", "value"
65
+ lambda { @memcached.add "key", "value" }.should raise_error(Memcached::NotStored)
66
+ end
67
+
68
+ it "should add expiry" do
69
+ @memcached.delete "key" rescue nil
70
+ @memcached.add "key", "value", 1
71
+ @memcached.get "key"
72
+ sleep 1
73
+ lambda { @memcached.get "key" }.should raise_error(Memcached::NotFound)
74
+ end
75
+ end
76
+
77
+ context "replace" do
78
+ it "should replace existing key" do
79
+ @memcached.set "key", nil
80
+ @memcached.replace "key", "value"
81
+ @memcached.get("key").should == "value"
82
+ end
83
+
84
+ it "should not replace with new key" do
85
+ @memcached.delete "key" rescue nil
86
+ lambda { @memcached.replace "key", "value" }.should raise_error(Memcached::NotStored)
87
+ lambda { @memcached.get "key" }.should raise_error(Memcached::NotFound)
88
+ end
89
+ end
90
+
91
+ context "delete" do
92
+ it "should delete with existing key" do
93
+ @memcached.set "key", "value"
94
+ @memcached.delete "key"
95
+ lambda { @memcached.get "key" }.should raise_error(Memcached::NotFound)
96
+ end
97
+
98
+ it "should not delete with new key" do
99
+ @memcached.delete "key" rescue nil
100
+ lambda { @memcached.delete "key" }.should raise_error(Memcached::NotFound)
101
+ end
102
+ end
103
+
104
+ context "flush" do
105
+ it "should flush all keys" do
106
+ @memcached.set "key1", "value2"
107
+ @memcached.set "key2", "value2"
108
+ @memcached.flush
109
+ lambda { @memcached.get "key1" }.should raise_error(Memcached::NotFound)
110
+ lambda { @memcached.get "key2" }.should raise_error(Memcached::NotFound)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rspec'
4
+ require 'rspec/autorun'
5
+
6
+ require 'memcached'
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_framework = :mocha
10
+ config.filter_run :focus => true
11
+ config.run_all_when_everything_filtered = true
12
+ end
@@ -0,0 +1,165 @@
1
+ package net.spy.memcached;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.Collection;
5
+ import java.util.Iterator;
6
+ import java.util.List;
7
+ import java.util.Map;
8
+ import java.util.SortedMap;
9
+ import java.util.TreeMap;
10
+
11
+ import net.spy.memcached.compat.SpyObject;
12
+ import net.spy.memcached.util.DefaultKetamaNodeLocatorConfiguration;
13
+ import net.spy.memcached.util.KetamaNodeLocatorConfiguration;
14
+
15
+ /**
16
+ * This is grabbed from spymemcached.
17
+ *
18
+ * hashAlg should only be used to get server by key.
19
+ *
20
+ */
21
+ public final class KetamaNodeLocator extends SpyObject implements NodeLocator {
22
+
23
+ private volatile TreeMap<Long, MemcachedNode> ketamaNodes;
24
+ private final Collection<MemcachedNode> allNodes;
25
+
26
+ private final HashAlgorithm hashAlg;
27
+ private final KetamaNodeLocatorConfiguration config;
28
+
29
+ /**
30
+ * Create a new KetamaNodeLocator using specified nodes and the specifed hash
31
+ * algorithm.
32
+ *
33
+ * @param nodes The List of nodes to use in the Ketama consistent hash
34
+ * continuum
35
+ * @param alg The hash algorithm to use when choosing a node in the Ketama
36
+ * consistent hash continuum
37
+ */
38
+ public KetamaNodeLocator(List<MemcachedNode> nodes, HashAlgorithm alg) {
39
+ this(nodes, alg, new DefaultKetamaNodeLocatorConfiguration());
40
+ }
41
+
42
+ /**
43
+ * Create a new KetamaNodeLocator using specified nodes and the specifed hash
44
+ * algorithm and configuration.
45
+ *
46
+ * @param nodes The List of nodes to use in the Ketama consistent hash
47
+ * continuum
48
+ * @param alg The hash algorithm to use when choosing a node in the Ketama
49
+ * consistent hash continuum
50
+ * @param conf
51
+ */
52
+ public KetamaNodeLocator(List<MemcachedNode> nodes, HashAlgorithm alg,
53
+ KetamaNodeLocatorConfiguration conf) {
54
+ super();
55
+ allNodes = nodes;
56
+ hashAlg = alg;
57
+ config = conf;
58
+ setKetamaNodes(nodes);
59
+ }
60
+
61
+ private KetamaNodeLocator(TreeMap<Long, MemcachedNode> smn,
62
+ Collection<MemcachedNode> an, HashAlgorithm alg,
63
+ KetamaNodeLocatorConfiguration conf) {
64
+ super();
65
+ ketamaNodes = smn;
66
+ allNodes = an;
67
+ hashAlg = alg;
68
+ config = conf;
69
+ }
70
+
71
+ public Collection<MemcachedNode> getAll() {
72
+ return allNodes;
73
+ }
74
+
75
+ public MemcachedNode getPrimary(final String k) {
76
+ MemcachedNode rv = getNodeForKey(hashAlg.hash(k));
77
+ assert rv != null : "Found no node for key " + k;
78
+ return rv;
79
+ }
80
+
81
+ long getMaxKey() {
82
+ return getKetamaNodes().lastKey();
83
+ }
84
+
85
+ MemcachedNode getNodeForKey(long hash) {
86
+ final MemcachedNode rv;
87
+ if (!ketamaNodes.containsKey(hash)) {
88
+ // Java 1.6 adds a ceilingKey method, but I'm still stuck in 1.5
89
+ // in a lot of places, so I'm doing this myself.
90
+ SortedMap<Long, MemcachedNode> tailMap = getKetamaNodes().tailMap(hash);
91
+ if (tailMap.isEmpty()) {
92
+ hash = getKetamaNodes().firstKey();
93
+ } else {
94
+ hash = tailMap.firstKey();
95
+ }
96
+ }
97
+ rv = getKetamaNodes().get(hash);
98
+ return rv;
99
+ }
100
+
101
+ public Iterator<MemcachedNode> getSequence(String k) {
102
+ // Seven searches gives us a 1 in 2^7 chance of hitting the
103
+ // same dead node all of the time.
104
+ return new KetamaIterator(k, 7, getKetamaNodes(), hashAlg);
105
+ }
106
+
107
+ public NodeLocator getReadonlyCopy() {
108
+ TreeMap<Long, MemcachedNode> smn =
109
+ new TreeMap<Long, MemcachedNode>(getKetamaNodes());
110
+ Collection<MemcachedNode> an =
111
+ new ArrayList<MemcachedNode>(allNodes.size());
112
+
113
+ // Rewrite the values a copy of the map.
114
+ for (Map.Entry<Long, MemcachedNode> me : smn.entrySet()) {
115
+ me.setValue(new MemcachedNodeROImpl(me.getValue()));
116
+ }
117
+
118
+ // Copy the allNodes collection.
119
+ for (MemcachedNode n : allNodes) {
120
+ an.add(new MemcachedNodeROImpl(n));
121
+ }
122
+
123
+ return new KetamaNodeLocator(smn, an, hashAlg, config);
124
+ }
125
+
126
+ @Override
127
+ public void updateLocator(List<MemcachedNode> nodes) {
128
+ setKetamaNodes(nodes);
129
+ }
130
+
131
+ /**
132
+ * @return the ketamaNodes
133
+ */
134
+ protected TreeMap<Long, MemcachedNode> getKetamaNodes() {
135
+ return ketamaNodes;
136
+ }
137
+
138
+ /**
139
+ * Setup the KetamaNodeLocator with the list of nodes it should use.
140
+ *
141
+ * @param nodes a List of MemcachedNodes for this KetamaNodeLocator to use in
142
+ * its continuum
143
+ */
144
+ protected void setKetamaNodes(List<MemcachedNode> nodes) {
145
+ TreeMap<Long, MemcachedNode> newNodeMap =
146
+ new TreeMap<Long, MemcachedNode>();
147
+ int numReps = config.getNodeRepetitions();
148
+ for (MemcachedNode node : nodes) {
149
+ for (int i = 0; i < numReps / 4; i++) {
150
+ byte[] digest =
151
+ DefaultHashAlgorithm.computeMd5(config.getKeyForNode(node, i));
152
+ for (int h = 0; h < 4; h++) {
153
+ Long k = ((long) (digest[3 + h * 4] & 0xFF) << 24)
154
+ | ((long) (digest[2 + h * 4] & 0xFF) << 16)
155
+ | ((long) (digest[1 + h * 4] & 0xFF) << 8)
156
+ | (digest[h * 4] & 0xFF);
157
+ newNodeMap.put(k, node);
158
+ getLogger().debug("Adding node %s in position %d", node, k);
159
+ }
160
+ }
161
+ }
162
+ assert newNodeMap.size() == numReps * nodes.size();
163
+ ketamaNodes = newNodeMap;
164
+ }
165
+ }
@@ -0,0 +1,20 @@
1
+ package net.spy.memcached;
2
+
3
+ public class ReturnData {
4
+ private int flags;
5
+ private byte[] data;
6
+
7
+ public ReturnData(int flags, byte[] data) {
8
+ this.flags = flags;
9
+ this.data = data;
10
+ }
11
+
12
+
13
+ public int getFlags() {
14
+ return flags;
15
+ }
16
+
17
+ public byte[] getData() {
18
+ return data;
19
+ }
20
+ }
@@ -0,0 +1,56 @@
1
+ package net.spy.memcached.transcoders;
2
+
3
+ import net.spy.memcached.CachedData;
4
+ import net.spy.memcached.ReturnData;
5
+ import net.spy.memcached.transcoders.Transcoder;
6
+
7
+ /**
8
+ *
9
+ * SimpleTranscoder didn't do any serializing/deserializing or compression/decompression.
10
+ * Ruby will convert object to string by Marshal and finally passing bytes[].
11
+ *
12
+ */
13
+ public class SimpleTranscoder implements Transcoder<Object> {
14
+ private final int maxSize;
15
+ private int flags;
16
+
17
+ public SimpleTranscoder() {
18
+ this(CachedData.MAX_SIZE, 0);
19
+ }
20
+
21
+ public SimpleTranscoder(int flags) {
22
+ this(CachedData.MAX_SIZE, flags);
23
+ }
24
+
25
+ public SimpleTranscoder(int maxSize, int flags) {
26
+ this.maxSize = maxSize;
27
+ this.flags = flags;
28
+ }
29
+
30
+ public boolean asyncDecode(CachedData d) {
31
+ return false;
32
+ }
33
+
34
+ public CachedData encode(Object o) {
35
+ byte[] b = (byte[]) o;
36
+ return new CachedData(getFlags(), b, getMaxSize());
37
+ }
38
+
39
+ public Object decode(CachedData d) {
40
+ byte[] data = d.getData();
41
+ int flags = d.getFlags();
42
+ return new ReturnData(flags, data);
43
+ }
44
+
45
+ public int getMaxSize() {
46
+ return maxSize;
47
+ }
48
+
49
+ public int getFlags() {
50
+ return flags;
51
+ }
52
+
53
+ public void setFlags(int flags) {
54
+ this.flags = flags;
55
+ }
56
+ }
@@ -0,0 +1,104 @@
1
+ package net.spy.memcached.util;
2
+
3
+ import java.net.InetSocketAddress;
4
+ import java.util.HashMap;
5
+ import java.util.Map;
6
+
7
+ import net.spy.memcached.MemcachedNode;
8
+
9
+ /**
10
+ * This is grabbed from spymemcached.
11
+ *
12
+ * socket address in libmemcached 0.32 is
13
+ * 127.0.0.1-1 (port is 11211) or
14
+ * 127.0.0.1:43043-1 (port is not 11211)
15
+ */
16
+ public class DefaultKetamaNodeLocatorConfiguration implements
17
+ KetamaNodeLocatorConfiguration {
18
+
19
+ private final int numReps = 160;
20
+ static final int DEFAULT_PORT = 11211;
21
+
22
+ // Internal lookup map to try to carry forward the optimisation that was
23
+ // previously in KetamaNodeLocator
24
+ protected Map<MemcachedNode, String> socketAddresses =
25
+ new HashMap<MemcachedNode, String>();
26
+
27
+ /**
28
+ * Returns the socket address of a given MemcachedNode.
29
+ *
30
+ * @param node The node which we're interested in
31
+ * @return String the socket address of that node.
32
+ */
33
+ protected String getSocketAddressForNode(MemcachedNode node) {
34
+ // Using the internal map retrieve the socket addresses
35
+ // for given nodes.
36
+ // I'm aware that this code is inherently thread-unsafe as
37
+ // I'm using a HashMap implementation of the map, but the worst
38
+ // case ( I believe) is we're slightly in-efficient when
39
+ // a node has never been seen before concurrently on two different
40
+ // threads, so it the socketaddress will be requested multiple times!
41
+ // all other cases should be as fast as possible.
42
+ String result = socketAddresses.get(node);
43
+ if (result == null) {
44
+ InetSocketAddress socketAddress = (InetSocketAddress) node.getSocketAddress();
45
+ if (socketAddress.getPort() == DEFAULT_PORT) {
46
+ result = socketAddress.getAddress().getHostAddress();
47
+ } else {
48
+ result = socketAddress.getAddress().getHostAddress() + ":" + socketAddress.getPort();
49
+ }
50
+ socketAddresses.put(node, result);
51
+ }
52
+ return result;
53
+ }
54
+
55
+ /**
56
+ * Returns the number of discrete hashes that should be defined for each node
57
+ * in the continuum.
58
+ *
59
+ * @return NUM_REPS repetitions.
60
+ */
61
+ public int getNodeRepetitions() {
62
+ return numReps;
63
+ }
64
+
65
+ /**
66
+ * Returns a uniquely identifying key, suitable for hashing by the
67
+ * KetamaNodeLocator algorithm.
68
+ *
69
+ * <p>
70
+ * This default implementation uses the socket-address of the MemcachedNode
71
+ * and concatenates it with a hyphen directly against the repetition number
72
+ * for example a key for a particular server's first repetition may look like:
73
+ * <p>
74
+ *
75
+ * <p>
76
+ * <code>myhost/10.0.2.1-0</code>
77
+ * </p>
78
+ *
79
+ * <p>
80
+ * for the second repetition
81
+ * </p>
82
+ *
83
+ * <p>
84
+ * <code>myhost/10.0.2.1-1</code>
85
+ * </p>
86
+ *
87
+ * <p>
88
+ * for a server where reverse lookups are failing the returned keys may look
89
+ * like
90
+ * </p>
91
+ *
92
+ * <p>
93
+ * <code>/10.0.2.1-0</code> and <code>/10.0.2.1-1</code>
94
+ * </p>
95
+ *
96
+ * @param node The MemcachedNode to use to form the unique identifier
97
+ * @param repetition The repetition number for the particular node in question
98
+ * (0 is the first repetition)
99
+ * @return The key that represents the specific repetition of the node
100
+ */
101
+ public String getKeyForNode(MemcachedNode node, int repetition) {
102
+ return getSocketAddressForNode(node) + "-" + repetition;
103
+ }
104
+ }
Binary file
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jruby-memcached
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Richard Huang
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-07-24 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :development
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: mocha
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :development
36
+ version_requirements: *id002
37
+ description: jruby memcacached client which is compatible with memcached gem
38
+ email:
39
+ - flyerhzm@gmail.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - .gitignore
48
+ - Gemfile
49
+ - Gemfile.lock
50
+ - README.md
51
+ - Rakefile
52
+ - benchmark.rb
53
+ - jruby_memcached.gemspec
54
+ - lib/memcached.rb
55
+ - lib/memcached/exceptions.rb
56
+ - lib/memcached/version.rb
57
+ - pom.xml
58
+ - spec/memcached_spec.rb
59
+ - spec/spec_helper.rb
60
+ - src/main/java/net/spy/memcached/KetamaNodeLocator.java
61
+ - src/main/java/net/spy/memcached/ReturnData.java
62
+ - src/main/java/net/spy/memcached/transcoders/SimpleTranscoder.java
63
+ - src/main/java/net/spy/memcached/util/DefaultKetamaNodeLocatorConfiguration.java
64
+ - target/spymemcached-ext-0.0.1.jar
65
+ homepage: ""
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options: []
70
+
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.8.24
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: jruby compatible memcached client
92
+ test_files:
93
+ - spec/memcached_spec.rb
94
+ - spec/spec_helper.rb