2
2
3
3
import java .net .InetSocketAddress ;
4
4
import java .util .Collection ;
5
+ import java .util .Collections ;
5
6
import java .util .HashMap ;
6
7
import java .util .Iterator ;
7
8
import java .util .List ;
8
9
import java .util .Map ;
10
+ import java .util .NoSuchElementException ;
11
+ import java .util .Set ;
12
+ import java .util .concurrent .atomic .AtomicReference ;
9
13
10
14
import net .spy .memcached .MemcachedNode ;
11
15
import net .spy .memcached .NodeLocator ;
12
16
import net .spy .memcached .compat .SpyObject ;
13
17
import net .spy .memcached .vbucket .config .Config ;
18
+ import net .spy .memcached .vbucket .config .ConfigDifference ;
14
19
15
20
/**
16
21
* Implementation of the {@link NodeLocator} interface that contains vbucket hashing methods
17
22
*/
18
23
public class VBucketNodeLocator extends SpyObject implements NodeLocator {
19
24
20
- private Map <String , MemcachedNode > nodesMap ;
21
-
22
- private Config config ;
25
+ private final AtomicReference <TotalConfig > fullConfig ;
23
26
24
27
/**
25
28
* Construct a VBucketNodeLocator over the given JSON configuration string.
@@ -29,23 +32,32 @@ public class VBucketNodeLocator extends SpyObject implements NodeLocator {
29
32
*/
30
33
public VBucketNodeLocator (List <MemcachedNode > nodes , Config jsonConfig ) {
31
34
super ();
32
- setNodes ( nodes );
33
- setConfig ( jsonConfig );
35
+ fullConfig = new AtomicReference < TotalConfig >( );
36
+ fullConfig . set ( new TotalConfig ( jsonConfig , fillNodesEntries ( nodes )) );
34
37
}
35
38
36
39
/**
37
40
* {@inheritDoc}
38
41
*/
39
42
public MemcachedNode getPrimary (String k ) {
43
+ TotalConfig totConfig = fullConfig .get ();
44
+ Config config = totConfig .getConfig ();
45
+ Map <String , MemcachedNode > nodesMap = totConfig .getNodesMap ();
40
46
int vbucket = config .getVbucketByKey (k );
41
47
int serverNumber = config .getMaster (vbucket );
42
48
String server = config .getServer (serverNumber );
43
- // choose appropriate MemecachedNode according to config data
49
+ // choose appropriate MemcachedNode according to config data
44
50
MemcachedNode pNode = nodesMap .get (server );
45
51
if (pNode == null ) {
46
- getLogger ().error ("The node locator does not have a primary for key %s." , k );
52
+ getLogger ().error ("The node locator does not have a primary for key %s. Wanted vbucket %s which should be on server %s." , k , vbucket , server );
53
+ getLogger ().error ("List of nodes has %s entries:" , nodesMap .size ());
54
+ Set <String > keySet = nodesMap .keySet ();
55
+ Iterator <String > iterator = keySet .iterator ();
56
+ while (iterator .hasNext ()) {
57
+ String anode = iterator .next ();
58
+ getLogger ().error ("MemcachedNode for %s is %s" , anode , nodesMap .get (anode ));
59
+ }
47
60
Collection <MemcachedNode > nodes = nodesMap .values ();
48
- getLogger ().error ("MemcachedNode has %s entries:" , nodesMap .size ());
49
61
for (MemcachedNode node : nodes ) {
50
62
getLogger ().error (node );
51
63
}
@@ -58,14 +70,16 @@ public MemcachedNode getPrimary(String k) {
58
70
* {@inheritDoc}
59
71
*/
60
72
public Iterator <MemcachedNode > getSequence (String k ) {
73
+ Map <String , MemcachedNode > nodesMap = fullConfig .get ().getNodesMap ();
61
74
return nodesMap .values ().iterator ();
62
75
}
63
76
64
77
/**
65
78
* {@inheritDoc}
66
79
*/
67
80
public Collection <MemcachedNode > getAll () {
68
- return this .nodesMap .values ();
81
+ Map <String , MemcachedNode > nodesMap = fullConfig .get ().getNodesMap ();
82
+ return nodesMap .values ();
69
83
}
70
84
71
85
/**
@@ -74,9 +88,17 @@ public Collection<MemcachedNode> getAll() {
74
88
public NodeLocator getReadonlyCopy () {
75
89
return this ;
76
90
}
77
- public void updateLocator (final List <MemcachedNode > nodes , final Config conf ) {
78
- setNodes (nodes );
79
- setConfig (conf );
91
+
92
+ public void updateLocator (final List <MemcachedNode > nodes , final Config newconf ) {
93
+ // we'll get a new config for various reasons we don't care about, so check if we do care
94
+ Config current = fullConfig .get ().getConfig ();
95
+ ConfigDifference compareTo = current .compareTo (newconf );
96
+ if (compareTo .isSequenceChanged () || compareTo .getVbucketsChanges () > 0 ) {
97
+ getLogger ().debug ("Updating configuration, received updated configuration with significant changes." );
98
+ fullConfig .set (new TotalConfig (newconf , fillNodesEntries (nodes )));
99
+ } else {
100
+ getLogger ().debug ("Received updated configuration with insignificant changes." );
101
+ }
80
102
}
81
103
82
104
/**
@@ -85,10 +107,11 @@ public void updateLocator(final List<MemcachedNode> nodes, final Config conf) {
85
107
* @return vbucket index
86
108
*/
87
109
public int getVBucketIndex (String key ) {
88
- return config .getVbucketByKey (key );
110
+ Config config = fullConfig .get ().getConfig ();
111
+ return config .getVbucketByKey (key );
89
112
}
90
113
91
- private void setNodes (Collection <MemcachedNode > nodes ) {
114
+ private Map < String , MemcachedNode > fillNodesEntries (Collection <MemcachedNode > nodes ) {
92
115
HashMap <String , MemcachedNode > vbnodesMap = new HashMap <String , MemcachedNode >();
93
116
getLogger ().debug ("Updating nodesMap in VBucketNodeLocator." );
94
117
for (MemcachedNode node : nodes ) {
@@ -101,12 +124,9 @@ private void setNodes(Collection<MemcachedNode> nodes) {
101
124
vbnodesMap .put (hostname , node );
102
125
}
103
126
104
- this . nodesMap = vbnodesMap ;
127
+ return Collections . unmodifiableMap ( vbnodesMap ) ;
105
128
}
106
129
107
- private void setConfig (final Config config ) {
108
- this .config = config ;
109
- }
110
130
111
131
/**
112
132
* Method returns the node that is not contained in the specified collection of the failed nodes
@@ -115,6 +135,8 @@ private void setConfig(final Config config) {
115
135
* @return The first MemcachedNode which meets requirements
116
136
*/
117
137
public MemcachedNode getAlternative (String k , Collection <MemcachedNode > notMyVbucketNodes ) {
138
+ // it's safe to only copy the map here, only removing references found to be incorrect, and trying remaining
139
+ Map <String , MemcachedNode > nodesMap = new HashMap <String ,MemcachedNode >(fullConfig .get ().getNodesMap ());
118
140
Collection <MemcachedNode > nodes = nodesMap .values ();
119
141
nodes .removeAll (notMyVbucketNodes );
120
142
if (nodes .isEmpty ()) {
@@ -123,4 +145,23 @@ public MemcachedNode getAlternative(String k, Collection<MemcachedNode> notMyVbu
123
145
return nodes .iterator ().next ();
124
146
}
125
147
}
148
+
149
+ private class TotalConfig {
150
+ private Config config ;
151
+ private Map <String , MemcachedNode > nodesMap ;
152
+
153
+ public TotalConfig (Config newConfig , Map <String , MemcachedNode > newMap ) {
154
+ config = newConfig ;
155
+ nodesMap = Collections .unmodifiableMap (newMap );
156
+ }
157
+
158
+ protected Config getConfig () {
159
+ return config ;
160
+ }
161
+
162
+ protected Map <String , MemcachedNode > getNodesMap () {
163
+ return nodesMap ;
164
+ }
165
+ }
166
+
126
167
}
0 commit comments