1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2008 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je.rep.impl;
9 
10 import java.io.Serializable;
11 import java.net.InetSocketAddress;
12 
13 import com.sleepycat.je.EnvironmentFailureException;
14 import com.sleepycat.je.JEVersion;
15 import com.sleepycat.je.rep.NodeType;
16 import com.sleepycat.je.rep.ReplicationConfig;
17 import com.sleepycat.je.rep.ReplicationNode;
18 import com.sleepycat.je.rep.impl.RepGroupImpl.BarrierState;
19 import com.sleepycat.je.rep.impl.node.NameIdPair;
20 import com.sleepycat.je.rep.stream.Protocol;
21 import com.sleepycat.je.rep.utilint.HostPortPair;
22 import com.sleepycat.je.utilint.VLSN;
23 
24 /**
25  * Describes a node that is a member of the replication group.
26  */
27 public class RepNodeImpl implements ReplicationNode, Serializable {
28 
29     private static final long serialVersionUID = 1L;
30 
31     /* Identifies the node both by external name and internal node ID. */
32     private final NameIdPair nameIdPair;
33 
34     /* The node type, electable, monitor, etc. */
35     private final NodeType type;
36 
37     /*
38      * True if the node was acknowledged by a quorum and its entry is therefore
39      * considered durable.  SECONDARY nodes are always considered acknowledged.
40      */
41     private boolean quorumAck;
42 
43     /*
44      * True if the node has been removed and is no longer an active part of the
45      * group
46      */
47     private boolean isRemoved;
48 
49     /* The hostname used for communications with the node. */
50     private String hostName;
51 
52     /* The port used by a node. */
53     private int port;
54 
55     /* The Cleaner Barrier state associated with the node. */
56     private BarrierState barrierState;
57 
58     /*
59      * This version is used in conjunction with the group level change
60      * version to identify the incremental changes made to individual
61      * changes made to a group.
62      */
63     private int changeVersion = NULL_CHANGE;
64 
65     private static final int NULL_CHANGE = -1;
66 
67     /**
68      * The JE version most recently noted running on this node, or null if not
69      * known.
70      */
71     private volatile JEVersion jeVersion;
72 
73     /**
74      * @hidden
75      *
76      * Constructor used to de-serialize a Node. All other convenience
77      * constructors funnel through this one so that argument checks can
78      * be systematically enforced.
79      */
RepNodeImpl(final NameIdPair nameIdPair, final NodeType type, final boolean quorumAck, final boolean isRemoved, final String hostName, final int port, final BarrierState barrierState, final int changeVersion, final JEVersion jeVersion)80     public RepNodeImpl(final NameIdPair nameIdPair,
81                        final NodeType type,
82                        final boolean quorumAck,
83                        final boolean isRemoved,
84                        final String hostName,
85                        final int port,
86                        final BarrierState barrierState,
87                        final int changeVersion,
88                        final JEVersion jeVersion) {
89 
90         if (nameIdPair.getName().equals(RepGroupDB.GROUP_KEY)) {
91             throw EnvironmentFailureException.unexpectedState
92             ("Member node ID is the reserved key value: " + nameIdPair);
93         }
94 
95         if (hostName == null) {
96             throw EnvironmentFailureException.unexpectedState
97             ("The hostname argument must not be null");
98         }
99 
100         if (type == null) {
101             throw EnvironmentFailureException.unexpectedState
102             ("The nodeType argument must not be null");
103         }
104 
105         this.nameIdPair = nameIdPair;
106         this.type = type;
107         this.quorumAck = quorumAck || type.isSecondary();
108         this.isRemoved = isRemoved;
109         this.hostName = hostName;
110         this.port = port;
111         this.barrierState = barrierState;
112         this.changeVersion = changeVersion;
113         this.jeVersion = jeVersion;
114     }
115 
116     /**
117      * @hidden
118      *
119      * Convenience constructor for the above.
120      */
RepNodeImpl(final NameIdPair nameIdPair, final NodeType type, final boolean quorumAck, final boolean isRemoved, final String hostName, final int port, final int changeVersion, final JEVersion jeVersion)121     public RepNodeImpl(final NameIdPair nameIdPair,
122                        final NodeType type,
123                        final boolean quorumAck,
124                        final boolean isRemoved,
125                        final String hostName,
126                        final int port,
127                        final int changeVersion,
128                        final JEVersion jeVersion) {
129         this(nameIdPair, type, quorumAck, isRemoved, hostName, port,
130              new BarrierState(VLSN.NULL_VLSN, System.currentTimeMillis()),
131              changeVersion, jeVersion);
132     }
133 
134     /**
135      * @hidden
136      * Convenience constructor for transient nodes
137      */
RepNodeImpl(final NameIdPair nameIdPair, final NodeType type, final String hostName, final int port, final JEVersion jeVersion)138     public RepNodeImpl(final NameIdPair nameIdPair,
139                        final NodeType type,
140                        final String hostName,
141                        final int port,
142                        final JEVersion jeVersion) {
143         this(nameIdPair, type, false, false, hostName, port, NULL_CHANGE,
144              jeVersion);
145     }
146 
147     /**
148      * @hidden
149      * Convenience constructor for transient nodes during unit tests.
150      */
RepNodeImpl(final ReplicationConfig repConfig)151     public RepNodeImpl(final ReplicationConfig repConfig) {
152         this(new NameIdPair(repConfig.getNodeName(), NameIdPair.NULL_NODE_ID),
153              repConfig.getNodeType(),
154              repConfig.getNodeHostname(),
155              repConfig.getNodePort(),
156              JEVersion.CURRENT_VERSION);
157     }
158 
159     /**
160      * @hidden
161      *
162      * Convenience constructor for the above.
163      */
RepNodeImpl(final String nodeName, final String hostName, final int port, final JEVersion jeVersion)164     public RepNodeImpl(final String nodeName,
165                        final String hostName,
166                        final int port,
167                        final JEVersion jeVersion) {
168         this(new NameIdPair(nodeName, NameIdPair.NULL.getId()),
169              NodeType.ELECTABLE, hostName, port, jeVersion);
170     }
171 
172     /**
173      * @hidden
174      *
175      * Convenience constructor for the above.
176      */
RepNodeImpl(Protocol.NodeGroupInfo mi)177     public RepNodeImpl(Protocol.NodeGroupInfo mi) {
178         this(mi.getNameIdPair(),
179              mi.getNodeType(),
180              mi.getHostName(),
181              mi.port(),
182              mi.getJEVersion());
183     }
184 
185     /* (non-Javadoc)
186      * @see com.sleepycat.je.rep.ReplicationNode#getSocketAddress()
187      */
188     @Override
getSocketAddress()189     public InetSocketAddress getSocketAddress() {
190         return new InetSocketAddress(hostName, port);
191     }
192 
193     /**
194      * Returns whether the node was acknowledged by a quorum and its entry is
195      * therefore considered durable.  Secondary nodes are always considered
196      * acknowledged.
197      */
isQuorumAck()198     public boolean isQuorumAck() {
199         return quorumAck;
200     }
201 
isRemoved()202     public boolean isRemoved() {
203         assert !(isRemoved && type.isSecondary())
204             : "Secondary nodes are never marked removed";
205         return isRemoved;
206     }
207 
setChangeVersion(int changeVersion)208     public void setChangeVersion(int changeVersion) {
209         this.changeVersion = changeVersion;
210     }
211 
getChangeVersion()212     public int getChangeVersion() {
213         return changeVersion;
214     }
215 
getNameIdPair()216     public NameIdPair getNameIdPair() {
217         return nameIdPair;
218     }
219 
220     /* (non-Javadoc)
221      * @see com.sleepycat.je.rep.ReplicationNode#getName()
222      */
223     @Override
getName()224     public String getName() {
225         return nameIdPair.getName();
226     }
227 
getNodeId()228     public int getNodeId() {
229         return nameIdPair.getId();
230     }
231 
232     /* (non-Javadoc)
233      * @see com.sleepycat.je.rep.ReplicationNode#getNodeType()
234      */
235     @Override
getType()236     public NodeType getType() {
237         return type;
238     }
239 
240     /* (non-Javadoc)
241      * @see com.sleepycat.je.rep.ReplicationNode#getHostName()
242      */
243     @Override
getHostName()244     public String getHostName() {
245         return hostName;
246     }
247 
setHostName(String hostName)248     public void setHostName(String hostName) {
249         this.hostName = hostName;
250     }
251 
252     /* (non-Javadoc)
253      * @see com.sleepycat.je.rep.ReplicationNode#getPort()
254      */
255     @Override
getPort()256     public int getPort() {
257         return port;
258     }
259 
setPort(int port)260     public void setPort(int port) {
261         this.port = port;
262     }
263 
getHostPortPair()264     public String getHostPortPair() {
265         return HostPortPair.getString(hostName, port);
266     }
267 
getBarrierState()268     public BarrierState getBarrierState() {
269         return barrierState;
270     }
271 
setBarrierState(BarrierState barrierState)272     public BarrierState setBarrierState(BarrierState barrierState) {
273         return this.barrierState = barrierState;
274     }
275 
setQuorumAck(boolean quorumAck)276     public void setQuorumAck(boolean quorumAck) {
277         this.quorumAck = quorumAck;
278     }
279 
setRemoved(boolean isRemoved)280     public void setRemoved(boolean isRemoved) {
281         this.isRemoved = isRemoved;
282     }
283 
284     /**
285      * Returns the JE Version most recently noted running on this node, or
286      * {@code null} if not known.
287      */
getJEVersion()288     public JEVersion getJEVersion() {
289         return jeVersion;
290     }
291 
292     /**
293      * Updates the JE version most recently known running on this node to match
294      * the version specified.  Does nothing if the argument is null.
295      *
296      * @param otherJEVersion the version or {@code null}
297      */
updateJEVersion(final JEVersion otherJEVersion)298     public void updateJEVersion(final JEVersion otherJEVersion) {
299         if (otherJEVersion != null) {
300             jeVersion = otherJEVersion;
301         }
302     }
303 
304     @Override
toString()305     public String toString() {
306 
307         String acked = " (is member)";
308 
309         if (!quorumAck) {
310             acked = " (not yet a durable member)";
311         }
312 
313         if (isRemoved) {
314             acked = " (is removed)";
315         }
316 
317         String info =
318             String.format("Node:%s %s:%d%s%s changeVersion:%d %s%s\n",
319                           getName(), getHostName(), getPort(),
320                           acked,
321                           (!type.isElectable() ? " " + type : ""),
322                           getChangeVersion(),
323                           barrierState,
324                           ((jeVersion != null) ?
325                            " jeVersion:" + jeVersion :
326                            ""));
327         return info;
328 
329     }
330 
331     /**
332      * Checks if the argument represents the same node, ignoring fields that
333      * might legitimately vary over time.  Like the equals method, considers
334      * all fields, except ignores the quorumAck field (which may change
335      * temporarily), the nodeId (since it may not have been resolved as yet),
336      * and the isRemoved, barrierState, changeVersion, and jeVersion fields
337      * (which can change over time).
338      *
339      * @param mi the other object in the comparison
340      *
341      * @return true if the two are equivalent
342      */
equivalent(RepNodeImpl mi)343     public boolean equivalent(RepNodeImpl mi) {
344         if (this == mi) {
345             return true;
346         }
347 
348         if (mi == null) {
349             return false;
350         }
351 
352         if (port != mi.port) {
353             return false;
354         }
355 
356         if (hostName == null) {
357             if (mi.hostName != null) {
358                 return false;
359             }
360         } else if (!hostName.equals(mi.hostName)) {
361             return false;
362         }
363 
364         /* Ignore the id. */
365         if (!nameIdPair.getName().equals(mi.nameIdPair.getName())) {
366             return false;
367         }
368 
369         if (getType() != mi.getType()) {
370             return false;
371         }
372 
373         /*
374          * Ignore quorumAck, isRemoved, barrierState, changeVersion, and
375          * jeVersion
376          */
377 
378         return true;
379     }
380 
381     @Override
hashCode()382     public int hashCode() {
383         final int prime = 31;
384         int result = 1;
385         result = prime * result
386                 + ((hostName == null) ? 0 : hostName.hashCode());
387         result = prime * result + nameIdPair.hashCode();
388         result = prime * result + port;
389         result = prime * result + (isQuorumAck() ? 1231 : 1237);
390         result = prime * result +
391             (jeVersion == null ? 0 : jeVersion.hashCode());
392         return result;
393     }
394 
395     @Override
equals(Object obj)396     public boolean equals(Object obj) {
397         if (this == obj) {
398             return true;
399         }
400         if (obj == null) {
401             return false;
402         }
403         if (!(obj instanceof RepNodeImpl)) {
404             return false;
405         }
406         final RepNodeImpl other = (RepNodeImpl) obj;
407         if (hostName == null) {
408             if (other.hostName != null) {
409                 return false;
410             }
411         } else if (!hostName.equals(other.hostName)) {
412             return false;
413         }
414         if (!nameIdPair.equals(other.nameIdPair)) {
415             return false;
416         }
417         if (getType() != other.getType()) {
418             return false;
419         }
420         if (port != other.port) {
421             return false;
422         }
423         if (isQuorumAck() != other.isQuorumAck()) {
424             return false;
425         }
426         if (jeVersion == null) {
427             if (other.jeVersion != null) {
428                 return false;
429             }
430         } else if (!jeVersion.equals(other.getJEVersion())) {
431             return false;
432         }
433         return true;
434     }
435 }
436