jruby-memcached 0.1.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/.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