|
18 | 18 |
|
19 | 19 | package org.apache.hadoop.ipc; |
20 | 20 |
|
| 21 | +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
21 | 22 | import static org.junit.Assert.assertEquals; |
22 | 23 | import static org.junit.Assert.assertFalse; |
23 | 24 | import static org.junit.Assert.assertNotNull; |
@@ -815,6 +816,81 @@ public Void call() throws IOException { |
815 | 816 | } |
816 | 817 | } |
817 | 818 |
|
| 819 | + /** |
| 820 | + * The {@link ConnectionId#hashCode} has to be stable despite updates that occur as the the |
| 821 | + * address evolves over time. The {@link ConnectionId} is used as a primary key in maps, so |
| 822 | + * its hashCode can't change. |
| 823 | + * |
| 824 | + * @throws IOException if there is a client or server failure |
| 825 | + */ |
| 826 | + @Test |
| 827 | + public void testStableHashCode() throws IOException { |
| 828 | + Server server = new TestServer(5, false); |
| 829 | + try { |
| 830 | + server.start(); |
| 831 | + |
| 832 | + // Leave host unresolved to start. Use "localhost" as opposed |
| 833 | + // to local IP from NetUtils.getConnectAddress(server) to force |
| 834 | + // resolution later |
| 835 | + InetSocketAddress unresolvedAddr = InetSocketAddress.createUnresolved( |
| 836 | + "localhost", NetUtils.getConnectAddress(server).getPort()); |
| 837 | + |
| 838 | + // Setup: Create a ConnectionID using an unresolved address, and get it's hashCode to serve |
| 839 | + // as a point of comparison. |
| 840 | + int rpcTimeout = MIN_SLEEP_TIME * 2; |
| 841 | + final ConnectionId remoteId = getConnectionId(unresolvedAddr, rpcTimeout, conf); |
| 842 | + int expected = remoteId.hashCode(); |
| 843 | + |
| 844 | + // Start client |
| 845 | + Client.setConnectTimeout(conf, 100); |
| 846 | + Client client = new Client(LongWritable.class, conf); |
| 847 | + try { |
| 848 | + // Test: Call should re-resolve host and succeed |
| 849 | + LongWritable param = new LongWritable(RANDOM.nextLong()); |
| 850 | + client.call(RPC.RpcKind.RPC_BUILTIN, param, remoteId, |
| 851 | + RPC.RPC_SERVICE_CLASS_DEFAULT, null); |
| 852 | + int actual = remoteId.hashCode(); |
| 853 | + |
| 854 | + // Verify: The hashCode should match, although the InetAddress is different since it has |
| 855 | + // now been resolved |
| 856 | + assertThat(remoteId.getAddress()).isNotEqualTo(unresolvedAddr); |
| 857 | + assertThat(remoteId.getAddress().getHostName()).isEqualTo(unresolvedAddr.getHostName()); |
| 858 | + assertThat(remoteId.hashCode()).isEqualTo(expected); |
| 859 | + |
| 860 | + // Test: Call should succeed without having to re-resolve |
| 861 | + InetSocketAddress expectedSocketAddress = remoteId.getAddress(); |
| 862 | + param = new LongWritable(RANDOM.nextLong()); |
| 863 | + client.call(RPC.RpcKind.RPC_BUILTIN, param, remoteId, |
| 864 | + RPC.RPC_SERVICE_CLASS_DEFAULT, null); |
| 865 | + |
| 866 | + // Verify: The same instance of the InetSocketAddress has been used to make the second |
| 867 | + // call |
| 868 | + assertThat(remoteId.getAddress()).isSameAs(expectedSocketAddress); |
| 869 | + |
| 870 | + // Verify: The hashCode is protected against updates to the host name |
| 871 | + String hostName = InetAddress.getLocalHost().getHostName(); |
| 872 | + InetSocketAddress mismatchedHostName = NetUtils.createSocketAddr( |
| 873 | + InetAddress.getLocalHost().getHostName(), |
| 874 | + remoteId.getAddress().getPort()); |
| 875 | + assertThatExceptionOfType(IllegalArgumentException.class) |
| 876 | + .isThrownBy(() -> remoteId.setAddress(mismatchedHostName)) |
| 877 | + .withMessageStartingWith("Hostname must match"); |
| 878 | + |
| 879 | + // Verify: The hashCode is protected against updates to the port |
| 880 | + InetSocketAddress mismatchedPort = NetUtils.createSocketAddr( |
| 881 | + remoteId.getAddress().getHostName(), |
| 882 | + remoteId.getAddress().getPort() + 1); |
| 883 | + assertThatExceptionOfType(IllegalArgumentException.class) |
| 884 | + .isThrownBy(() -> remoteId.setAddress(mismatchedPort)) |
| 885 | + .withMessageStartingWith("Port must match"); |
| 886 | + } finally { |
| 887 | + client.stop(); |
| 888 | + } |
| 889 | + } finally { |
| 890 | + server.stop(); |
| 891 | + } |
| 892 | + } |
| 893 | + |
818 | 894 | @Test(timeout=60000) |
819 | 895 | public void testIpcFlakyHostResolution() throws IOException { |
820 | 896 | // start server |
|
0 commit comments