jruby-memcached-thoughtworks 0.6.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.
- checksums.yaml +7 -0
- data/.gitignore +31 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +89 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +20 -0
- data/README.md +75 -0
- data/Rakefile +16 -0
- data/benchmark.rb +104 -0
- data/jruby_memcached.gemspec +21 -0
- data/lib/memcached.rb +12 -0
- data/lib/memcached/version.rb +3 -0
- data/pom.xml +69 -0
- data/spec/memcached_spec.rb +247 -0
- data/spec/rails_spec.rb +181 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/timeout_spec.rb +103 -0
- data/src/main/java/com/openfeint/memcached/Memcached.java +511 -0
- data/src/main/java/com/openfeint/memcached/MemcachedService.java +35 -0
- data/src/main/java/com/openfeint/memcached/Rails.java +237 -0
- data/src/main/java/com/openfeint/memcached/error/ATimeoutOccurred.java +7 -0
- data/src/main/java/com/openfeint/memcached/error/Error.java +31 -0
- data/src/main/java/com/openfeint/memcached/error/NotFound.java +7 -0
- data/src/main/java/com/openfeint/memcached/error/NotStored.java +7 -0
- data/src/main/java/com/openfeint/memcached/error/NotSupport.java +7 -0
- data/src/main/java/com/openfeint/memcached/transcoder/MarshalTranscoder.java +91 -0
- data/src/main/java/com/openfeint/memcached/transcoder/MarshalZlibTranscoder.java +103 -0
- data/src/main/java/net/spy/memcached/KetamaNodeLocator.java +165 -0
- data/src/main/java/net/spy/memcached/util/DefaultKetamaNodeLocatorConfiguration.java +104 -0
- data/target/spymemcached-ext-0.0.2.jar +0 -0
- metadata +104 -0
@@ -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,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,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jruby-memcached-thoughtworks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bill DePhillips
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mocha
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: jruby memcacached client which is compatible with memcached gem
|
42
|
+
email:
|
43
|
+
- bill.dephillips@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .travis.yml
|
50
|
+
- CHANGELOG.md
|
51
|
+
- Gemfile
|
52
|
+
- MIT-LICENSE
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- benchmark.rb
|
56
|
+
- jruby_memcached.gemspec
|
57
|
+
- lib/memcached.rb
|
58
|
+
- lib/memcached/version.rb
|
59
|
+
- pom.xml
|
60
|
+
- spec/memcached_spec.rb
|
61
|
+
- spec/rails_spec.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- spec/timeout_spec.rb
|
64
|
+
- src/main/java/com/openfeint/memcached/Memcached.java
|
65
|
+
- src/main/java/com/openfeint/memcached/MemcachedService.java
|
66
|
+
- src/main/java/com/openfeint/memcached/Rails.java
|
67
|
+
- src/main/java/com/openfeint/memcached/error/ATimeoutOccurred.java
|
68
|
+
- src/main/java/com/openfeint/memcached/error/Error.java
|
69
|
+
- src/main/java/com/openfeint/memcached/error/NotFound.java
|
70
|
+
- src/main/java/com/openfeint/memcached/error/NotStored.java
|
71
|
+
- src/main/java/com/openfeint/memcached/error/NotSupport.java
|
72
|
+
- src/main/java/com/openfeint/memcached/transcoder/MarshalTranscoder.java
|
73
|
+
- src/main/java/com/openfeint/memcached/transcoder/MarshalZlibTranscoder.java
|
74
|
+
- src/main/java/net/spy/memcached/KetamaNodeLocator.java
|
75
|
+
- src/main/java/net/spy/memcached/util/DefaultKetamaNodeLocatorConfiguration.java
|
76
|
+
- target/spymemcached-ext-0.0.2.jar
|
77
|
+
homepage: https://github.com/ThoughtWorksStudios/jruby-memcached
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.3.0
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: jruby compatible memcached client
|
100
|
+
test_files:
|
101
|
+
- spec/memcached_spec.rb
|
102
|
+
- spec/rails_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/timeout_spec.rb
|