From 884c42e315dfdc8287f0b5ea207a8a55d188dd97 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Mon, 21 Aug 2023 11:54:00 +0300 Subject: [PATCH 01/34] WIP --- .../io/scalecube/cluster/ClusterConfig.java | 68 ++++++++----------- .../java/io/scalecube/cluster/Member.java | 49 +++++++------ .../io/scalecube/cluster/ClusterImpl.java | 39 +++++++---- .../io/scalecube/cluster/ClusterTest.java | 11 ++- 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java b/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java index ee6a1b09..5cbd641c 100644 --- a/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java +++ b/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java @@ -5,6 +5,9 @@ import io.scalecube.cluster.membership.MembershipConfig; import io.scalecube.cluster.metadata.MetadataCodec; import io.scalecube.cluster.transport.api.TransportConfig; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Optional; import java.util.StringJoiner; import java.util.function.UnaryOperator; @@ -35,8 +38,7 @@ public final class ClusterConfig implements Cloneable { private String memberId; private String memberAlias; - private String externalHost; - private Integer externalPort; + private List externalHosts; private TransportConfig transportConfig = TransportConfig.defaultConfig(); private FailureDetectorConfig failureDetectorConfig = FailureDetectorConfig.defaultConfig(); @@ -136,27 +138,39 @@ public ClusterConfig metadataCodec(MetadataCodec metadataCodec) { } /** - * Returns externalHost. {@code externalHost} is a config property for container environments, - * it's being set for advertising to scalecube cluster some connectable hostname which maps to + * Returns externalHosts. {@code externalHosts} is a config property for container environments, + * it's being set for advertising to scalecube cluster some connectable hostnames which maps to * scalecube transport's hostname on which scalecube transport is listening. * - * @return external host + * @return external hosts */ - public String externalHost() { - return externalHost; + public List externalHosts() { + return externalHosts; } /** - * Setter for externalHost. {@code externalHost} is a config property for container environments, - * it's being set for advertising to scalecube cluster some connectable hostname which maps to - * scalecube transport's hostname on which scalecube transport is listening. + * Setter for externalHosts. {@code externalHosts} is a config property for container + * environments, it's being set for advertising to scalecube cluster some connectable hostnames + * which maps to scalecube transport's hostname on which scalecube transport is listening. + * + * @param externalHosts external hosts + * @return new {@code ClusterConfig} instance + */ + public ClusterConfig externalHosts(String... externalHosts) { + return externalHosts(Arrays.asList(externalHosts)); + } + + /** + * Setter for externalHosts. {@code externalHosts} is a config property for container + * environments, it's being set for advertising to scalecube cluster some connectable hostnames + * which maps to scalecube transport's hostname on which scalecube transport is listening. * - * @param externalHost external host + * @param externalHosts external hosts * @return new {@code ClusterConfig} instance */ - public ClusterConfig externalHost(String externalHost) { + public ClusterConfig externalHosts(List externalHosts) { ClusterConfig c = clone(); - c.externalHost = externalHost; + c.externalHosts = new ArrayList<>(externalHosts); return c; } @@ -205,31 +219,6 @@ public ClusterConfig memberAlias(String memberAlias) { return c; } - /** - * Returns externalPort. {@code externalPort} is a config property for container environments, - * it's being set for advertising to scalecube cluster a port which mapped to scalecube - * transport's listening port. - * - * @return external port - */ - public Integer externalPort() { - return externalPort; - } - - /** - * Setter for externalPort. {@code externalPort} is a config property for container environments, - * it's being set for advertising to scalecube cluster a port which mapped to scalecube - * transport's listening port. - * - * @param externalPort external port - * @return new {@code ClusterConfig} instance - */ - public ClusterConfig externalPort(Integer externalPort) { - ClusterConfig c = clone(); - c.externalPort = externalPort; - return c; - } - /** * Applies {@link TransportConfig} settings. * @@ -316,8 +305,7 @@ public String toString() { .add("metadataCodec=" + metadataCodec) .add("memberId='" + memberId + "'") .add("memberAlias='" + memberAlias + "'") - .add("externalHost='" + externalHost + "'") - .add("externalPort=" + externalPort) + .add("externalHosts=" + externalHosts) .add("transportConfig=" + transportConfig) .add("failureDetectorConfig=" + failureDetectorConfig) .add("gossipConfig=" + gossipConfig) diff --git a/cluster-api/src/main/java/io/scalecube/cluster/Member.java b/cluster-api/src/main/java/io/scalecube/cluster/Member.java index 65dc7835..b9798687 100644 --- a/cluster-api/src/main/java/io/scalecube/cluster/Member.java +++ b/cluster-api/src/main/java/io/scalecube/cluster/Member.java @@ -6,6 +6,8 @@ import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.StringJoiner; import java.util.UUID; @@ -20,7 +22,7 @@ public final class Member implements Externalizable { private String id; private String alias; - private Address address; + private List
addresses; private String namespace; public Member() {} @@ -28,16 +30,16 @@ public Member() {} /** * Constructor. * - * @param id member id; not null + * @param id member id * @param alias member alias (optional) - * @param address member address; not null - * @param namespace namespace; not null + * @param addresses member addresses + * @param namespace namespace */ - public Member(String id, String alias, Address address, String namespace) { - this.id = Objects.requireNonNull(id, "member id"); - this.alias = alias; // optional - this.address = Objects.requireNonNull(address, "member address"); - this.namespace = Objects.requireNonNull(namespace, "member namespace"); + public Member(String id, String alias, List
addresses, String namespace) { + this.id = Objects.requireNonNull(id, "id"); + this.alias = alias; + this.addresses = Objects.requireNonNull(addresses, "addresses"); + this.namespace = Objects.requireNonNull(namespace, "namespace"); } /** @@ -70,14 +72,14 @@ public String namespace() { } /** - * Returns cluster member address, an address on which this cluster member listens connections - * from other cluster members. + * Returns cluster member addresses, those are addresses on which this cluster member listens + * connections from other cluster members. * * @see io.scalecube.cluster.transport.api.TransportConfig#port(int) * @return member address */ - public Address address() { - return address; + public List
addresses() { + return addresses; } @Override @@ -90,13 +92,13 @@ public boolean equals(Object that) { } Member member = (Member) that; return Objects.equals(id, member.id) - && Objects.equals(address, member.address) + && Objects.equals(addresses, member.addresses) && Objects.equals(namespace, member.namespace); } @Override public int hashCode() { - return Objects.hash(id, address, namespace); + return Objects.hash(id, addresses, namespace); } @Override @@ -110,7 +112,10 @@ public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(alias); } // address - out.writeUTF(address.toString()); + out.writeInt(addresses.size()); + for (Address address : addresses) { + out.writeUTF(address.toString()); + } // namespace out.writeUTF(namespace); } @@ -124,8 +129,12 @@ public void readExternal(ObjectInput in) throws IOException { if (aliasNotNull) { alias = in.readUTF(); } - // address - address = Address.from(in.readUTF()); + // addresses + final int addressesSize = in.readInt(); + addresses = new ArrayList<>(addressesSize); + for (int i = 0; i < addressesSize; i++) { + addresses.add(Address.from(in.readUTF())); + } // namespace this.namespace = in.readUTF(); } @@ -143,9 +152,9 @@ private static String stringifyId(String id) { public String toString() { StringJoiner stringJoiner = new StringJoiner(":"); if (alias == null) { - return stringJoiner.add(namespace).add(stringifyId(id) + "@" + address).toString(); + return stringJoiner.add(namespace).add(stringifyId(id)).toString(); } else { - return stringJoiner.add(namespace).add(alias).add(stringifyId(id) + "@" + address).toString(); + return stringJoiner.add(namespace).add(alias).add(stringifyId(id)).toString(); } } } diff --git a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java index 8419f358..f38f5e97 100644 --- a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java @@ -20,8 +20,10 @@ import io.scalecube.utils.ServiceLoaderUtil; import java.io.Serializable; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -243,7 +245,9 @@ private Mono doStart0() { localMember = createLocalMember(boundTransport.address()); transport = new SenderAwareTransport(boundTransport, localMember.address()); - scheduler = Schedulers.newSingle("sc-cluster-" + localMember.address().port(), true); + final String name = + "sc-cluster-" + Integer.toHexString(System.identityHashCode(this)); + scheduler = Schedulers.newSingle(name, true); failureDetector = new FailureDetectorImpl( @@ -369,19 +373,26 @@ private Flux listenMembership() { * @return local cluster member with cluster address and cluster member id */ private Member createLocalMember(Address address) { - int port = Optional.ofNullable(config.externalPort()).orElse(address.port()); - - // calculate local member cluster address - Address memberAddress = - Optional.ofNullable(config.externalHost()) - .map(host -> Address.create(host, port)) - .orElseGet(() -> Address.create(address.host(), port)); - - return new Member( - config.memberId() != null ? config.memberId() : UUID.randomUUID().toString(), - config.memberAlias(), - memberAddress, - config.membershipConfig().namespace()); + final int port = address.port(); + final List
memberAddresses = new ArrayList<>(); + + // First address comes as "fair" listen address + memberAddresses.add(address); + + // Tail goes as externalHosts, if the exist + final List externalHosts = config.externalHosts(); + if (externalHosts != null) { + for (String externalHost : externalHosts) { + memberAddresses.add(Address.create(externalHost, port)); + } + } + + final String memberId = + config.memberId() != null ? config.memberId() : UUID.randomUUID().toString(); + final String memberAlias = config.memberAlias(); + final String namespace = config.membershipConfig().namespace(); + + return new Member(memberId, memberAlias, memberAddresses, namespace); } @Override diff --git a/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java b/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java index dd104ade..b99f6189 100644 --- a/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java @@ -154,7 +154,7 @@ public void testJoinLocalhostIgnoredWithOverride() throws InterruptedException { // Start seed node Cluster seedNode = - new ClusterImpl(new ClusterConfig().externalHost("localhost").externalPort(7878)) + new ClusterImpl(new ClusterConfig().externalHosts("localhost")) .transport(opts -> opts.port(7878).connectTimeout(CONNECT_TIMEOUT)) .membership(opts -> opts.seedMembers(addresses)) .transportFactory(TcpTransportFactory::new) @@ -590,14 +590,13 @@ private void shutdown(List nodes) { @Test public void testExplicitLocalMemberId() { - ClusterConfig config = ClusterConfig.defaultConfig() - .memberId("test-member"); + ClusterConfig config = ClusterConfig.defaultConfig().memberId("test-member"); ClusterImpl cluster = null; try { - cluster = (ClusterImpl) new ClusterImpl(config) - .transportFactory(TcpTransportFactory::new) - .startAwait(); + cluster = + (ClusterImpl) + new ClusterImpl(config).transportFactory(TcpTransportFactory::new).startAwait(); assertEquals("test-member", cluster.member().id()); } finally { From b757da636b90274846b3f073c0ace4457afd5fbf Mon Sep 17 00:00:00 2001 From: "rostyslav.baldovskyi" Date: Thu, 24 Aug 2023 18:20:57 +0300 Subject: [PATCH 02/34] Make compile (WIP) --- .../java/io/scalecube/cluster/Cluster.java | 12 ++- .../java/io/scalecube/cluster/Member.java | 13 +++ .../cluster/utils/NetworkEmulator.java | 32 ++++++- .../utils/NetworkEmulatorTransport.java | 3 +- .../io/scalecube/cluster/ClusterImpl.java | 27 +++--- .../fdetector/FailureDetectorImpl.java | 47 ++++++----- .../cluster/gossip/GossipProtocolImpl.java | 8 +- .../membership/MembershipProtocolImpl.java | 84 ++++++++++++------- .../cluster/metadata/MetadataStoreImpl.java | 26 +++--- .../cluster/ClusterNamespacesTest.java | 46 +++++----- .../io/scalecube/cluster/ClusterTest.java | 36 ++++---- .../fdetector/FailureDetectorTest.java | 6 +- .../membership/MembershipProtocolTest.java | 10 +-- .../examples/ClusterJoinExamples.java | 24 +++--- .../ClusterJoinNamespacesExamples.java | 26 +++--- .../examples/ClusterMetadataExample.java | 2 +- .../CustomMetadataEncodingExample.java | 4 +- .../io/scalecube/examples/GossipExample.java | 8 +- .../examples/MembershipEventsExample.java | 4 +- .../scalecube/examples/MessagingExample.java | 4 +- .../examples/WebsocketMessagingExample.java | 16 ++-- .../cluster/transport/api/Message.java | 32 +++++-- .../transport/api/TransportWrapper.java | 53 ++++++++++++ .../scalecube/transport/netty/BaseTest.java | 24 ++++++ .../transport/netty/tcp/TcpTransportTest.java | 22 ++--- .../websocket/WebsocketTransportTest.java | 22 ++--- 26 files changed, 391 insertions(+), 200 deletions(-) create mode 100644 transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java diff --git a/cluster-api/src/main/java/io/scalecube/cluster/Cluster.java b/cluster-api/src/main/java/io/scalecube/cluster/Cluster.java index b0f15582..00edafbb 100644 --- a/cluster-api/src/main/java/io/scalecube/cluster/Cluster.java +++ b/cluster-api/src/main/java/io/scalecube/cluster/Cluster.java @@ -3,6 +3,7 @@ import io.scalecube.cluster.transport.api.Message; import io.scalecube.net.Address; import java.util.Collection; +import java.util.List; import java.util.Optional; import reactor.core.publisher.Mono; @@ -14,7 +15,7 @@ public interface Cluster { * * @return cluster address */ - Address address(); + List
addresses(); /** * Send a msg from this member (src) to target member (specified in parameters). @@ -34,6 +35,15 @@ public interface Cluster { */ Mono send(Address address, Message message); + /** + * Send a msg from this member (src) to target member (specified in parameters). + * + * @param addresses target addresses + * @param message msg + * @return promise telling success or failure + */ + Mono send(List
addresses, Message message); + /** * Sends message to the given address. It will issue connect in case if no transport channel by * given transport {@code address} exists already. Send is an async operation and expecting a diff --git a/cluster-api/src/main/java/io/scalecube/cluster/Member.java b/cluster-api/src/main/java/io/scalecube/cluster/Member.java index b9798687..42b958c9 100644 --- a/cluster-api/src/main/java/io/scalecube/cluster/Member.java +++ b/cluster-api/src/main/java/io/scalecube/cluster/Member.java @@ -7,6 +7,7 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.StringJoiner; @@ -42,6 +43,18 @@ public Member(String id, String alias, List
addresses, String namespace this.namespace = Objects.requireNonNull(namespace, "namespace"); } + /** + * Constructor. + * + * @param id member id + * @param alias member alias (optional) + * @param address member address + * @param namespace namespace + */ + public Member(String id, String alias, Address address, String namespace) { + this(id, alias, Collections.singletonList(address), namespace); + } + /** * Returns cluster member local id. * diff --git a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java index d51a7526..d3e6ab3d 100644 --- a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java +++ b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java @@ -5,6 +5,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.StringJoiner; import java.util.concurrent.ConcurrentHashMap; @@ -213,15 +214,40 @@ public InboundSettings inboundSettings(Address destination) { return inboundSettings.getOrDefault(destination, defaultInboundSettings); } + /** + * Returns network inbound settings applied to the given destination. + * + * @param destinations addresses of target endpoint + * @return network inbound settings + */ + public InboundSettings inboundSettings(List
destinations) { + if (destinations.isEmpty()) { + return defaultInboundSettings; + } + + for (Address destination : destinations) { + InboundSettings inboundSettings = this.inboundSettings.get(destination); + + if (inboundSettings != null) { + return inboundSettings; + } + } + + return defaultInboundSettings; + } + /** * Setter for network emulator inbound settings for specific destination. * * @param shallPass shallPass inbound flag */ - public void inboundSettings(Address destination, boolean shallPass) { + public void inboundSettings(List
destinations, boolean shallPass) { InboundSettings settings = new InboundSettings(shallPass); - inboundSettings.put(destination, settings); - LOGGER.debug("[{}] Set inbound settings {} to {}", address, settings, destination); + + destinations.forEach(destination -> { + inboundSettings.put(destination, settings); + LOGGER.debug("[{}] Set inbound settings {} to {}", address, settings, destination); + }); } /** diff --git a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java index 381042c5..c75ab24f 100644 --- a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java +++ b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java @@ -3,6 +3,7 @@ import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; +import java.util.Collections; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -83,6 +84,6 @@ public Flux listen() { } private Message enhanceWithSender(Message message) { - return Message.with(message).sender(transport.address()).build(); + return Message.with(message).sender(Collections.singletonList(transport.address())).build(); } } diff --git a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java index f38f5e97..a530fd98 100644 --- a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java @@ -16,6 +16,7 @@ import io.scalecube.cluster.transport.api.Transport; import io.scalecube.cluster.transport.api.TransportConfig; import io.scalecube.cluster.transport.api.TransportFactory; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.net.Address; import io.scalecube.utils.ServiceLoaderUtil; import java.io.Serializable; @@ -243,7 +244,8 @@ private Mono doStart0() { .flatMap( boundTransport -> { localMember = createLocalMember(boundTransport.address()); - transport = new SenderAwareTransport(boundTransport, localMember.address()); + + transport = new SenderAwareTransport(boundTransport, localMember.addresses()); final String name = "sc-cluster-" + Integer.toHexString(System.identityHashCode(this)); @@ -379,7 +381,7 @@ private Member createLocalMember(Address address) { // First address comes as "fair" listen address memberAddresses.add(address); - // Tail goes as externalHosts, if the exist + // Tail goes as externalHosts, if exists final List externalHosts = config.externalHosts(); if (externalHosts != null) { for (String externalHost : externalHosts) { @@ -396,13 +398,13 @@ private Member createLocalMember(Address address) { } @Override - public Address address() { - return member().address(); + public List
addresses() { + return member().addresses(); } @Override public Mono send(Member member, Message message) { - return send(member.address(), message); + return TransportWrapper.send(transport, member.addresses(), message); } @Override @@ -410,6 +412,11 @@ public Mono send(Address address, Message message) { return transport.send(address, message); } + @Override + public Mono send(List
addresses, Message message) { + return TransportWrapper.send(transport, addresses, message); + } + @Override public Mono requestResponse(Address address, Message request) { return transport.requestResponse(address, request); @@ -417,7 +424,7 @@ public Mono requestResponse(Address address, Message request) { @Override public Mono requestResponse(Member member, Message request) { - return transport.requestResponse(member.address(), request); + return TransportWrapper.requestResponse(transport, member.addresses(), request); } @Override @@ -526,11 +533,11 @@ public Mono onShutdown() { private static class SenderAwareTransport implements Transport { private final Transport transport; - private final Address address; + private final List
addresses; - private SenderAwareTransport(Transport transport, Address address) { + private SenderAwareTransport(Transport transport, List
addresses) { this.transport = Objects.requireNonNull(transport); - this.address = Objects.requireNonNull(address); + this.addresses = Objects.requireNonNull(addresses); } @Override @@ -569,7 +576,7 @@ public Flux listen() { } private Message enhanceWithSender(Message message) { - return Message.with(message).sender(address).build(); + return Message.with(message).sender(addresses).build(); } } } diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index ac270c4e..719437b5 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -8,6 +8,7 @@ import io.scalecube.cluster.membership.MembershipEvent; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.net.Address; import java.time.Duration; import java.util.ArrayList; @@ -42,6 +43,8 @@ public final class FailureDetectorImpl implements FailureDetector { private final Transport transport; private final FailureDetectorConfig config; + private final TransportWrapper transportWrapper; + // State private final List pingMembers = new ArrayList<>(); @@ -81,6 +84,8 @@ public FailureDetectorImpl( this.config = Objects.requireNonNull(config); this.scheduler = Objects.requireNonNull(scheduler); + this.transportWrapper = new TransportWrapper(this.transport); + // Subscribe actionsDisposables.addAll( Arrays.asList( @@ -145,9 +150,9 @@ private void doPing() { Message pingMsg = Message.withData(pingData).qualifier(PING).correlationId(cid).build(); LOGGER.debug("[{}][{}] Send Ping to {}", localMember, period, pingMember); - Address address = pingMember.address(); - transport - .requestResponse(address, pingMsg) + List
addresses = pingMember.addresses(); + transportWrapper + .requestResponse(addresses, pingMsg) .timeout(Duration.ofMillis(config.pingTimeout()), scheduler) .publishOn(scheduler) .subscribe( @@ -189,8 +194,8 @@ private void doPingReq( Duration timeout = Duration.ofMillis(config.pingInterval() - config.pingTimeout()); pingReqMembers.forEach( member -> - transport - .requestResponse(member.address(), pingReqMsg) + transportWrapper + .requestResponse(member.addresses(), pingReqMsg) .timeout(timeout, scheduler) .publishOn(scheduler) .subscribe( @@ -232,7 +237,7 @@ private void onMessage(Message message) { /** Listens to PING message and answers with ACK. */ private void onPing(Message message) { long period = this.currentPeriod; - Address sender = message.sender(); + List
sender = message.sender(); LOGGER.debug("[{}][{}] Received Ping from {}", localMember, period, sender); PingData data = message.data(); data = data.withAckType(AckType.DEST_OK); @@ -249,10 +254,10 @@ private void onPing(Message message) { String correlationId = message.correlationId(); Message ackMessage = Message.withData(data).qualifier(PING_ACK).correlationId(correlationId).build(); - Address address = data.getFrom().address(); - LOGGER.debug("[{}][{}] Send PingAck to {}", localMember, period, address); - transport - .send(address, ackMessage) + List
addresses = data.getFrom().addresses(); + LOGGER.debug("[{}][{}] Send PingAck to {}", localMember, period, addresses); + transportWrapper + .send(addresses, ackMessage) .subscribe( null, ex -> @@ -260,7 +265,7 @@ private void onPing(Message message) { "[{}][{}] Failed to send PingAck to {}, cause: {}", localMember, period, - address, + addresses, ex.toString())); } @@ -275,10 +280,10 @@ private void onPingReq(Message message) { PingData pingReqData = new PingData(localMember, target, originalIssuer); Message pingMessage = Message.withData(pingReqData).qualifier(PING).correlationId(correlationId).build(); - Address address = target.address(); - LOGGER.debug("[{}][{}] Send transit Ping to {}", localMember, period, address); - transport - .send(address, pingMessage) + List
addresses = target.addresses(); + LOGGER.debug("[{}][{}] Send transit Ping to {}", localMember, period, addresses); + transportWrapper + .send(addresses, pingMessage) .subscribe( null, ex -> @@ -286,7 +291,7 @@ private void onPingReq(Message message) { "[{}][{}] Failed to send transit Ping to {}, cause: {}", localMember, period, - address, + addresses, ex.toString())); } @@ -305,10 +310,10 @@ private void onTransitPingAck(Message message) { PingData originalAckData = new PingData(target, data.getTo()).withAckType(ackType); Message originalAckMessage = Message.withData(originalAckData).qualifier(PING_ACK).correlationId(correlationId).build(); - Address address = target.address(); - LOGGER.debug("[{}][{}] Resend transit PingAck to {}", localMember, period, address); - transport - .send(address, originalAckMessage) + List
addresses = target.addresses(); + LOGGER.debug("[{}][{}] Resend transit PingAck to {}", localMember, period, addresses); + transportWrapper + .send(addresses, originalAckMessage) .subscribe( null, ex -> @@ -316,7 +321,7 @@ private void onTransitPingAck(Message message) { "[{}][{}] Failed to resend transit PingAck to {}, cause: {}", localMember, period, - address, + addresses, ex.toString())); } diff --git a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java index 2f24beb8..1406dad9 100644 --- a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java @@ -7,6 +7,7 @@ import io.scalecube.cluster.membership.MembershipEvent; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.net.Address; import java.util.ArrayList; import java.util.Arrays; @@ -287,14 +288,13 @@ private void spreadGossipsTo(long period, Member member) { } // Send gossip request - Address address = member.address(); + List
addresses = member.addresses(); gossips.stream() .map(this::buildGossipRequestMessage) .forEach( message -> - transport - .send(address, message) + TransportWrapper.send(transport, addresses, message) .subscribe( null, ex -> @@ -303,7 +303,7 @@ private void spreadGossipsTo(long period, Member member) { localMember, period, message, - address, + addresses, ex.toString()))); } diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index 53fed376..af16480e 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -15,6 +15,7 @@ import io.scalecube.cluster.metadata.MetadataStore; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.net.Address; import java.net.InetAddress; import java.nio.ByteBuffer; @@ -39,7 +40,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.Disposable; @@ -80,6 +80,8 @@ private enum MembershipUpdateReason { private final GossipProtocol gossipProtocol; private final MetadataStore metadataStore; + private final TransportWrapper transportWrapper; + // State private final Map membershipTable = new HashMap<>(); @@ -127,6 +129,8 @@ public MembershipProtocolImpl( this.membershipConfig = Objects.requireNonNull(config).membershipConfig(); this.failureDetectorConfig = Objects.requireNonNull(config).failureDetectorConfig(); + this.transportWrapper = new TransportWrapper(this.transport); + // Prepare seeds seedMembers = cleanUpSeedMembers(membershipConfig.seedMembers()); @@ -170,24 +174,31 @@ private List
cleanUpSeedMembers(Collection
seedMembers) { String hostAddress = localIpAddress.getHostAddress(); String hostName = localIpAddress.getHostName(); - Address memberAddr = localMember.address(); Address transportAddr = transport.address(); - Address memberAddrByHostAddress = Address.create(hostAddress, memberAddr.port()); Address transportAddrByHostAddress = Address.create(hostAddress, transportAddr.port()); - Address memberAddByHostName = Address.create(hostName, memberAddr.port()); Address transportAddrByHostName = Address.create(hostName, transportAddr.port()); return new LinkedHashSet<>(seedMembers) .stream() - .filter(addr -> checkAddressesNotEqual(addr, memberAddr)) + .filter(addr -> checkAddressesNotEqual(addr, localMember, hostAddress, hostName)) .filter(addr -> checkAddressesNotEqual(addr, transportAddr)) - .filter(addr -> checkAddressesNotEqual(addr, memberAddrByHostAddress)) .filter(addr -> checkAddressesNotEqual(addr, transportAddrByHostAddress)) - .filter(addr -> checkAddressesNotEqual(addr, memberAddByHostName)) .filter(addr -> checkAddressesNotEqual(addr, transportAddrByHostName)) .collect(Collectors.toList()); } + private boolean checkAddressesNotEqual( + Address smAddress, Member localMember, String hostAddress, String hostName) { + return localMember.addresses().stream() + .allMatch( + memberAddress -> + checkAddressesNotEqual(smAddress, memberAddress) + && checkAddressesNotEqual( + smAddress, Address.create(hostAddress, memberAddress.port())) + && checkAddressesNotEqual( + smAddress, Address.create(hostName, memberAddress.port()))); + } + private boolean checkAddressesNotEqual(Address address0, Address address1) { if (!address0.equals(address1)) { return true; @@ -330,27 +341,29 @@ public Optional member(String id) { @Override public Optional member(Address address) { - return new ArrayList<>(members.values()) - .stream().filter(member -> member.address().equals(address)).findFirst(); + return members.values().stream() + .filter(member -> member.addresses().stream().anyMatch(address::equals)) + .findFirst(); } private void doSync() { - Address address = selectSyncAddress().orElse(null); - if (address == null) { + List
addresses = selectSyncAddress(); + + if (addresses.isEmpty()) { return; } Message message = prepareSyncDataMsg(SYNC, null); - LOGGER.debug("[{}][doSync] Send Sync to {}", localMember, address); - transport - .send(address, message) + LOGGER.debug("[{}][doSync] Send Sync to {}", localMember, addresses); + transportWrapper + .send(addresses, message) .subscribe( null, ex -> LOGGER.debug( "[{}][doSync] Failed to send Sync to {}, cause: {}", localMember, - address, + addresses, ex.toString())); } @@ -394,13 +407,13 @@ private Mono onSyncAck(Message syncAckMsg, boolean onStart) { private Mono onSync(Message syncMsg) { return Mono.defer( () -> { - final Address sender = syncMsg.sender(); + final List
sender = syncMsg.sender(); LOGGER.debug("[{}] Received Sync from {}", localMember, sender); return syncMembership(syncMsg.data(), false) .doOnSuccess( avoid -> { Message message = prepareSyncDataMsg(SYNC_ACK, syncMsg.correlationId()); - transport + transportWrapper .send(sender, message) .subscribe( null, @@ -429,16 +442,16 @@ private void onFailureDetectorEvent(FailureDetectorEvent fdEvent) { // Alive won't override SUSPECT so issue instead extra sync with member to force it spread // alive with inc + 1 Message syncMsg = prepareSyncDataMsg(SYNC, null); - Address address = fdEvent.member().address(); - transport - .send(address, syncMsg) + List
addresses = fdEvent.member().addresses(); + transportWrapper + .send(addresses, syncMsg) .subscribe( null, ex -> LOGGER.debug( "[{}][onFailureDetectorEvent] Failed to send Sync to {}, cause: {}", localMember, - address, + addresses, ex.toString())); } else { MembershipRecord record = @@ -468,17 +481,24 @@ private void onMembershipGossip(Message message) { } } - private Optional
selectSyncAddress() { - List
addresses = - Stream.concat(seedMembers.stream(), otherMembers().stream().map(Member::address)) - .collect(Collectors.collectingAndThen(Collectors.toSet(), ArrayList::new)); - Collections.shuffle(addresses); - if (addresses.isEmpty()) { - return Optional.empty(); - } else { - int i = ThreadLocalRandom.current().nextInt(addresses.size()); - return Optional.of(addresses.get(i)); + private List
selectSyncAddress() { + Collection otherMembers = otherMembers(); + + if (seedMembers.isEmpty() && otherMembers.isEmpty()) { + return Collections.emptyList(); + } + + int totalSize = seedMembers.size() + otherMembers.size(); + int randomIndex = ThreadLocalRandom.current().nextInt(totalSize); + + if (randomIndex < seedMembers.size()) { + return Collections.singletonList(seedMembers.get(randomIndex)); } + + List otherMembersList = new ArrayList<>(otherMembers); + Member member = otherMembersList.get(randomIndex - seedMembers.size()); + + return member.addresses(); } // ================================================ @@ -593,7 +613,7 @@ private Mono updateMembership(MembershipRecord r1, MembershipUpdateReason } // If received updated for local member then increase incarnation and spread Alive gossip - if (r1.member().address().equals(localMember.address())) { + if (r1.member().addresses().equals(localMember.addresses())) { if (r1.member().id().equals(localMember.id())) { return onSelfMemberDetected(r0, r1, reason); } else { diff --git a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java index 35ba5328..1ace90eb 100644 --- a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java @@ -4,10 +4,12 @@ import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.net.Address; import java.nio.ByteBuffer; import java.time.Duration; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -37,6 +39,8 @@ public class MetadataStoreImpl implements MetadataStore { private final Transport transport; private final ClusterConfig config; + private final TransportWrapper transportWrapper; + // State private final Map membersMetadata = new HashMap<>(); @@ -69,6 +73,8 @@ public MetadataStoreImpl( this.config = Objects.requireNonNull(config); this.scheduler = Objects.requireNonNull(scheduler); this.localMetadata = localMetadata; // optional + + this.transportWrapper = new TransportWrapper(this.transport); } @Override @@ -148,7 +154,6 @@ public Mono fetchMetadata(Member member) { return Mono.defer( () -> { final String cid = UUID.randomUUID().toString(); - final Address targetAddress = member.address(); LOGGER.debug("[{}][{}] Getting metadata for member {}", localMember, cid, member); @@ -159,17 +164,18 @@ public Mono fetchMetadata(Member member) { .data(new GetMetadataRequest(member)) .build(); - return transport - .requestResponse(targetAddress, request) + // TODO. Make transport abstraction around this logic + + List
addresses = member.addresses(); + + return transportWrapper + .requestResponse(addresses, request) .timeout(Duration.ofMillis(config.metadataTimeout()), scheduler) .publishOn(scheduler) .doOnSuccess( s -> LOGGER.debug( - "[{}][{}] Received GetMetadataResp from {}", - localMember, - cid, - targetAddress)) + "[{}][{}] Received GetMetadataResp from {}", localMember, cid, addresses)) .map(Message::data) .map(GetMetadataResponse::getMetadata) .doOnError( @@ -179,7 +185,7 @@ public Mono fetchMetadata(Member member) { + "from {} within {} ms, cause: {}", localMember, cid, - targetAddress, + addresses, config.metadataTimeout(), th.toString())); }); @@ -196,7 +202,7 @@ private void onMessage(Message message) { } private void onMetadataRequest(Message message) { - final Address sender = message.sender(); + final List
sender = message.sender(); // TODO. Log LOGGER.debug("[{}] Received GetMetadataReq from {}", localMember, sender); GetMetadataRequest reqData = message.data(); @@ -224,7 +230,7 @@ private void onMetadataRequest(Message message) { .build(); LOGGER.debug("[{}] Send GetMetadataResp to {}", localMember, sender); - transport + transportWrapper .send(sender, response) .subscribe( null, diff --git a/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java b/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java index bbcbd1ab..6c41fd3e 100644 --- a/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java @@ -66,14 +66,14 @@ public void testSeparateEmptyNamespaces() { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("root1")) - .membership(opts -> opts.seedMembers(root.address())) + .membership(opts -> opts.seedMembers(root.addresses())) .startAwait(); Cluster root2 = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("root2")) - .membership(opts -> opts.seedMembers(root.address())) + .membership(opts -> opts.seedMembers(root.addresses())) .startAwait(); assertThat(root.otherMembers(), iterableWithSize(0)); @@ -93,21 +93,21 @@ public void testSeparateNonEmptyNamespaces() { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("root")) - .membership(opts -> opts.seedMembers(root.address())) + .membership(opts -> opts.seedMembers(root.addresses())) .startAwait(); Cluster carol = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("root")) - .membership(opts -> opts.seedMembers(root.address(), bob.address())) + .membership(opts -> opts.seedMembers(root.addresses().get(0), bob.addresses().get(0))) .startAwait(); Cluster root2 = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("root2")) - .membership(opts -> opts.seedMembers(root.address())) + .membership(opts -> opts.seedMembers(root.addresses())) .startAwait(); Cluster dan = @@ -117,7 +117,7 @@ public void testSeparateNonEmptyNamespaces() { .membership( opts -> opts.seedMembers( - root.address(), root2.address(), bob.address(), carol.address())) + root.addresses().get(0), root2.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0))) .startAwait(); Cluster eve = @@ -127,11 +127,11 @@ public void testSeparateNonEmptyNamespaces() { .membership( opts -> opts.seedMembers( - root.address(), - root2.address(), - dan.address(), - bob.address(), - carol.address())) + root.addresses().get(0), + root2.addresses().get(0), + dan.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0))) .startAwait(); assertThat(root.otherMembers(), containsInAnyOrder(bob.member(), carol.member())); @@ -155,14 +155,14 @@ public void testSimpleNamespacesHierarchy() { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("develop/develop")) - .membership(opts -> opts.seedMembers(rootDevelop.address())) + .membership(opts -> opts.seedMembers(rootDevelop.addresses())) .startAwait(); Cluster carol = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("develop/develop")) - .membership(opts -> opts.seedMembers(rootDevelop.address(), bob.address())) + .membership(opts -> opts.seedMembers(rootDevelop.addresses().get(0), bob.addresses().get(0))) .startAwait(); Cluster dan = @@ -170,7 +170,7 @@ public void testSimpleNamespacesHierarchy() { .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("develop/develop-2")) .membership( - opts -> opts.seedMembers(rootDevelop.address(), bob.address(), carol.address())) + opts -> opts.seedMembers(rootDevelop.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0))) .startAwait(); Cluster eve = @@ -180,7 +180,7 @@ public void testSimpleNamespacesHierarchy() { .membership( opts -> opts.seedMembers( - rootDevelop.address(), bob.address(), carol.address(), dan.address())) + rootDevelop.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0), dan.addresses().get(0))) .startAwait(); assertThat( @@ -206,14 +206,14 @@ public void testIsolatedParentNamespaces() { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("a/1/c")) - .membership(opts -> opts.seedMembers(parent1.address())) + .membership(opts -> opts.seedMembers(parent1.addresses())) .startAwait(); Cluster carol = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("a/1/c")) - .membership(opts -> opts.seedMembers(parent1.address(), bob.address())) + .membership(opts -> opts.seedMembers(parent1.addresses().get(0), bob.addresses().get(0))) .startAwait(); Cluster parent2 = @@ -229,7 +229,7 @@ public void testIsolatedParentNamespaces() { .membership( opts -> opts.seedMembers( - parent1.address(), parent2.address(), bob.address(), carol.address())) + parent1.addresses().get(0), parent2.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0))) .startAwait(); //noinspection unused @@ -240,11 +240,11 @@ public void testIsolatedParentNamespaces() { .membership( opts -> opts.seedMembers( - parent1.address(), - parent2.address(), - bob.address(), - carol.address(), - dan.address())) + parent1.addresses().get(0), + parent2.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0), + dan.addresses().get(0))) .startAwait(); assertThat(parent1.otherMembers(), containsInAnyOrder(bob.member(), carol.member())); diff --git a/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java b/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java index b99f6189..906d7e2f 100644 --- a/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java @@ -92,7 +92,7 @@ public void testMembersAccessFromScheduler() { Cluster seedNode = new ClusterImpl().transportFactory(TcpTransportFactory::new).startAwait(); Cluster otherNode = new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -101,8 +101,8 @@ public void testMembersAccessFromScheduler() { // Members by address - Optional otherNodeOnSeedNode = seedNode.member(otherNode.address()); - Optional seedNodeOnOtherNode = otherNode.member(seedNode.address()); + Optional otherNodeOnSeedNode = seedNode.member(otherNode.addresses().get(0)); + Optional seedNodeOnOtherNode = otherNode.member(seedNode.addresses().get(0)); assertEquals(otherNode.member(), otherNodeOnSeedNode.orElse(null)); assertEquals(seedNode.member(), seedNodeOnOtherNode.orElse(null)); @@ -181,7 +181,7 @@ public void testJoinDynamicPort() { for (int i = 0; i < membersNum; i++) { otherNodes.add( new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait()); } @@ -212,7 +212,7 @@ public void testUpdateMetadata() throws Exception { metadataNode = new ClusterImpl() .config(opts -> opts.metadata(metadata)) - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -221,7 +221,7 @@ public void testUpdateMetadata() throws Exception { .flatMap( integer -> new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> @@ -285,7 +285,7 @@ public void testUpdateMetadataProperty() throws Exception { metadataNode = new ClusterImpl() .config(opts -> opts.metadata(metadata)) - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -294,7 +294,7 @@ public void testUpdateMetadataProperty() throws Exception { .flatMap( integer -> new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> @@ -363,7 +363,7 @@ public void testRemoveMetadataProperty() throws Exception { metadataNode = new ClusterImpl() .config(opts -> opts.metadata(metadata)) - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -372,7 +372,7 @@ public void testRemoveMetadataProperty() throws Exception { .flatMap( integer -> new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> @@ -452,19 +452,19 @@ public void onMembershipEvent(MembershipEvent event) { // Start nodes final Cluster node1 = new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler(cluster -> listener) .startAwait(); final Cluster node2 = new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler(cluster -> listener) .startAwait(); final Cluster node3 = new ClusterImpl() - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler(cluster -> listener) .startAwait(); @@ -506,7 +506,7 @@ public void onMembershipEvent(MembershipEvent event) { final Cluster node1 = new ClusterImpl() .config(opts -> opts.metadata(node1Metadata)) - .membership(opts -> opts.seedMembers(seedNode.address())) + .membership(opts -> opts.seedMembers(seedNode.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> @@ -559,9 +559,11 @@ public void testJoinSeedClusterWithNoExistingSeedMember() { // Start seed node Cluster seedNode = new ClusterImpl().transportFactory(TcpTransportFactory::new).startAwait(); - Address nonExistingSeed1 = Address.from("localhost:1234"); - Address nonExistingSeed2 = Address.from("localhost:5678"); - Address[] seeds = new Address[] {nonExistingSeed1, nonExistingSeed2, seedNode.address()}; + List
seeds = new ArrayList<>(); + + seeds.add(Address.from("localhost:1234")); // Not existent + seeds.add(Address.from("localhost:5678")); // Not existent + seeds.addAll(seedNode.addresses()); Cluster otherNode = new ClusterImpl() diff --git a/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java b/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java index 316f814e..bf762570 100644 --- a/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java @@ -445,7 +445,8 @@ private void assertStatus( events.stream() .filter(event -> event.status() == status) .map(FailureDetectorEvent::member) - .map(Member::address) + .map(Member::addresses) + .flatMap(Collection::stream) .collect(Collectors.toList()); String msg1 = @@ -472,7 +473,8 @@ private Future> listenNextEventFor( List> resultFuture = new ArrayList<>(); for (final Address member : addresses) { final CompletableFuture future = new CompletableFuture<>(); - fd.listen().filter(event -> event.member().address() == member).subscribe(future::complete); + fd.listen() + .filter(event -> event.member().addresses().contains(member)).subscribe(future::complete); resultFuture.add(future); } diff --git a/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java b/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java index ef559256..0678bd45 100644 --- a/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java @@ -750,19 +750,19 @@ public void testOverrideMemberAddress() throws UnknownHostException { NetworkEmulatorTransport e = createTransport(); MembershipProtocolImpl cmA = - createMembership(a, testConfig(Collections.emptyList()).externalHost(localAddress)); + createMembership(a, testConfig(Collections.emptyList()).externalHosts(localAddress)); MembershipProtocolImpl cmB = createMembership( - b, testConfig(Collections.singletonList(a.address())).externalHost(localAddress)); + b, testConfig(Collections.singletonList(a.address())).externalHosts(localAddress)); MembershipProtocolImpl cmC = createMembership( - c, testConfig(Collections.singletonList(a.address())).externalHost(localAddress)); + c, testConfig(Collections.singletonList(a.address())).externalHosts(localAddress)); MembershipProtocolImpl cmD = createMembership( - d, testConfig(Collections.singletonList(b.address())).externalHost(localAddress)); + d, testConfig(Collections.singletonList(b.address())).externalHosts(localAddress)); MembershipProtocolImpl cmE = createMembership( - e, testConfig(Collections.singletonList(b.address())).externalHost(localAddress)); + e, testConfig(Collections.singletonList(b.address())).externalHosts(localAddress)); try { awaitSeconds(3); diff --git a/examples/src/main/java/io/scalecube/examples/ClusterJoinExamples.java b/examples/src/main/java/io/scalecube/examples/ClusterJoinExamples.java index cff607f2..b2312f15 100644 --- a/examples/src/main/java/io/scalecube/examples/ClusterJoinExamples.java +++ b/examples/src/main/java/io/scalecube/examples/ClusterJoinExamples.java @@ -30,7 +30,7 @@ public static void main(String[] args) { Cluster bob = new ClusterImpl() .config(opts -> opts.memberAlias("Bob")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -39,7 +39,7 @@ public static void main(String[] args) { Cluster carol = new ClusterImpl() .config(opts -> opts.memberAlias("Carol").metadata(metadata)) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -47,7 +47,7 @@ public static void main(String[] args) { ClusterConfig configWithFixedPort = new ClusterConfig() .memberAlias("Dan") - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transport(opts -> opts.port(3000)); Cluster dan = new ClusterImpl(configWithFixedPort) @@ -61,10 +61,10 @@ public static void main(String[] args) { .membership( opts -> opts.seedMembers( - alice.address(), - bob.address(), - carol.address(), - dan.address()) // won't join anyway + alice.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0), + dan.addresses().get(0)) // won't join anyway .namespace("another-cluster")); Cluster eve = new ClusterImpl(configWithSyncGroup) @@ -75,31 +75,31 @@ public static void main(String[] args) { System.out.println( "Alice (" - + alice.address() + + alice.addresses() + ") cluster: " + alice.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Bob (" - + bob.address() + + bob.addresses() + ") cluster: " + bob.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Carol (" - + carol.address() + + carol.addresses() + ") cluster: " + carol.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Dan (" - + dan.address() + + dan.addresses() + ") cluster: " + dan.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Eve (" - + eve.address() + + eve.addresses() + ") cluster: " // alone in cluster + eve.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); } diff --git a/examples/src/main/java/io/scalecube/examples/ClusterJoinNamespacesExamples.java b/examples/src/main/java/io/scalecube/examples/ClusterJoinNamespacesExamples.java index bb1910b1..611c5cfa 100644 --- a/examples/src/main/java/io/scalecube/examples/ClusterJoinNamespacesExamples.java +++ b/examples/src/main/java/io/scalecube/examples/ClusterJoinNamespacesExamples.java @@ -24,7 +24,7 @@ public static void main(String[] args) { new ClusterImpl() .config(opts -> opts.memberAlias("Bob")) .membership(opts -> opts.namespace("alice/bob-and-carol")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -33,7 +33,7 @@ public static void main(String[] args) { new ClusterImpl() .config(opts -> opts.memberAlias("Carol")) .membership(opts -> opts.namespace("alice/bob-and-carol")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -41,7 +41,7 @@ public static void main(String[] args) { new ClusterImpl() .config(opts -> opts.memberAlias("Bob-and-Carol-Child-1")) .membership(opts -> opts.namespace("alice/bob-and-carol/child-1")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -49,7 +49,7 @@ public static void main(String[] args) { new ClusterImpl() .config(opts -> opts.memberAlias("Bob-and-Carol-Child-2")) .membership(opts -> opts.namespace("alice/bob-and-carol/child-2")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -58,7 +58,7 @@ public static void main(String[] args) { new ClusterImpl() .config(opts -> opts.memberAlias("Dan")) .membership(opts -> opts.namespace("alice/dan-and-eve")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -67,7 +67,7 @@ public static void main(String[] args) { new ClusterImpl() .config(opts -> opts.memberAlias("Eve")) .membership(opts -> opts.namespace("alice/dan-and-eve")) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); @@ -75,37 +75,37 @@ public static void main(String[] args) { System.out.println( "Alice (" - + alice.address() + + alice.addresses() + ") cluster: " + alice.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Bob (" - + bob.address() + + bob.addresses() + ") cluster: " + bob.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Carol (" - + carol.address() + + carol.addresses() + ") cluster: " + carol.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Dan (" - + dan.address() + + dan.addresses() + ") cluster: " + dan.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Eve (" - + eve.address() + + eve.addresses() + ") cluster: " // alone in cluster + eve.members().stream().map(Member::toString).collect(joining("\n", "\n", "\n"))); System.out.println( "Bob-And-Carol-Child-1 (" - + bobAndCarolChild1.address() + + bobAndCarolChild1.addresses() + ") cluster: " // alone in cluster + bobAndCarolChild1.members().stream() .map(Member::toString) @@ -113,7 +113,7 @@ public static void main(String[] args) { System.out.println( "Bob-And-Carol-Child-2 (" - + carolChild2.address() + + carolChild2.addresses() + ") cluster: " // alone in cluster + carolChild2.members().stream() .map(Member::toString) diff --git a/examples/src/main/java/io/scalecube/examples/ClusterMetadataExample.java b/examples/src/main/java/io/scalecube/examples/ClusterMetadataExample.java index 95824c93..14ce4056 100644 --- a/examples/src/main/java/io/scalecube/examples/ClusterMetadataExample.java +++ b/examples/src/main/java/io/scalecube/examples/ClusterMetadataExample.java @@ -30,7 +30,7 @@ public static void main(String[] args) throws Exception { Cluster joe = new ClusterImpl() .config(opts -> opts.metadata(Collections.singletonMap("name", "Joe"))) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { diff --git a/examples/src/main/java/io/scalecube/examples/CustomMetadataEncodingExample.java b/examples/src/main/java/io/scalecube/examples/CustomMetadataEncodingExample.java index 90604932..6dd1f2fe 100644 --- a/examples/src/main/java/io/scalecube/examples/CustomMetadataEncodingExample.java +++ b/examples/src/main/java/io/scalecube/examples/CustomMetadataEncodingExample.java @@ -24,7 +24,7 @@ public static void main(String[] args) throws Exception { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .config(opts -> opts.metadataCodec(new LongMetadataCodec()).metadata(123L)) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .startAwait(); System.out.println( "[" + joe.member().id() + "] Joe's metadata: " + joe.metadata().orElse(null)); @@ -33,7 +33,7 @@ public static void main(String[] args) throws Exception { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .config(opts -> opts.metadataCodec(new LongMetadataCodec()).metadata(456L)) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .startAwait(); System.out.println( "[" + bob.member().id() + "] Bob's metadata: " + bob.metadata().orElse(null)); diff --git a/examples/src/main/java/io/scalecube/examples/GossipExample.java b/examples/src/main/java/io/scalecube/examples/GossipExample.java index 2135880c..d4919541 100644 --- a/examples/src/main/java/io/scalecube/examples/GossipExample.java +++ b/examples/src/main/java/io/scalecube/examples/GossipExample.java @@ -34,7 +34,7 @@ public void onGossip(Message gossip) { //noinspection unused Cluster bob = new ClusterImpl() - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { @@ -50,7 +50,7 @@ public void onGossip(Message gossip) { //noinspection unused Cluster carol = new ClusterImpl() - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { @@ -66,7 +66,7 @@ public void onGossip(Message gossip) { //noinspection unused Cluster dan = new ClusterImpl() - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { @@ -82,7 +82,7 @@ public void onGossip(Message gossip) { // Start cluster node Eve that joins cluster and spreads gossip Cluster eve = new ClusterImpl() - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); eve.spreadGossip(Message.fromData("Gossip from Eve")) diff --git a/examples/src/main/java/io/scalecube/examples/MembershipEventsExample.java b/examples/src/main/java/io/scalecube/examples/MembershipEventsExample.java index 2040588f..b68bb3b0 100644 --- a/examples/src/main/java/io/scalecube/examples/MembershipEventsExample.java +++ b/examples/src/main/java/io/scalecube/examples/MembershipEventsExample.java @@ -47,7 +47,7 @@ public void onMembershipEvent(MembershipEvent event) { new ClusterImpl() .config(opts -> opts.memberAlias("Bob")) .config(opts -> opts.metadata(Collections.singletonMap("name", "Bob"))) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { @@ -66,7 +66,7 @@ public void onMembershipEvent(MembershipEvent event) { new ClusterImpl() .config(opts -> opts.memberAlias("Carol")) .config(opts -> opts.metadata(Collections.singletonMap("name", "Carol"))) - .membership(opts -> opts.seedMembers(bob.address())) + .membership(opts -> opts.seedMembers(bob.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { diff --git a/examples/src/main/java/io/scalecube/examples/MessagingExample.java b/examples/src/main/java/io/scalecube/examples/MessagingExample.java index b747b229..37dd445a 100644 --- a/examples/src/main/java/io/scalecube/examples/MessagingExample.java +++ b/examples/src/main/java/io/scalecube/examples/MessagingExample.java @@ -39,7 +39,7 @@ public void onMessage(Message msg) { // messages Cluster bob = new ClusterImpl() - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { @@ -58,7 +58,7 @@ public void onMessage(Message msg) { // Join cluster node Carol to cluster with Alice and Bob Cluster carol = new ClusterImpl() - .membership(opts -> opts.seedMembers(alice.address(), bob.address())) + .membership(opts -> opts.seedMembers(alice.addresses().get(0), bob.addresses().get(0))) .transportFactory(TcpTransportFactory::new) .handler( cluster -> { diff --git a/examples/src/main/java/io/scalecube/examples/WebsocketMessagingExample.java b/examples/src/main/java/io/scalecube/examples/WebsocketMessagingExample.java index 7049e1a1..cb888573 100644 --- a/examples/src/main/java/io/scalecube/examples/WebsocketMessagingExample.java +++ b/examples/src/main/java/io/scalecube/examples/WebsocketMessagingExample.java @@ -40,7 +40,7 @@ public void onMessage(Message msg) { Cluster bob = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) - .membership(opts -> opts.seedMembers(alice.address())) + .membership(opts -> opts.seedMembers(alice.addresses())) .handler( cluster -> { return new ClusterMessageHandler() { @@ -59,15 +59,13 @@ public void onMessage(Message msg) { Cluster carol = new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) - .membership(opts -> opts.seedMembers(alice.address(), bob.address())) + .membership(opts -> opts.seedMembers(alice.addresses().get(0), bob.addresses().get(0))) .handler( - cluster -> { - return new ClusterMessageHandler() { - @Override - public void onMessage(Message msg) { - System.out.println("Carol received: " + msg.data()); - } - }; + cluster -> new ClusterMessageHandler() { + @Override + public void onMessage(Message msg) { + System.out.println("Carol received: " + msg.data()); + } }) .startAwait(); diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java index b5e3e879..e9a3c7a9 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java @@ -5,13 +5,15 @@ import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import java.util.Optional; import java.util.StringJoiner; +import java.util.stream.Collectors; /** * The Class Message introduces generic protocol used for point to point communication by transport. @@ -36,7 +38,8 @@ public final class Message implements Externalizable { * This header represents sender address of type {@link Address}. It's an address of message * originator. This header is optional. */ - public static final String HEADER_SENDER = "sender"; + public static final String HEADER_SENDER = + "sender"; // TODO. Value should be list of addresses (comma separated) private Map headers = Collections.emptyMap(); private Object data; @@ -190,8 +193,17 @@ public T data() { * * @return address */ - public Address sender() { - return Optional.ofNullable(header(HEADER_SENDER)).map(Address::from).orElse(null); + public List
sender() { + String headerValue = header(HEADER_SENDER); + + if (headerValue == null) { + return Collections.emptyList(); + } + + return Arrays.stream(headerValue.split(",")) + .map(String::trim) // Removes leading and trailing spaces. + .map(Address::from) + .collect(Collectors.toList()); } @Override @@ -281,8 +293,16 @@ public Builder correlationId(String correlationId) { return header(HEADER_CORRELATION_ID, correlationId); } - public Builder sender(Address sender) { - return header(HEADER_SENDER, sender.toString()); + /** + * Setter for header. + * + * @param addresses addresses + * @return builder + */ + public Builder sender(List
addresses) { + return header( + HEADER_SENDER, + addresses.stream().map(Address::toString).collect(Collectors.joining(","))); } public Message build() { diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java new file mode 100644 index 00000000..2d025a8b --- /dev/null +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java @@ -0,0 +1,53 @@ +package io.scalecube.cluster.transport.api; + +import io.scalecube.net.Address; +import java.util.List; +import reactor.core.publisher.Mono; + +public class TransportWrapper { + + private final Transport transport; + + public TransportWrapper(Transport transport) { + this.transport = transport; + } + + public Mono requestResponse(List
addresses, Message request) { + return requestResponse(transport, addresses, 0, request); + } + + public static Mono requestResponse( + Transport transport, List
addresses, Message request) { + return requestResponse(transport, addresses, 0, request); + } + + private static Mono requestResponse( + Transport transport, List
addresses, int currentIndex, Message request) { + if (currentIndex >= addresses.size()) { + return Mono.error(new RuntimeException("All addresses have been tried and failed.")); + } + + return transport + .requestResponse(addresses.get(currentIndex), request) + .onErrorResume(th -> requestResponse(transport, addresses, currentIndex + 1, request)); + } + + public Mono send(List
addresses, Message request) { + return send(transport, addresses, 0, request); + } + + public static Mono send(Transport transport, List
addresses, Message request) { + return send(transport, addresses, 0, request); + } + + private static Mono send( + Transport transport, List
addresses, int currentIndex, Message request) { + if (currentIndex >= addresses.size()) { + return Mono.error(new RuntimeException("All addresses have been tried and failed.")); + } + + return transport + .send(addresses.get(currentIndex), request) + .onErrorResume(th -> send(transport, addresses, currentIndex + 1, request)); + } +} diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java index 3d07e84d..3f24d0bd 100644 --- a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java +++ b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java @@ -3,11 +3,14 @@ import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; import io.scalecube.cluster.transport.api.TransportConfig; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.cluster.utils.NetworkEmulatorTransport; import io.scalecube.net.Address; import io.scalecube.transport.netty.tcp.TcpTransportFactory; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.time.Duration; +import java.util.List; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; @@ -15,6 +18,8 @@ import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; +import javax.sql.rowset.spi.TransactionalWriter; + /** Base test class. */ public class BaseTest { @@ -50,6 +55,25 @@ protected Mono send(Transport transport, Address to, Message msg) { th.toString())); } + /** + * Sending message from src to destination. + * + * @param transport src + * @param to destinations + * @param msg request + */ + protected Mono send(Transport transport, List
to, Message msg) { + return TransportWrapper.send(transport, to, msg) + .doOnError( + th -> + LOGGER.error( + "Failed to send {} to {} from transport: {}, cause: {}", + msg, + to, + transport, + th.toString())); + } + /** * Stopping transport. * diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java index f8bb8daa..2bd14e96 100644 --- a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java +++ b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.fail; import io.scalecube.cluster.transport.api.Message; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.cluster.utils.NetworkEmulatorTransport; import io.scalecube.net.Address; import io.scalecube.transport.netty.BaseTest; @@ -94,9 +95,11 @@ public void testPingPongClientTfListenAndServerTfListen() throws Exception { .listen() .subscribe( message -> { - Address address = message.sender(); - assertEquals(client.address(), address, "Expected clientAddress"); - send(server, address, Message.fromQualifier("hi client")).subscribe(); + List
addresses = message.sender(); + assertTrue( + addresses.stream().anyMatch(a -> client.address().equals(a)), + "Expected clientAddress"); + send(server, addresses, Message.fromQualifier("hi client")).subscribe(); }); CompletableFuture messageFuture = new CompletableFuture<>(); @@ -145,8 +148,7 @@ public void testPingPongOnSingleChannel() throws Exception { messages -> { for (Message message : messages) { Message echo = Message.withData("echo/" + message.qualifier()).build(); - server - .send(message.sender(), echo) + TransportWrapper.send(server, message.sender(), echo) .subscribe(null, th -> LOGGER.error("Failed to send message", th)); } }); @@ -215,8 +217,7 @@ public void testPingPongOnSeparateChannel() throws Exception { messages -> { for (Message message : messages) { Message echo = Message.withData("echo/" + message.qualifier()).build(); - server - .send(message.sender(), echo) + TransportWrapper.send(server, message.sender(), echo) .subscribe(null, th -> LOGGER.error("Failed to send message", th)); } }); @@ -280,8 +281,7 @@ public void testObserverThrowsException() throws Exception { } if (qualifier.startsWith("q")) { Message echo = Message.withData("echo/" + message.qualifier()).build(); - server - .send(message.sender(), echo) + TransportWrapper.send(server, message.sender(), echo) .subscribe(null, th -> LOGGER.error("Failed to send message", th)); } }, @@ -320,7 +320,9 @@ public void testBlockAndUnblockTraffic() throws Exception { client = createTcpTransport(); server = createTcpTransport(); - server.listen().subscribe(message -> server.send(message.sender(), message).subscribe()); + server + .listen() + .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); Sinks.Many responses = Sinks.many().replay().all(); client diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java index 050474af..da9b00d3 100644 --- a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java +++ b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.fail; import io.scalecube.cluster.transport.api.Message; +import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.cluster.utils.NetworkEmulatorTransport; import io.scalecube.net.Address; import io.scalecube.transport.netty.BaseTest; @@ -94,9 +95,11 @@ public void testPingPongClientTfListenAndServerTfListen() throws Exception { .listen() .subscribe( message -> { - Address address = message.sender(); - assertEquals(client.address(), address, "Expected clientAddress"); - send(server, address, Message.fromQualifier("hi client")).subscribe(); + List
addresses = message.sender(); + assertTrue( + addresses.stream().anyMatch(a -> client.address().equals(a)), + "Expected clientAddress"); + send(server, addresses, Message.fromQualifier("hi client")).subscribe(); }); CompletableFuture messageFuture = new CompletableFuture<>(); @@ -145,8 +148,7 @@ public void testPingPongOnSingleChannel() throws Exception { messages -> { for (Message message : messages) { Message echo = Message.withData("echo/" + message.qualifier()).build(); - server - .send(message.sender(), echo) + TransportWrapper.send(server, message.sender(), echo) .subscribe(null, th -> LOGGER.error("Failed to send message", th)); } }); @@ -215,8 +217,7 @@ public void testPingPongOnSeparateChannel() throws Exception { messages -> { for (Message message : messages) { Message echo = Message.withData("echo/" + message.qualifier()).build(); - server - .send(message.sender(), echo) + TransportWrapper.send(server, message.sender(), echo) .subscribe(null, th -> LOGGER.error("Failed to send message", th)); } }); @@ -280,8 +281,7 @@ public void testObserverThrowsException() throws Exception { } if (qualifier.startsWith("q")) { Message echo = Message.withData("echo/" + message.qualifier()).build(); - server - .send(message.sender(), echo) + TransportWrapper.send(server, message.sender(), echo) .subscribe(null, th -> LOGGER.error("Failed to send message", th)); } }, @@ -320,7 +320,9 @@ public void testBlockAndUnblockTraffic() throws Exception { client = createWebsocketTransport(); server = createWebsocketTransport(); - server.listen().subscribe(message -> server.send(message.sender(), message).subscribe()); + server + .listen() + .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); Sinks.Many responses = Sinks.many().replay().all(); client From 2a94fc5eaf0c586d77a20041decd6736bc5fb5db Mon Sep 17 00:00:00 2001 From: "rostyslav.baldovskyi" Date: Wed, 30 Aug 2023 14:33:11 +0300 Subject: [PATCH 03/34] Fix comments, Improve error handler for updateMembership --- .../fdetector/FailureDetectorImpl.java | 19 +++++-------------- .../membership/MembershipProtocolImpl.java | 17 +++++------------ .../cluster/metadata/MetadataStoreImpl.java | 12 ++---------- .../cluster/transport/api/Message.java | 1 - .../transport/api/TransportWrapper.java | 16 +--------------- 5 files changed, 13 insertions(+), 52 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index 719437b5..462d0ab2 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -43,8 +43,6 @@ public final class FailureDetectorImpl implements FailureDetector { private final Transport transport; private final FailureDetectorConfig config; - private final TransportWrapper transportWrapper; - // State private final List pingMembers = new ArrayList<>(); @@ -84,8 +82,6 @@ public FailureDetectorImpl( this.config = Objects.requireNonNull(config); this.scheduler = Objects.requireNonNull(scheduler); - this.transportWrapper = new TransportWrapper(this.transport); - // Subscribe actionsDisposables.addAll( Arrays.asList( @@ -151,8 +147,7 @@ private void doPing() { LOGGER.debug("[{}][{}] Send Ping to {}", localMember, period, pingMember); List
addresses = pingMember.addresses(); - transportWrapper - .requestResponse(addresses, pingMsg) + TransportWrapper.requestResponse(transport, addresses, pingMsg) .timeout(Duration.ofMillis(config.pingTimeout()), scheduler) .publishOn(scheduler) .subscribe( @@ -194,8 +189,7 @@ private void doPingReq( Duration timeout = Duration.ofMillis(config.pingInterval() - config.pingTimeout()); pingReqMembers.forEach( member -> - transportWrapper - .requestResponse(member.addresses(), pingReqMsg) + TransportWrapper.requestResponse(transport, member.addresses(), pingReqMsg) .timeout(timeout, scheduler) .publishOn(scheduler) .subscribe( @@ -256,8 +250,7 @@ private void onPing(Message message) { Message.withData(data).qualifier(PING_ACK).correlationId(correlationId).build(); List
addresses = data.getFrom().addresses(); LOGGER.debug("[{}][{}] Send PingAck to {}", localMember, period, addresses); - transportWrapper - .send(addresses, ackMessage) + TransportWrapper.send(transport, addresses, ackMessage) .subscribe( null, ex -> @@ -282,8 +275,7 @@ private void onPingReq(Message message) { Message.withData(pingReqData).qualifier(PING).correlationId(correlationId).build(); List
addresses = target.addresses(); LOGGER.debug("[{}][{}] Send transit Ping to {}", localMember, period, addresses); - transportWrapper - .send(addresses, pingMessage) + TransportWrapper.send(transport, addresses, pingMessage) .subscribe( null, ex -> @@ -312,8 +304,7 @@ private void onTransitPingAck(Message message) { Message.withData(originalAckData).qualifier(PING_ACK).correlationId(correlationId).build(); List
addresses = target.addresses(); LOGGER.debug("[{}][{}] Resend transit PingAck to {}", localMember, period, addresses); - transportWrapper - .send(addresses, originalAckMessage) + TransportWrapper.send(transport, addresses, originalAckMessage) .subscribe( null, ex -> diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index af16480e..24de5ca3 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -80,8 +80,6 @@ private enum MembershipUpdateReason { private final GossipProtocol gossipProtocol; private final MetadataStore metadataStore; - private final TransportWrapper transportWrapper; - // State private final Map membershipTable = new HashMap<>(); @@ -129,8 +127,6 @@ public MembershipProtocolImpl( this.membershipConfig = Objects.requireNonNull(config).membershipConfig(); this.failureDetectorConfig = Objects.requireNonNull(config).failureDetectorConfig(); - this.transportWrapper = new TransportWrapper(this.transport); - // Prepare seeds seedMembers = cleanUpSeedMembers(membershipConfig.seedMembers()); @@ -355,8 +351,7 @@ private void doSync() { Message message = prepareSyncDataMsg(SYNC, null); LOGGER.debug("[{}][doSync] Send Sync to {}", localMember, addresses); - transportWrapper - .send(addresses, message) + TransportWrapper.send(transport, addresses, message) .subscribe( null, ex -> @@ -413,8 +408,7 @@ private Mono onSync(Message syncMsg) { .doOnSuccess( avoid -> { Message message = prepareSyncDataMsg(SYNC_ACK, syncMsg.correlationId()); - transportWrapper - .send(sender, message) + TransportWrapper.send(transport, sender, message) .subscribe( null, ex -> @@ -443,8 +437,7 @@ private void onFailureDetectorEvent(FailureDetectorEvent fdEvent) { // alive with inc + 1 Message syncMsg = prepareSyncDataMsg(SYNC, null); List
addresses = fdEvent.member().addresses(); - transportWrapper - .send(addresses, syncMsg) + TransportWrapper.send(transport, addresses, syncMsg) .subscribe( null, ex -> @@ -532,11 +525,11 @@ private Mono syncMembership(SyncData syncData, boolean onStart) { updateMembership(r1, reason) .doOnError( ex -> - LOGGER.warn( + LOGGER.error( "[{}][syncMembership][{}][error] cause: {}", localMember, reason, - ex.toString())) + ex)) .onErrorResume(ex -> Mono.empty())) .toArray(Mono[]::new); diff --git a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java index 1ace90eb..e9ebe785 100644 --- a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java @@ -39,8 +39,6 @@ public class MetadataStoreImpl implements MetadataStore { private final Transport transport; private final ClusterConfig config; - private final TransportWrapper transportWrapper; - // State private final Map membersMetadata = new HashMap<>(); @@ -73,8 +71,6 @@ public MetadataStoreImpl( this.config = Objects.requireNonNull(config); this.scheduler = Objects.requireNonNull(scheduler); this.localMetadata = localMetadata; // optional - - this.transportWrapper = new TransportWrapper(this.transport); } @Override @@ -164,12 +160,9 @@ public Mono fetchMetadata(Member member) { .data(new GetMetadataRequest(member)) .build(); - // TODO. Make transport abstraction around this logic - List
addresses = member.addresses(); - return transportWrapper - .requestResponse(addresses, request) + return TransportWrapper.requestResponse(transport, addresses, request) .timeout(Duration.ofMillis(config.metadataTimeout()), scheduler) .publishOn(scheduler) .doOnSuccess( @@ -230,8 +223,7 @@ private void onMetadataRequest(Message message) { .build(); LOGGER.debug("[{}] Send GetMetadataResp to {}", localMember, sender); - transportWrapper - .send(sender, response) + TransportWrapper.send(transport, sender, response) .subscribe( null, ex -> diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java index e9a3c7a9..d4fb5569 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java @@ -201,7 +201,6 @@ public List
sender() { } return Arrays.stream(headerValue.split(",")) - .map(String::trim) // Removes leading and trailing spaces. .map(Address::from) .collect(Collectors.toList()); } diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java index 2d025a8b..3bbe6d81 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java @@ -6,16 +6,6 @@ public class TransportWrapper { - private final Transport transport; - - public TransportWrapper(Transport transport) { - this.transport = transport; - } - - public Mono requestResponse(List
addresses, Message request) { - return requestResponse(transport, addresses, 0, request); - } - public static Mono requestResponse( Transport transport, List
addresses, Message request) { return requestResponse(transport, addresses, 0, request); @@ -24,7 +14,7 @@ public static Mono requestResponse( private static Mono requestResponse( Transport transport, List
addresses, int currentIndex, Message request) { if (currentIndex >= addresses.size()) { - return Mono.error(new RuntimeException("All addresses have been tried and failed.")); + return Mono.error(new RuntimeException("All addresses have been tried and failed")); } return transport @@ -32,10 +22,6 @@ private static Mono requestResponse( .onErrorResume(th -> requestResponse(transport, addresses, currentIndex + 1, request)); } - public Mono send(List
addresses, Message request) { - return send(transport, addresses, 0, request); - } - public static Mono send(Transport transport, List
addresses, Message request) { return send(transport, addresses, 0, request); } From 24093250a5e2a08c651cb34028c6c16c71f42b04 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Wed, 30 Aug 2023 21:40:15 +0300 Subject: [PATCH 04/34] Enabled logging :set back old versions for log4j and slf4j --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ee828b01..e21c8b82 100644 --- a/pom.xml +++ b/pom.xml @@ -35,8 +35,8 @@ 1.0.24 - 2.0.7 - 2.20.0 + 1.7.36 + 2.17.2 2020.0.32 2.15.1 From c38a23a1f808e8f24f85844549406bc2af384f74 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 13:19:55 +0300 Subject: [PATCH 05/34] WIP --- .../io/scalecube/cluster/ClusterImpl.java | 1 - .../scalecube/cluster}/TransportWrapper.java | 30 ++++++++++++++----- .../fdetector/FailureDetectorImpl.java | 2 +- .../cluster/gossip/GossipProtocolImpl.java | 2 +- .../membership/MembershipProtocolImpl.java | 2 +- .../cluster/metadata/MetadataStoreImpl.java | 2 +- .../scalecube/transport/netty/BaseTest.java | 2 -- 7 files changed, 27 insertions(+), 14 deletions(-) rename {transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api => cluster/src/main/java/io/scalecube/cluster}/TransportWrapper.java (51%) diff --git a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java index 656b4628..813724cb 100644 --- a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java @@ -16,7 +16,6 @@ import io.scalecube.cluster.transport.api.Transport; import io.scalecube.cluster.transport.api.TransportConfig; import io.scalecube.cluster.transport.api.TransportFactory; -import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.net.Address; import io.scalecube.utils.ServiceLoaderUtil; import java.io.Serializable; diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java similarity index 51% rename from transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java rename to cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 3bbe6d81..1b3c2e73 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -1,25 +1,41 @@ -package io.scalecube.cluster.transport.api; +package io.scalecube.cluster; +import io.scalecube.cluster.transport.api.Message; +import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import reactor.core.publisher.Mono; public class TransportWrapper { - public static Mono requestResponse( - Transport transport, List
addresses, Message request) { - return requestResponse(transport, addresses, 0, request); + private final Transport transport; + + private final Map connections = new ConcurrentHashMap<>(); + + public TransportWrapper(Transport transport) { + this.transport = transport; } - private static Mono requestResponse( - Transport transport, List
addresses, int currentIndex, Message request) { + public Mono requestResponse(Member member, Message request) { + // return requestResponse(transport, addresses, 0, request); + connections.computeIfAbsent(member, m -> requestResponse0(m, request)); + } + + private Address requestResponse0(Member member, Message request) { + return null; + } + + private Mono requestResponse( + List
addresses, int currentIndex, Message request) { if (currentIndex >= addresses.size()) { return Mono.error(new RuntimeException("All addresses have been tried and failed")); } return transport .requestResponse(addresses.get(currentIndex), request) - .onErrorResume(th -> requestResponse(transport, addresses, currentIndex + 1, request)); + .onErrorResume(th -> requestResponse(addresses, currentIndex + 1, request)); } public static Mono send(Transport transport, List
addresses, Message request) { diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index 462d0ab2..901ec7ad 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -8,7 +8,7 @@ import io.scalecube.cluster.membership.MembershipEvent; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.transport.api.TransportWrapper; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.time.Duration; import java.util.ArrayList; diff --git a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java index 1406dad9..d0a56767 100644 --- a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java @@ -7,7 +7,7 @@ import io.scalecube.cluster.membership.MembershipEvent; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.transport.api.TransportWrapper; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.util.ArrayList; import java.util.Arrays; diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index 24de5ca3..7c579ada 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -15,7 +15,7 @@ import io.scalecube.cluster.metadata.MetadataStore; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.transport.api.TransportWrapper; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.net.InetAddress; import java.nio.ByteBuffer; diff --git a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java index e9ebe785..747e70d0 100644 --- a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java @@ -4,7 +4,7 @@ import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.transport.api.TransportWrapper; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.nio.ByteBuffer; import java.time.Duration; diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java index 3f24d0bd..627d9f0b 100644 --- a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java +++ b/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java @@ -18,8 +18,6 @@ import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; -import javax.sql.rowset.spi.TransactionalWriter; - /** Base test class. */ public class BaseTest { From 95ea168490a05abfaeb346e5d441ac5b6467e1f9 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 14:12:17 +0300 Subject: [PATCH 06/34] WIP --- .../scalecube/cluster/TransportWrapper.java | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 1b3c2e73..081a68a1 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -6,36 +6,49 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import reactor.core.publisher.Mono; public class TransportWrapper { private final Transport transport; - private final Map connections = new ConcurrentHashMap<>(); + private final Map> connections = new ConcurrentHashMap<>(); public TransportWrapper(Transport transport) { this.transport = transport; } public Mono requestResponse(Member member, Message request) { - // return requestResponse(transport, addresses, 0, request); - connections.computeIfAbsent(member, m -> requestResponse0(m, request)); + return connections + .compute( + member, + (m, resultMono) -> { + if (resultMono == null) { + return requestResponse(member.addresses(), request); + } + return resultMono.flatMap( + result -> + transport + .requestResponse(result.address, request) + .map(message -> new Result(result.address, message))); + }) + .map(result -> result.message); } - private Address requestResponse0(Member member, Message request) { - return null; - } - - private Mono requestResponse( - List
addresses, int currentIndex, Message request) { - if (currentIndex >= addresses.size()) { - return Mono.error(new RuntimeException("All addresses have been tried and failed")); - } - - return transport - .requestResponse(addresses.get(currentIndex), request) - .onErrorResume(th -> requestResponse(addresses, currentIndex + 1, request)); + private Mono requestResponse(List
addresses, Message request) { + final AtomicInteger currentIndex = new AtomicInteger(); + return Mono.defer( + () -> { + final int index = currentIndex.getAndIncrement(); + return transport.requestResponse(addresses.get(index), request); + }) + .retry(addresses.size() - 1) + .map( + message -> { + final int index = currentIndex.get(); + return new Result(addresses.get(index), message); + }); } public static Mono send(Transport transport, List
addresses, Message request) { @@ -52,4 +65,19 @@ private static Mono send( .send(addresses.get(currentIndex), request) .onErrorResume(th -> send(transport, addresses, currentIndex + 1, request)); } + + private static class Result { + + private final Address address; + private final Message message; + + private Result(Address address) { + this(address, null); + } + + private Result(Address address, Message message) { + this.address = address; + this.message = message; + } + } } From 76d15767771f04cc214445a61aabfbe63eb8207d Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 14:17:50 +0300 Subject: [PATCH 07/34] Done with TransportWrapper --- .../scalecube/cluster/TransportWrapper.java | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 081a68a1..893d8cc9 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -36,6 +36,23 @@ public Mono requestResponse(Member member, Message request) { .map(result -> result.message); } + public Mono send(Member member, Message request) { + return connections + .compute( + member, + (m, resultMono) -> { + if (resultMono == null) { + return send(member.addresses(), request); + } + return resultMono.flatMap( + result -> + transport + .send(result.address, request) + .thenReturn(new Result(result.address))); + }) + .then(); + } + private Mono requestResponse(List
addresses, Message request) { final AtomicInteger currentIndex = new AtomicInteger(); return Mono.defer( @@ -51,19 +68,20 @@ private Mono requestResponse(List
addresses, Message request) { }); } - public static Mono send(Transport transport, List
addresses, Message request) { - return send(transport, addresses, 0, request); - } - - private static Mono send( - Transport transport, List
addresses, int currentIndex, Message request) { - if (currentIndex >= addresses.size()) { - return Mono.error(new RuntimeException("All addresses have been tried and failed.")); - } - - return transport - .send(addresses.get(currentIndex), request) - .onErrorResume(th -> send(transport, addresses, currentIndex + 1, request)); + private Mono send(List
addresses, Message request) { + final AtomicInteger currentIndex = new AtomicInteger(); + return Mono.defer( + () -> { + final int index = currentIndex.getAndIncrement(); + return transport.send(addresses.get(index), request); + }) + .retry(addresses.size() - 1) + .then( + Mono.fromCallable( + () -> { + final int index = currentIndex.get(); + return new Result(addresses.get(index)); + })); } private static class Result { From b8e42b43cfc2f6bbe0b28bf23ee5d9dfdf954b1c Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 14:31:23 +0300 Subject: [PATCH 08/34] Added cluster-tests --- .../cluster/utils/NetworkEmulator.java | 9 ++-- cluster-tests/pom.xml | 36 +++++++++++++++ .../scalecube/transport/netty/BaseTest.java | 17 ++++--- .../netty/tcp/TcpTransportSendOrderTest.java | 0 .../transport/netty/tcp/TcpTransportTest.java | 0 .../WebsocketTransportSendOrderTest.java | 0 .../websocket/WebsocketTransportTest.java | 0 .../src/test/resources/log4j2-test.xml | 0 .../scalecube/cluster/TransportWrapper.java | 44 ++++++++++++------- .../fdetector/FailureDetectorImpl.java | 2 +- .../cluster/gossip/GossipProtocolImpl.java | 2 +- .../membership/MembershipProtocolImpl.java | 2 +- .../cluster/metadata/MetadataStoreImpl.java | 2 +- .../cluster/ClusterNamespacesTest.java | 27 +++++++++--- .../io/scalecube/cluster/ClusterTest.java | 2 +- .../fdetector/FailureDetectorTest.java | 3 +- pom.xml | 5 ++- .../cluster/transport/api/Message.java | 8 ++-- 18 files changed, 113 insertions(+), 46 deletions(-) create mode 100644 cluster-tests/pom.xml rename {transport-parent/transport-netty => cluster-tests}/src/test/java/io/scalecube/transport/netty/BaseTest.java (91%) rename {transport-parent/transport-netty => cluster-tests}/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java (100%) rename {transport-parent/transport-netty => cluster-tests}/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java (100%) rename {transport-parent/transport-netty => cluster-tests}/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java (100%) rename {transport-parent/transport-netty => cluster-tests}/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java (100%) rename {transport-parent/transport-netty => cluster-tests}/src/test/resources/log4j2-test.xml (100%) diff --git a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java index d3e6ab3d..f3da9c66 100644 --- a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java +++ b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java @@ -244,10 +244,11 @@ public InboundSettings inboundSettings(List
destinations) { public void inboundSettings(List
destinations, boolean shallPass) { InboundSettings settings = new InboundSettings(shallPass); - destinations.forEach(destination -> { - inboundSettings.put(destination, settings); - LOGGER.debug("[{}] Set inbound settings {} to {}", address, settings, destination); - }); + destinations.forEach( + destination -> { + inboundSettings.put(destination, settings); + LOGGER.debug("[{}] Set inbound settings {} to {}", address, settings, destination); + }); } /** diff --git a/cluster-tests/pom.xml b/cluster-tests/pom.xml new file mode 100644 index 00000000..b63c0c6b --- /dev/null +++ b/cluster-tests/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + + io.scalecube + scalecube-cluster-parent + 2.6.18-SNAPSHOT + + + cluster-tests + + + + ${project.groupId} + scalecube-cluster-testlib + ${project.version} + + + ${project.groupId} + scalecube-transport-netty + ${project.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + + diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java similarity index 91% rename from transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java rename to cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java index 627d9f0b..84b7a100 100644 --- a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/BaseTest.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java @@ -10,7 +10,6 @@ import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.time.Duration; import java.util.List; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; @@ -62,14 +61,14 @@ protected Mono send(Transport transport, Address to, Message msg) { */ protected Mono send(Transport transport, List
to, Message msg) { return TransportWrapper.send(transport, to, msg) - .doOnError( - th -> - LOGGER.error( - "Failed to send {} to {} from transport: {}, cause: {}", - msg, - to, - transport, - th.toString())); + .doOnError( + th -> + LOGGER.error( + "Failed to send {} to {} from transport: {}, cause: {}", + msg, + to, + transport, + th.toString())); } /** diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java similarity index 100% rename from transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java rename to cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java similarity index 100% rename from transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java rename to cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java similarity index 100% rename from transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java rename to cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java diff --git a/transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java similarity index 100% rename from transport-parent/transport-netty/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java rename to cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java diff --git a/transport-parent/transport-netty/src/test/resources/log4j2-test.xml b/cluster-tests/src/test/resources/log4j2-test.xml similarity index 100% rename from transport-parent/transport-netty/src/test/resources/log4j2-test.xml rename to cluster-tests/src/test/resources/log4j2-test.xml diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 893d8cc9..6055f5b7 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -19,6 +19,13 @@ public TransportWrapper(Transport transport) { this.transport = transport; } + /** + * Execute request response call. + * + * @param member member + * @param request request + * @return mono result + */ public Mono requestResponse(Member member, Message request) { return connections .compute( @@ -36,6 +43,28 @@ public Mono requestResponse(Member member, Message request) { .map(result -> result.message); } + private Mono requestResponse(List
addresses, Message request) { + final AtomicInteger currentIndex = new AtomicInteger(); + return Mono.defer( + () -> { + final int index = currentIndex.getAndIncrement(); + return transport.requestResponse(addresses.get(index), request); + }) + .retry(addresses.size() - 1) + .map( + message -> { + final int index = currentIndex.get(); + return new Result(addresses.get(index), message); + }); + } + + /** + * Execute send call. + * + * @param member member + * @param request request + * @return mono result + */ public Mono send(Member member, Message request) { return connections .compute( @@ -53,21 +82,6 @@ public Mono send(Member member, Message request) { .then(); } - private Mono requestResponse(List
addresses, Message request) { - final AtomicInteger currentIndex = new AtomicInteger(); - return Mono.defer( - () -> { - final int index = currentIndex.getAndIncrement(); - return transport.requestResponse(addresses.get(index), request); - }) - .retry(addresses.size() - 1) - .map( - message -> { - final int index = currentIndex.get(); - return new Result(addresses.get(index), message); - }); - } - private Mono send(List
addresses, Message request) { final AtomicInteger currentIndex = new AtomicInteger(); return Mono.defer( diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index 901ec7ad..81204b3f 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -3,12 +3,12 @@ import static io.scalecube.reactor.RetryNonSerializedEmitFailureHandler.RETRY_NON_SERIALIZED; import io.scalecube.cluster.Member; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.cluster.fdetector.PingData.AckType; import io.scalecube.cluster.membership.MemberStatus; import io.scalecube.cluster.membership.MembershipEvent; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.time.Duration; import java.util.ArrayList; diff --git a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java index d0a56767..e3c36caa 100644 --- a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java @@ -4,10 +4,10 @@ import io.scalecube.cluster.ClusterMath; import io.scalecube.cluster.Member; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.cluster.membership.MembershipEvent; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.util.ArrayList; import java.util.Arrays; diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index 7c579ada..c021d710 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -8,6 +8,7 @@ import io.scalecube.cluster.ClusterConfig; import io.scalecube.cluster.ClusterMath; import io.scalecube.cluster.Member; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.cluster.fdetector.FailureDetector; import io.scalecube.cluster.fdetector.FailureDetectorConfig; import io.scalecube.cluster.fdetector.FailureDetectorEvent; @@ -15,7 +16,6 @@ import io.scalecube.cluster.metadata.MetadataStore; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.net.InetAddress; import java.nio.ByteBuffer; diff --git a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java index 747e70d0..499a4bff 100644 --- a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java @@ -2,9 +2,9 @@ import io.scalecube.cluster.ClusterConfig; import io.scalecube.cluster.Member; +import io.scalecube.cluster.TransportWrapper; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.TransportWrapper; import io.scalecube.net.Address; import java.nio.ByteBuffer; import java.time.Duration; diff --git a/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java b/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java index 6c41fd3e..000dbedf 100644 --- a/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/ClusterNamespacesTest.java @@ -117,7 +117,10 @@ public void testSeparateNonEmptyNamespaces() { .membership( opts -> opts.seedMembers( - root.addresses().get(0), root2.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0))) + root.addresses().get(0), + root2.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0))) .startAwait(); Cluster eve = @@ -162,7 +165,8 @@ public void testSimpleNamespacesHierarchy() { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("develop/develop")) - .membership(opts -> opts.seedMembers(rootDevelop.addresses().get(0), bob.addresses().get(0))) + .membership( + opts -> opts.seedMembers(rootDevelop.addresses().get(0), bob.addresses().get(0))) .startAwait(); Cluster dan = @@ -170,7 +174,11 @@ public void testSimpleNamespacesHierarchy() { .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("develop/develop-2")) .membership( - opts -> opts.seedMembers(rootDevelop.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0))) + opts -> + opts.seedMembers( + rootDevelop.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0))) .startAwait(); Cluster eve = @@ -180,7 +188,10 @@ public void testSimpleNamespacesHierarchy() { .membership( opts -> opts.seedMembers( - rootDevelop.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0), dan.addresses().get(0))) + rootDevelop.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0), + dan.addresses().get(0))) .startAwait(); assertThat( @@ -213,7 +224,8 @@ public void testIsolatedParentNamespaces() { new ClusterImpl() .transportFactory(WebsocketTransportFactory::new) .membership(opts -> opts.namespace("a/1/c")) - .membership(opts -> opts.seedMembers(parent1.addresses().get(0), bob.addresses().get(0))) + .membership( + opts -> opts.seedMembers(parent1.addresses().get(0), bob.addresses().get(0))) .startAwait(); Cluster parent2 = @@ -229,7 +241,10 @@ public void testIsolatedParentNamespaces() { .membership( opts -> opts.seedMembers( - parent1.addresses().get(0), parent2.addresses().get(0), bob.addresses().get(0), carol.addresses().get(0))) + parent1.addresses().get(0), + parent2.addresses().get(0), + bob.addresses().get(0), + carol.addresses().get(0))) .startAwait(); //noinspection unused diff --git a/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java b/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java index 906d7e2f..89a9cbcd 100644 --- a/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/ClusterTest.java @@ -562,7 +562,7 @@ public void testJoinSeedClusterWithNoExistingSeedMember() { List
seeds = new ArrayList<>(); seeds.add(Address.from("localhost:1234")); // Not existent - seeds.add(Address.from("localhost:5678")); // Not existent + seeds.add(Address.from("localhost:5678")); // Not existent seeds.addAll(seedNode.addresses()); Cluster otherNode = diff --git a/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java b/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java index bf762570..ac83c606 100644 --- a/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/fdetector/FailureDetectorTest.java @@ -474,7 +474,8 @@ private Future> listenNextEventFor( for (final Address member : addresses) { final CompletableFuture future = new CompletableFuture<>(); fd.listen() - .filter(event -> event.member().addresses().contains(member)).subscribe(future::complete); + .filter(event -> event.member().addresses().contains(member)) + .subscribe(future::complete); resultFuture.add(future); } diff --git a/pom.xml b/pom.xml index e21c8b82..4e73dfb3 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 @@ -55,6 +57,7 @@ cluster-testlib transport-parent codec-parent + cluster-tests diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java index d4fb5569..31798a61 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java @@ -200,9 +200,7 @@ public List
sender() { return Collections.emptyList(); } - return Arrays.stream(headerValue.split(",")) - .map(Address::from) - .collect(Collectors.toList()); + return Arrays.stream(headerValue.split(",")).map(Address::from).collect(Collectors.toList()); } @Override @@ -300,8 +298,8 @@ public Builder correlationId(String correlationId) { */ public Builder sender(List
addresses) { return header( - HEADER_SENDER, - addresses.stream().map(Address::toString).collect(Collectors.joining(","))); + HEADER_SENDER, + addresses.stream().map(Address::toString).collect(Collectors.joining(","))); } public Message build() { From 7aafb99cd012e11a1744ed09872d1b66ca21b5d5 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 14:36:12 +0300 Subject: [PATCH 09/34] Fixed style --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 4e73dfb3..ec37b81a 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ https://maven.pkg.github.com/scalecube/scalecube-cluster + true From ef7a7a9322bceb4691a03b9b9f9ed87b8098ed07 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 15:21:49 +0300 Subject: [PATCH 10/34] WIP --- .../cluster/utils/NetworkEmulator.java | 10 ++- .../utils/NetworkEmulatorTransport.java | 7 +- .../io/scalecube/cluster/ClusterImpl.java | 53 +---------- .../fdetector/FailureDetectorImpl.java | 88 +++++++++++++----- .../cluster/gossip/GossipProtocolImpl.java | 12 ++- .../membership/MembershipProtocolImpl.java | 43 ++++++--- .../cluster/metadata/MetadataStoreImpl.java | 22 +++-- .../cluster/transport/api/Message.java | 89 ++++++------------- 8 files changed, 159 insertions(+), 165 deletions(-) diff --git a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java index f3da9c66..2239e8f9 100644 --- a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java +++ b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulator.java @@ -1,5 +1,6 @@ package io.scalecube.cluster.utils; +import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.net.Address; import java.time.Duration; @@ -217,10 +218,12 @@ public InboundSettings inboundSettings(Address destination) { /** * Returns network inbound settings applied to the given destination. * - * @param destinations addresses of target endpoint + * @param member target member * @return network inbound settings */ - public InboundSettings inboundSettings(List
destinations) { + public InboundSettings inboundSettings(Member member) { + final List
destinations = member.addresses(); + if (destinations.isEmpty()) { return defaultInboundSettings; } @@ -241,7 +244,8 @@ public InboundSettings inboundSettings(List
destinations) { * * @param shallPass shallPass inbound flag */ - public void inboundSettings(List
destinations, boolean shallPass) { + public void inboundSettings(Member member, boolean shallPass) { + final List
destinations = member.addresses(); InboundSettings settings = new InboundSettings(shallPass); destinations.forEach( diff --git a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java index c75ab24f..4812c34f 100644 --- a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java +++ b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java @@ -1,5 +1,6 @@ package io.scalecube.cluster.utils; +import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; @@ -70,7 +71,9 @@ public Mono requestResponse(Address address, Message request) { .flatMap( message -> { boolean shallPass = - networkEmulator.inboundSettings(message.sender()).shallPass(); + networkEmulator + .inboundSettings((Member) message.sender()) + .shallPass(); return shallPass ? Mono.just(message) : Mono.never(); }))); } @@ -79,7 +82,7 @@ public Mono requestResponse(Address address, Message request) { public Flux listen() { return transport .listen() - .filter(message -> networkEmulator.inboundSettings(message.sender()).shallPass()) + .filter(message -> networkEmulator.inboundSettings((Member) message.sender()).shallPass()) .onBackpressureBuffer(); } diff --git a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java index 813724cb..65f1df94 100644 --- a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java @@ -243,8 +243,7 @@ private Mono doStart0() { .flatMap( boundTransport -> { localMember = createLocalMember(boundTransport.address()); - - transport = new SenderAwareTransport(boundTransport, localMember.addresses()); + transport = boundTransport; final String name = "sc-cluster-" + Integer.toHexString(System.identityHashCode(this)); @@ -503,54 +502,4 @@ private Mono dispose() { public Mono onShutdown() { return onShutdown.asMono(); } - - private static class SenderAwareTransport implements Transport { - - private final Transport transport; - private final List
addresses; - - private SenderAwareTransport(Transport transport, List
addresses) { - this.transport = Objects.requireNonNull(transport); - this.addresses = Objects.requireNonNull(addresses); - } - - @Override - public Address address() { - return transport.address(); - } - - @Override - public Mono start() { - return transport.start(); - } - - @Override - public Mono stop() { - return transport.stop(); - } - - @Override - public boolean isStopped() { - return transport.isStopped(); - } - - @Override - public Mono send(Address address, Message message) { - return Mono.defer(() -> transport.send(address, enhanceWithSender(message))); - } - - @Override - public Mono requestResponse(Address address, Message request) { - return Mono.defer(() -> transport.requestResponse(address, enhanceWithSender(request))); - } - - @Override - public Flux listen() { - return transport.listen(); - } - - private Message enhanceWithSender(Message message) { - return Message.with(message).sender(addresses).build(); - } - } } diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index 81204b3f..a63d255f 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -46,6 +46,7 @@ public final class FailureDetectorImpl implements FailureDetector { // State private final List pingMembers = new ArrayList<>(); + private final TransportWrapper transportWrapper; private long currentPeriod = 0; private int pingMemberIndex = 0; // index for sequential ping member selection @@ -81,6 +82,7 @@ public FailureDetectorImpl( this.transport = Objects.requireNonNull(transport); this.config = Objects.requireNonNull(config); this.scheduler = Objects.requireNonNull(scheduler); + transportWrapper = new TransportWrapper(transport); // Subscribe actionsDisposables.addAll( @@ -143,11 +145,18 @@ private void doPing() { // Send ping String cid = UUID.randomUUID().toString(); PingData pingData = new PingData(localMember, pingMember); - Message pingMsg = Message.withData(pingData).qualifier(PING).correlationId(cid).build(); + Message pingMsg = + Message.builder() + .sender(localMember) + .data(pingData) + .qualifier(PING) + .correlationId(cid) + .build(); LOGGER.debug("[{}][{}] Send Ping to {}", localMember, period, pingMember); - List
addresses = pingMember.addresses(); - TransportWrapper.requestResponse(transport, addresses, pingMsg) + + transportWrapper + .requestResponse(pingMember, pingMsg) .timeout(Duration.ofMillis(config.pingTimeout()), scheduler) .publishOn(scheduler) .subscribe( @@ -179,7 +188,9 @@ private void doPing() { private void doPingReq( long period, final Member pingMember, final List pingReqMembers, String cid) { Message pingReqMsg = - Message.withData(new PingData(localMember, pingMember)) + Message.builder() + .sender(localMember) + .data(new PingData(localMember, pingMember)) .qualifier(PING_REQ) .correlationId(cid) .build(); @@ -189,7 +200,8 @@ private void doPingReq( Duration timeout = Duration.ofMillis(config.pingInterval() - config.pingTimeout()); pingReqMembers.forEach( member -> - TransportWrapper.requestResponse(transport, member.addresses(), pingReqMsg) + transportWrapper + .requestResponse(member, pingReqMsg) .timeout(timeout, scheduler) .publishOn(scheduler) .subscribe( @@ -231,26 +243,42 @@ private void onMessage(Message message) { /** Listens to PING message and answers with ACK. */ private void onPing(Message message) { long period = this.currentPeriod; - List
sender = message.sender(); - LOGGER.debug("[{}][{}] Received Ping from {}", localMember, period, sender); + + LOGGER.debug("[{}][{}] Received Ping from {}", localMember, period, message.sender()); + PingData data = message.data(); + final Member dataTo = data.getTo(); + final Member dataFrom = data.getFrom(); + data = data.withAckType(AckType.DEST_OK); - if (!data.getTo().id().equals(localMember.id())) { + + if (!dataTo.id().equals(localMember.id())) { LOGGER.debug( "[{}][{}] Received Ping from {} to {}, but local member is {}", localMember, period, - sender, - data.getTo(), + message.sender(), + dataTo, localMember); data = data.withAckType(AckType.DEST_GONE); } + String correlationId = message.correlationId(); + Message ackMessage = - Message.withData(data).qualifier(PING_ACK).correlationId(correlationId).build(); - List
addresses = data.getFrom().addresses(); + Message.builder() + .sender(localMember) + .data(data) + .qualifier(PING_ACK) + .correlationId(correlationId) + .build(); + + List
addresses = dataFrom.addresses(); + LOGGER.debug("[{}][{}] Send PingAck to {}", localMember, period, addresses); - TransportWrapper.send(transport, addresses, ackMessage) + + transportWrapper + .send(dataFrom, ackMessage) .subscribe( null, ex -> @@ -272,10 +300,17 @@ private void onPingReq(Message message) { String correlationId = message.correlationId(); PingData pingReqData = new PingData(localMember, target, originalIssuer); Message pingMessage = - Message.withData(pingReqData).qualifier(PING).correlationId(correlationId).build(); - List
addresses = target.addresses(); - LOGGER.debug("[{}][{}] Send transit Ping to {}", localMember, period, addresses); - TransportWrapper.send(transport, addresses, pingMessage) + Message.builder() + .sender(localMember) + .data(pingReqData) + .qualifier(PING) + .correlationId(correlationId) + .build(); + + LOGGER.debug("[{}][{}] Send transit Ping to {}", localMember, period, target.addresses()); + + transportWrapper + .send(target, pingMessage) .subscribe( null, ex -> @@ -283,7 +318,7 @@ private void onPingReq(Message message) { "[{}][{}] Failed to send transit Ping to {}, cause: {}", localMember, period, - addresses, + target.addresses(), ex.toString())); } @@ -301,10 +336,17 @@ private void onTransitPingAck(Message message) { String correlationId = message.correlationId(); PingData originalAckData = new PingData(target, data.getTo()).withAckType(ackType); Message originalAckMessage = - Message.withData(originalAckData).qualifier(PING_ACK).correlationId(correlationId).build(); - List
addresses = target.addresses(); - LOGGER.debug("[{}][{}] Resend transit PingAck to {}", localMember, period, addresses); - TransportWrapper.send(transport, addresses, originalAckMessage) + Message.builder() + .sender(localMember) + .data(originalAckData) + .qualifier(PING_ACK) + .correlationId(correlationId) + .build(); + + LOGGER.debug("[{}][{}] Resend transit PingAck to {}", localMember, period, target.addresses()); + + transportWrapper + .send(target, originalAckMessage) .subscribe( null, ex -> @@ -312,7 +354,7 @@ private void onTransitPingAck(Message message) { "[{}][{}] Failed to resend transit PingAck to {}, cause: {}", localMember, period, - addresses, + target.addresses(), ex.toString())); } diff --git a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java index e3c36caa..7b9ba8c1 100644 --- a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java @@ -43,6 +43,7 @@ public final class GossipProtocolImpl implements GossipProtocol { private final Member localMember; private final Transport transport; private final GossipConfig config; + private final TransportWrapper transportWrapper; // Local State @@ -87,6 +88,7 @@ public GossipProtocolImpl( this.config = Objects.requireNonNull(config); this.localMember = Objects.requireNonNull(localMember); this.scheduler = Objects.requireNonNull(scheduler); + transportWrapper = new TransportWrapper(transport); // Subscribe actionsDisposables.addAll( @@ -294,7 +296,8 @@ private void spreadGossipsTo(long period, Member member) { .map(this::buildGossipRequestMessage) .forEach( message -> - TransportWrapper.send(transport, addresses, message) + transportWrapper + .send(member, message) .subscribe( null, ex -> @@ -342,8 +345,11 @@ private List selectGossipMembers() { } private Message buildGossipRequestMessage(Gossip gossip) { - GossipRequest gossipRequest = new GossipRequest(gossip, localMember.id()); - return Message.withData(gossipRequest).qualifier(GOSSIP_REQ).build(); + return Message.builder() + .sender(localMember) + .data(new GossipRequest(gossip, localMember.id())) + .qualifier(GOSSIP_REQ) + .build(); } private Set getGossipsToRemove(long period) { diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index c021d710..606a62a8 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -53,6 +53,7 @@ public final class MembershipProtocolImpl implements MembershipProtocol { private static final Logger LOGGER = LoggerFactory.getLogger(MembershipProtocol.class); + private final TransportWrapper transportWrapper; private enum MembershipUpdateReason { FAILURE_DETECTOR_EVENT, @@ -126,6 +127,7 @@ public MembershipProtocolImpl( this.scheduler = Objects.requireNonNull(scheduler); this.membershipConfig = Objects.requireNonNull(config).membershipConfig(); this.failureDetectorConfig = Objects.requireNonNull(config).failureDetectorConfig(); + transportWrapper = new TransportWrapper(transport); // Prepare seeds seedMembers = cleanUpSeedMembers(membershipConfig.seedMembers()); @@ -402,13 +404,14 @@ private Mono onSyncAck(Message syncAckMsg, boolean onStart) { private Mono onSync(Message syncMsg) { return Mono.defer( () -> { - final List
sender = syncMsg.sender(); + final Member sender = syncMsg.sender(); LOGGER.debug("[{}] Received Sync from {}", localMember, sender); return syncMembership(syncMsg.data(), false) .doOnSuccess( avoid -> { - Message message = prepareSyncDataMsg(SYNC_ACK, syncMsg.correlationId()); - TransportWrapper.send(transport, sender, message) + final Message message = prepareSyncDataMsg(SYNC_ACK, syncMsg.correlationId()); + transportWrapper + .send(sender, message) .subscribe( null, ex -> @@ -423,7 +426,10 @@ private Mono onSync(Message syncMsg) { /** Merges FD updates and processes them. */ private void onFailureDetectorEvent(FailureDetectorEvent fdEvent) { - MembershipRecord r0 = membershipTable.get(fdEvent.member().id()); + final Member member = fdEvent.member(); + final List
addresses = member.addresses(); + + MembershipRecord r0 = membershipTable.get(member.id()); if (r0 == null) { // member already removed return; } @@ -436,8 +442,9 @@ private void onFailureDetectorEvent(FailureDetectorEvent fdEvent) { // Alive won't override SUSPECT so issue instead extra sync with member to force it spread // alive with inc + 1 Message syncMsg = prepareSyncDataMsg(SYNC, null); - List
addresses = fdEvent.member().addresses(); - TransportWrapper.send(transport, addresses, syncMsg) + + transportWrapper + .send(member, syncMsg) .subscribe( null, ex -> @@ -506,9 +513,12 @@ private void schedulePeriodicSync() { } private Message prepareSyncDataMsg(String qualifier, String cid) { - List membershipRecords = new ArrayList<>(membershipTable.values()); - SyncData syncData = new SyncData(membershipRecords); - return Message.withData(syncData).qualifier(qualifier).correlationId(cid).build(); + return Message.builder() + .sender(localMember) + .data(new SyncData(new ArrayList<>(membershipTable.values()))) + .qualifier(qualifier) + .correlationId(cid) + .build(); } private Mono syncMembership(SyncData syncData, boolean onStart) { @@ -860,13 +870,20 @@ private void spreadMembershipGossipUnlessGossiped( } } - private Mono spreadMembershipGossip(MembershipRecord r) { + private Mono spreadMembershipGossip(MembershipRecord record) { return Mono.defer( () -> { - Message msg = Message.withData(r).qualifier(MEMBERSHIP_GOSSIP).build(); - LOGGER.debug("[{}] Send membership with gossip", localMember); + final Message message = + Message.builder() + .sender(localMember) + .data(record) + .qualifier(MEMBERSHIP_GOSSIP) + .build(); + return gossipProtocol - .spread(msg) + .spread(message) + .doOnSubscribe( + subscription -> LOGGER.debug("[{}] Send membership with gossip", localMember)) .doOnError( ex -> LOGGER.debug( diff --git a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java index 499a4bff..7b89e6ee 100644 --- a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java @@ -5,11 +5,9 @@ import io.scalecube.cluster.TransportWrapper; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.net.Address; import java.nio.ByteBuffer; import java.time.Duration; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -38,6 +36,7 @@ public class MetadataStoreImpl implements MetadataStore { private final Member localMember; private final Transport transport; private final ClusterConfig config; + private final TransportWrapper transportWrapper; // State @@ -71,6 +70,7 @@ public MetadataStoreImpl( this.config = Objects.requireNonNull(config); this.scheduler = Objects.requireNonNull(scheduler); this.localMetadata = localMetadata; // optional + transportWrapper = new TransportWrapper(transport); } @Override @@ -160,15 +160,17 @@ public Mono fetchMetadata(Member member) { .data(new GetMetadataRequest(member)) .build(); - List
addresses = member.addresses(); - - return TransportWrapper.requestResponse(transport, addresses, request) + return transportWrapper + .requestResponse(member, request) .timeout(Duration.ofMillis(config.metadataTimeout()), scheduler) .publishOn(scheduler) .doOnSuccess( s -> LOGGER.debug( - "[{}][{}] Received GetMetadataResp from {}", localMember, cid, addresses)) + "[{}][{}] Received GetMetadataResp from {}", + localMember, + cid, + member.addresses())) .map(Message::data) .map(GetMetadataResponse::getMetadata) .doOnError( @@ -178,7 +180,7 @@ public Mono fetchMetadata(Member member) { + "from {} within {} ms, cause: {}", localMember, cid, - addresses, + member.addresses(), config.metadataTimeout(), th.toString())); }); @@ -195,7 +197,7 @@ private void onMessage(Message message) { } private void onMetadataRequest(Message message) { - final List
sender = message.sender(); // TODO. Log + final Member sender = (Member) message.sender(); LOGGER.debug("[{}] Received GetMetadataReq from {}", localMember, sender); GetMetadataRequest reqData = message.data(); @@ -223,7 +225,9 @@ private void onMetadataRequest(Message message) { .build(); LOGGER.debug("[{}] Send GetMetadataResp to {}", localMember, sender); - TransportWrapper.send(transport, sender, response) + + transportWrapper + .send(sender, response) .subscribe( null, ex -> diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java index 31798a61..b1bd2bc4 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/Message.java @@ -1,19 +1,15 @@ package io.scalecube.cluster.transport.api; -import io.scalecube.net.Address; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.StringJoiner; -import java.util.stream.Collectors; /** * The Class Message introduces generic protocol used for point to point communication by transport. @@ -34,43 +30,18 @@ public final class Message implements Externalizable { */ public static final String HEADER_CORRELATION_ID = "cid"; - /** - * This header represents sender address of type {@link Address}. It's an address of message - * originator. This header is optional. - */ - public static final String HEADER_SENDER = - "sender"; // TODO. Value should be list of addresses (comma separated) - private Map headers = Collections.emptyMap(); - private Object data; + private Object sender; // Member + private Object data; // GossipReq|SyncData|SyncAckData|PingReqData|... public Message() {} private Message(Builder builder) { + this.sender = Objects.requireNonNull(builder.sender, "sender"); this.data = builder.data; this.headers = Collections.unmodifiableMap(Objects.requireNonNull(builder.headers)); } - /** - * Instantiates a new message with the given data and without headers. - * - * @param data the data to build a message from - * @return the built message - */ - public static Message fromData(Object data) { - return withData(data).build(); - } - - /** - * Instantiates a new message builder with the given data and without headers. - * - * @param data the initial data for the builder - * @return a builder with initial data - */ - public static Builder withData(Object data) { - return builder().data(data); - } - /** * Instantiates a new message with the given headers and with empty data. * @@ -128,7 +99,7 @@ public static Message from(Message message) { * @return a builder with initial data and headers from the message */ public static Builder with(Message message) { - return withData(message.data).headers(message.headers); + return builder().sender(message.sender).data(message.data).headers(message.headers); } /** @@ -177,6 +148,16 @@ public String correlationId() { return header(HEADER_CORRELATION_ID); } + /** + * Returns {@code Member} of the sender of this message. + * + * @return address + */ + public T sender() { + //noinspection unchecked + return (T) sender; + } + /** * Return the message data, which can be byte array, string or any type. * @@ -188,25 +169,11 @@ public T data() { return (T) data; } - /** - * Returns {@link Address} of the sender of this message. - * - * @return address - */ - public List
sender() { - String headerValue = header(HEADER_SENDER); - - if (headerValue == null) { - return Collections.emptyList(); - } - - return Arrays.stream(headerValue.split(",")).map(Address::from).collect(Collectors.toList()); - } - @Override public String toString() { return new StringJoiner(", ", Message.class.getSimpleName() + "[", "]") .add("headers=" + headers) + .add("sender=" + sender) .add("data=" + data) .toString(); } @@ -219,6 +186,8 @@ public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(header.getKey()); out.writeObject(header.getValue()); // value is nullable } + // sender + out.writeObject(sender); // data out.writeObject(data); } @@ -234,6 +203,8 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept headers.put(name, value); } this.headers = Collections.unmodifiableMap(headers); + // sender + sender = in.readObject(); // data data = in.readObject(); } @@ -241,6 +212,7 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept public static class Builder { private final Map headers = new HashMap<>(); + private Object sender; private Object data; private Builder() {} @@ -254,6 +226,15 @@ public Builder data(Object data) { return this; } + public Object sender() { + return sender; + } + + public Builder sender(Object sender) { + this.sender = sender; + return this; + } + private Map headers() { return this.headers; } @@ -290,18 +271,6 @@ public Builder correlationId(String correlationId) { return header(HEADER_CORRELATION_ID, correlationId); } - /** - * Setter for header. - * - * @param addresses addresses - * @return builder - */ - public Builder sender(List
addresses) { - return header( - HEADER_SENDER, - addresses.stream().map(Address::toString).collect(Collectors.joining(","))); - } - public Message build() { return new Message(this); } From 5ded4074d06cfb2f1c0e2c55206ea3abd92db5d0 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 16:41:44 +0300 Subject: [PATCH 11/34] WIP --- cluster-tests/pom.xml | 5 + .../scalecube/transport/netty/BaseTest.java | 37 +- .../transport/netty/tcp/TcpTransportTest.java | 712 +++++++++--------- .../websocket/WebsocketTransportTest.java | 692 ++++++++--------- .../scalecube/cluster/TransportWrapper.java | 52 +- .../membership/MembershipProtocolImpl.java | 17 +- .../cluster/metadata/MetadataStoreImpl.java | 4 +- .../cluster/TransportWrapperTest.java | 164 ++++ .../cluster/gossip/GossipDelayTest.java | 5 +- .../cluster/gossip/GossipProtocolTest.java | 4 +- .../cluster/gossip/GossipRequestTest.java | 17 +- .../io/scalecube/examples/GossipExample.java | 7 +- 12 files changed, 971 insertions(+), 745 deletions(-) create mode 100644 cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java diff --git a/cluster-tests/pom.xml b/cluster-tests/pom.xml index b63c0c6b..80d641d5 100644 --- a/cluster-tests/pom.xml +++ b/cluster-tests/pom.xml @@ -13,6 +13,11 @@ cluster-tests + + ${project.groupId} + scalecube-cluster + ${project.version} + ${project.groupId} scalecube-cluster-testlib diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java index 84b7a100..2cb27b01 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java @@ -3,7 +3,6 @@ import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; import io.scalecube.cluster.transport.api.TransportConfig; -import io.scalecube.cluster.transport.api.TransportWrapper; import io.scalecube.cluster.utils.NetworkEmulatorTransport; import io.scalecube.net.Address; import io.scalecube.transport.netty.tcp.TcpTransportFactory; @@ -52,24 +51,24 @@ protected Mono send(Transport transport, Address to, Message msg) { th.toString())); } - /** - * Sending message from src to destination. - * - * @param transport src - * @param to destinations - * @param msg request - */ - protected Mono send(Transport transport, List
to, Message msg) { - return TransportWrapper.send(transport, to, msg) - .doOnError( - th -> - LOGGER.error( - "Failed to send {} to {} from transport: {}, cause: {}", - msg, - to, - transport, - th.toString())); - } +// /** +// * Sending message from src to destination. +// * +// * @param transport src +// * @param to destinations +// * @param msg request +// */ +// protected Mono send(Transport transport, List
to, Message msg) { +// return TransportWrapper.send(transport, to, msg) +// .doOnError( +// th -> +// LOGGER.error( +// "Failed to send {} to {} from transport: {}, cause: {}", +// msg, +// to, +// transport, +// th.toString())); +// } /** * Stopping transport. diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java index 2bd14e96..12314882 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java @@ -1,346 +1,366 @@ -package io.scalecube.transport.netty.tcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import io.scalecube.cluster.transport.api.Message; -import io.scalecube.cluster.transport.api.TransportWrapper; -import io.scalecube.cluster.utils.NetworkEmulatorTransport; -import io.scalecube.net.Address; -import io.scalecube.transport.netty.BaseTest; -import java.io.IOException; -import java.net.UnknownHostException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.test.StepVerifier; - -public class TcpTransportTest extends BaseTest { - - public static final Duration TIMEOUT = Duration.ofSeconds(10); - - // Auto-destroyed on tear down - private NetworkEmulatorTransport client; - private NetworkEmulatorTransport server; - - /** Tear down. */ - @AfterEach - public final void tearDown() { - destroyTransport(client); - destroyTransport(server); - } - - @Test - public void testUnresolvedHostConnection() { - client = createTcpTransport(); - // create transport with wrong host - try { - Address address = Address.from("wronghost:49255"); - Message message = Message.withData("q").build(); - client.send(address, message).block(Duration.ofSeconds(20)); - fail("fail"); - } catch (Exception e) { - assertEquals( - UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); - } - } - - @Test - public void testInteractWithNoConnection(TestInfo testInfo) { - Address serverAddress = Address.from("localhost:49255"); - for (int i = 0; i < 10; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - - client = createTcpTransport(); - - // create transport and don't wait just send message - try { - Message msg = Message.withData("q").build(); - client.send(serverAddress, msg).block(Duration.ofSeconds(3)); - fail("fail"); - } catch (Exception e) { - assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); - } - - // send second message: no connection yet and it's clear that there's no connection - try { - Message msg = Message.withData("q").build(); - client.send(serverAddress, msg).block(Duration.ofSeconds(3)); - fail("fail"); - } catch (Exception e) { - assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); - } - - destroyTransport(client); - } - } - - @Test - public void testPingPongClientTfListenAndServerTfListen() throws Exception { - client = createTcpTransport(); - server = createTcpTransport(); - - server - .listen() - .subscribe( - message -> { - List
addresses = message.sender(); - assertTrue( - addresses.stream().anyMatch(a -> client.address().equals(a)), - "Expected clientAddress"); - send(server, addresses, Message.fromQualifier("hi client")).subscribe(); - }); - - CompletableFuture messageFuture = new CompletableFuture<>(); - client.listen().subscribe(messageFuture::complete); - - send(client, server.address(), Message.fromQualifier("hello server")).subscribe(); - - Message result = messageFuture.get(3, TimeUnit.SECONDS); - assertNotNull(result, "No response from serverAddress"); - assertEquals("hi client", result.qualifier()); - } - - @Test - public void testNetworkSettings() { - client = createTcpTransport(); - server = createTcpTransport(); - - int lostPercent = 50; - int mean = 0; - client.networkEmulator().outboundSettings(server.address(), lostPercent, mean); - - final List serverMessageList = new ArrayList<>(); - server.listen().subscribe(serverMessageList::add); - - int total = 1000; - Flux.range(0, total) - .flatMap(i -> client.send(server.address(), Message.withData("q" + i).build())) - .onErrorContinue((th, o) -> {}) - .blockLast(TIMEOUT); - - int expectedMax = - total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages - int size = serverMessageList.size(); - assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); - } - - @Test - public void testPingPongOnSingleChannel() throws Exception { - server = createTcpTransport(); - client = createTcpTransport(); - - server - .listen() - .buffer(2) - .subscribe( - messages -> { - for (Message message : messages) { - Message echo = Message.withData("echo/" + message.qualifier()).build(); - TransportWrapper.send(server, message.sender(), echo) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - }); - - final CompletableFuture> targetFuture = new CompletableFuture<>(); - client.listen().buffer(2).subscribe(targetFuture::complete); - - Message q1 = Message.withData("q1").build(); - Message q2 = Message.withData("q2").build(); - - client - .send(server.address(), q1) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - client - .send(server.address(), q2) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - - List target = targetFuture.get(1, TimeUnit.SECONDS); - assertNotNull(target); - assertEquals(2, target.size()); - } - - @Test - public void testShouldRequestResponseSuccess() { - client = createTcpTransport(); - server = createTcpTransport(); - - server - .listen() - .filter(req -> req.qualifier().equals("hello/server")) - .subscribe( - message -> - send( - server, - message.sender(), - Message.builder() - .correlationId(message.correlationId()) - .data("hello: " + message.data()) - .build()) - .subscribe()); - - String result = - client - .requestResponse( - server.address(), - Message.builder() - .qualifier("hello/server") - .correlationId("123xyz") - .data("server") - .build()) - .map(msg -> msg.data().toString()) - .block(Duration.ofSeconds(1)); - - assertEquals("hello: server", result); - } - - @Test - public void testPingPongOnSeparateChannel() throws Exception { - server = createTcpTransport(); - client = createTcpTransport(); - - server - .listen() - .buffer(2) - .subscribe( - messages -> { - for (Message message : messages) { - Message echo = Message.withData("echo/" + message.qualifier()).build(); - TransportWrapper.send(server, message.sender(), echo) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - }); - - final CompletableFuture> targetFuture = new CompletableFuture<>(); - client.listen().buffer(2).subscribe(targetFuture::complete); - - Message q1 = Message.withData("q1").build(); - Message q2 = Message.withData("q2").build(); - - client - .send(server.address(), q1) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - client - .send(server.address(), q2) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - - List target = targetFuture.get(1, TimeUnit.SECONDS); - assertNotNull(target); - assertEquals(2, target.size()); - } - - @Test - public void testCompleteObserver() throws Exception { - server = createTcpTransport(); - client = createTcpTransport(); - - final CompletableFuture completeLatch = new CompletableFuture<>(); - final CompletableFuture messageLatch = new CompletableFuture<>(); - - server - .listen() - .subscribe( - messageLatch::complete, - errorConsumer -> { - // no-op - }, - () -> completeLatch.complete(true)); - - client.send(server.address(), Message.withData("q").build()).block(Duration.ofSeconds(1)); - - assertNotNull(messageLatch.get(1, TimeUnit.SECONDS)); - - server.stop().block(TIMEOUT); - - assertTrue(completeLatch.get(1, TimeUnit.SECONDS)); - } - - @Test - public void testObserverThrowsException() throws Exception { - server = createTcpTransport(); - client = createTcpTransport(); - - server - .listen() - .subscribe( - message -> { - String qualifier = message.data(); - if (qualifier.startsWith("throw")) { - throw new RuntimeException("" + message); - } - if (qualifier.startsWith("q")) { - Message echo = Message.withData("echo/" + message.qualifier()).build(); - TransportWrapper.send(server, message.sender(), echo) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - }, - Throwable::printStackTrace); - - // send "throw" and raise exception on server subscriber - final CompletableFuture messageFuture0 = new CompletableFuture<>(); - client.listen().subscribe(messageFuture0::complete); - Message message = Message.withData("throw").build(); - client - .send(server.address(), message) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - Message message0 = null; - try { - message0 = messageFuture0.get(1, TimeUnit.SECONDS); - } catch (TimeoutException e) { - // ignore since expected behavior - } - assertNull(message0); - - // send normal message and check whether server subscriber is broken (no response) - final CompletableFuture messageFuture1 = new CompletableFuture<>(); - client.listen().subscribe(messageFuture1::complete); - client.send(server.address(), Message.withData("q").build()); - Message transportMessage1 = null; - try { - transportMessage1 = messageFuture1.get(1, TimeUnit.SECONDS); - } catch (TimeoutException e) { - // ignore since expected behavior - } - assertNull(transportMessage1); - } - - @Test - public void testBlockAndUnblockTraffic() throws Exception { - client = createTcpTransport(); - server = createTcpTransport(); - - server - .listen() - .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); - - Sinks.Many responses = Sinks.many().replay().all(); - client - .listen() - .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); - - // test at unblocked transport - send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); - - // then block client->server messages - Thread.sleep(1000); - client.networkEmulator().blockOutbound(server.address()); - send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); - - StepVerifier.create(responses.asFlux()) - .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) - .expectNoEvent(Duration.ofMillis(300)) - .thenCancel() - .verify(TIMEOUT); - } -} +//package io.scalecube.transport.netty.tcp; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertNotNull; +//import static org.junit.jupiter.api.Assertions.assertNull; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static org.junit.jupiter.api.Assertions.fail; +// +//import io.scalecube.cluster.Member; +//import io.scalecube.cluster.TransportWrapper; +//import io.scalecube.cluster.transport.api.Message; +//import io.scalecube.cluster.utils.NetworkEmulatorTransport; +//import io.scalecube.net.Address; +//import io.scalecube.transport.netty.BaseTest; +//import java.io.IOException; +//import java.net.UnknownHostException; +//import java.time.Duration; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.CompletableFuture; +//import java.util.concurrent.TimeUnit; +//import java.util.concurrent.TimeoutException; +//import org.junit.jupiter.api.AfterEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.TestInfo; +//import reactor.core.publisher.Flux; +//import reactor.core.publisher.Sinks; +//import reactor.test.StepVerifier; +// +//public class TcpTransportTest extends BaseTest { +// +// public static final Duration TIMEOUT = Duration.ofSeconds(10); +// +// // Auto-destroyed on tear down +// private NetworkEmulatorTransport client; +// private NetworkEmulatorTransport server; +// +// /** Tear down. */ +// @AfterEach +// public final void tearDown() { +// destroyTransport(client); +// destroyTransport(server); +// } +// +// private static Member createMember(Address address) { +// return new Member("0", null, address, "NAMESPACE"); +// } +// +// @Test +// public void testUnresolvedHostConnection() { +// client = createTcpTransport(); +// // create transport with wrong host +// try { +// Address address = Address.from("wronghost:49255"); +// Member sender = createMember(address); +// Message message = Message.builder().sender(sender).data("q").build(); +// client.send(address, message).block(Duration.ofSeconds(20)); +// fail("fail"); +// } catch (Exception e) { +// assertEquals( +// UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); +// } +// } +// +// @Test +// public void testInteractWithNoConnection(TestInfo testInfo) { +// Address serverAddress = Address.from("localhost:49255"); +// Member sender = createMember(serverAddress); +// +// for (int i = 0; i < 10; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// +// client = createTcpTransport(); +// +// // create transport and don't wait just send message +// try { +// Message msg = Message.builder().sender(sender).data("q").build(); +// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); +// fail("fail"); +// } catch (Exception e) { +// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); +// } +// +// // send second message: no connection yet and it's clear that there's no connection +// try { +// Message msg = Message.builder().sender(sender).data("q").build(); +// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); +// fail("fail"); +// } catch (Exception e) { +// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); +// } +// +// destroyTransport(client); +// } +// } +// +// @Test +// public void testPingPongClientTfListenAndServerTfListen() throws Exception { +// client = createTcpTransport(); +// server = createTcpTransport(); +// +// server +// .listen() +// .subscribe( +// message -> { +// Member sender = message.sender(); +// final List
addresses = sender.addresses(); +// assertTrue( +// addresses.stream().anyMatch(a -> client.address().equals(a)), +// "Expected clientAddress"); +// send(server, addresses, Message.fromQualifier("hi client")).subscribe(); +// }); +// +// CompletableFuture messageFuture = new CompletableFuture<>(); +// client.listen().subscribe(messageFuture::complete); +// +// send(client, server.address(), Message.fromQualifier("hello server")).subscribe(); +// +// Message result = messageFuture.get(3, TimeUnit.SECONDS); +// assertNotNull(result, "No response from serverAddress"); +// assertEquals("hi client", result.qualifier()); +// } +// +// @Test +// public void testNetworkSettings() { +// client = createTcpTransport(); +// server = createTcpTransport(); +// +// int lostPercent = 50; +// int mean = 0; +// final Address address = server.address(); +// client.networkEmulator().outboundSettings(address, lostPercent, mean); +// +// final List serverMessageList = new ArrayList<>(); +// server.listen().subscribe(serverMessageList::add); +// +// int total = 1000; +// Flux.range(0, total) +// .flatMap( +// i -> +// client.send( +// address, Message.builder().sender(createMember(address)).data("q" + i).build())) +// .onErrorContinue((th, o) -> {}) +// .blockLast(TIMEOUT); +// +// int expectedMax = +// total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages +// int size = serverMessageList.size(); +// assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); +// } +// +// @Test +// public void testPingPongOnSingleChannel() throws Exception { +// server = createTcpTransport(); +// client = createTcpTransport(); +// final TransportWrapper transportWrapper = new TransportWrapper(server); +// +// server +// .listen() +// .buffer(2) +// .subscribe( +// messages -> { +// for (Message message : messages) { +// Message echo = +// Message.builder() +// .sender(createMember(server.address())) +// .data("echo/" + message.qualifier()) +// .build(); +// +// transportWrapper +// .send(message.sender(), echo) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// }); +// +// final CompletableFuture> targetFuture = new CompletableFuture<>(); +// client.listen().buffer(2).subscribe(targetFuture::complete); +// +// Message q1 = Message.withData("q1").build(); +// Message q2 = Message.withData("q2").build(); +// +// client +// .send(server.address(), q1) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// client +// .send(server.address(), q2) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// +// List target = targetFuture.get(1, TimeUnit.SECONDS); +// assertNotNull(target); +// assertEquals(2, target.size()); +// } +// +// @Test +// public void testShouldRequestResponseSuccess() { +// client = createTcpTransport(); +// server = createTcpTransport(); +// +// server +// .listen() +// .filter(req -> req.qualifier().equals("hello/server")) +// .subscribe( +// message -> +// send( +// server, +// message.sender(), +// Message.builder() +// .correlationId(message.correlationId()) +// .data("hello: " + message.data()) +// .build()) +// .subscribe()); +// +// String result = +// client +// .requestResponse( +// server.address(), +// Message.builder() +// .qualifier("hello/server") +// .correlationId("123xyz") +// .data("server") +// .build()) +// .map(msg -> msg.data().toString()) +// .block(Duration.ofSeconds(1)); +// +// assertEquals("hello: server", result); +// } +// +// @Test +// public void testPingPongOnSeparateChannel() throws Exception { +// server = createTcpTransport(); +// client = createTcpTransport(); +// +// server +// .listen() +// .buffer(2) +// .subscribe( +// messages -> { +// for (Message message : messages) { +// Message echo = Message.withData("echo/" + message.qualifier()).build(); +// TransportWrapper.send(server, message.sender(), echo) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// }); +// +// final CompletableFuture> targetFuture = new CompletableFuture<>(); +// client.listen().buffer(2).subscribe(targetFuture::complete); +// +// Message q1 = Message.withData("q1").build(); +// Message q2 = Message.withData("q2").build(); +// +// client +// .send(server.address(), q1) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// client +// .send(server.address(), q2) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// +// List target = targetFuture.get(1, TimeUnit.SECONDS); +// assertNotNull(target); +// assertEquals(2, target.size()); +// } +// +// @Test +// public void testCompleteObserver() throws Exception { +// server = createTcpTransport(); +// client = createTcpTransport(); +// +// final CompletableFuture completeLatch = new CompletableFuture<>(); +// final CompletableFuture messageLatch = new CompletableFuture<>(); +// +// server +// .listen() +// .subscribe( +// messageLatch::complete, +// errorConsumer -> { +// // no-op +// }, +// () -> completeLatch.complete(true)); +// +// client.send(server.address(), Message.withData("q").build()).block(Duration.ofSeconds(1)); +// +// assertNotNull(messageLatch.get(1, TimeUnit.SECONDS)); +// +// server.stop().block(TIMEOUT); +// +// assertTrue(completeLatch.get(1, TimeUnit.SECONDS)); +// } +// +// @Test +// public void testObserverThrowsException() throws Exception { +// server = createTcpTransport(); +// client = createTcpTransport(); +// +// server +// .listen() +// .subscribe( +// message -> { +// String qualifier = message.data(); +// if (qualifier.startsWith("throw")) { +// throw new RuntimeException("" + message); +// } +// if (qualifier.startsWith("q")) { +// Message echo = Message.withData("echo/" + message.qualifier()).build(); +// TransportWrapper.send(server, message.sender(), echo) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// }, +// Throwable::printStackTrace); +// +// // send "throw" and raise exception on server subscriber +// final CompletableFuture messageFuture0 = new CompletableFuture<>(); +// client.listen().subscribe(messageFuture0::complete); +// Message message = Message.withData("throw").build(); +// client +// .send(server.address(), message) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// Message message0 = null; +// try { +// message0 = messageFuture0.get(1, TimeUnit.SECONDS); +// } catch (TimeoutException e) { +// // ignore since expected behavior +// } +// assertNull(message0); +// +// // send normal message and check whether server subscriber is broken (no response) +// final CompletableFuture messageFuture1 = new CompletableFuture<>(); +// client.listen().subscribe(messageFuture1::complete); +// client.send(server.address(), Message.withData("q").build()); +// Message transportMessage1 = null; +// try { +// transportMessage1 = messageFuture1.get(1, TimeUnit.SECONDS); +// } catch (TimeoutException e) { +// // ignore since expected behavior +// } +// assertNull(transportMessage1); +// } +// +// @Test +// public void testBlockAndUnblockTraffic() throws Exception { +// client = createTcpTransport(); +// server = createTcpTransport(); +// +// server +// .listen() +// .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); +// +// Sinks.Many responses = Sinks.many().replay().all(); +// client +// .listen() +// .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); +// +// // test at unblocked transport +// send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); +// +// // then block client->server messages +// Thread.sleep(1000); +// client.networkEmulator().blockOutbound(server.address()); +// send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); +// +// StepVerifier.create(responses.asFlux()) +// .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) +// .expectNoEvent(Duration.ofMillis(300)) +// .thenCancel() +// .verify(TIMEOUT); +// } +//} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java index da9b00d3..d9434490 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java @@ -1,346 +1,346 @@ -package io.scalecube.transport.netty.websocket; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import io.scalecube.cluster.transport.api.Message; -import io.scalecube.cluster.transport.api.TransportWrapper; -import io.scalecube.cluster.utils.NetworkEmulatorTransport; -import io.scalecube.net.Address; -import io.scalecube.transport.netty.BaseTest; -import java.io.IOException; -import java.net.UnknownHostException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.test.StepVerifier; - -public class WebsocketTransportTest extends BaseTest { - - public static final Duration TIMEOUT = Duration.ofSeconds(10); - - // Auto-destroyed on tear down - private NetworkEmulatorTransport client; - private NetworkEmulatorTransport server; - - /** Tear down. */ - @AfterEach - public final void tearDown() { - destroyTransport(client); - destroyTransport(server); - } - - @Test - public void testUnresolvedHostConnection() { - client = createWebsocketTransport(); - // create transport with wrong host - try { - Address address = Address.from("wronghost:49255"); - Message message = Message.withData("q").build(); - client.send(address, message).block(Duration.ofSeconds(20)); - fail("fail"); - } catch (Exception e) { - assertEquals( - UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); - } - } - - @Test - public void testInteractWithNoConnection(TestInfo testInfo) { - Address serverAddress = Address.from("localhost:49255"); - for (int i = 0; i < 10; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - - client = createWebsocketTransport(); - - // create transport and don't wait just send message - try { - Message msg = Message.withData("q").build(); - client.send(serverAddress, msg).block(Duration.ofSeconds(3)); - fail("fail"); - } catch (Exception e) { - assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); - } - - // send second message: no connection yet and it's clear that there's no connection - try { - Message msg = Message.withData("q").build(); - client.send(serverAddress, msg).block(Duration.ofSeconds(3)); - fail("fail"); - } catch (Exception e) { - assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); - } - - destroyTransport(client); - } - } - - @Test - public void testPingPongClientTfListenAndServerTfListen() throws Exception { - client = createWebsocketTransport(); - server = createWebsocketTransport(); - - server - .listen() - .subscribe( - message -> { - List
addresses = message.sender(); - assertTrue( - addresses.stream().anyMatch(a -> client.address().equals(a)), - "Expected clientAddress"); - send(server, addresses, Message.fromQualifier("hi client")).subscribe(); - }); - - CompletableFuture messageFuture = new CompletableFuture<>(); - client.listen().subscribe(messageFuture::complete); - - send(client, server.address(), Message.fromQualifier("hello server")).subscribe(); - - Message result = messageFuture.get(3, TimeUnit.SECONDS); - assertNotNull(result, "No response from serverAddress"); - assertEquals("hi client", result.qualifier()); - } - - @Test - public void testNetworkSettings() { - client = createWebsocketTransport(); - server = createWebsocketTransport(); - - int lostPercent = 50; - int mean = 0; - client.networkEmulator().outboundSettings(server.address(), lostPercent, mean); - - final List serverMessageList = new ArrayList<>(); - server.listen().subscribe(serverMessageList::add); - - int total = 1000; - Flux.range(0, total) - .flatMap(i -> client.send(server.address(), Message.withData("q" + i).build())) - .onErrorContinue((th, o) -> {}) - .blockLast(TIMEOUT); - - int expectedMax = - total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages - int size = serverMessageList.size(); - assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); - } - - @Test - public void testPingPongOnSingleChannel() throws Exception { - server = createWebsocketTransport(); - client = createWebsocketTransport(); - - server - .listen() - .buffer(2) - .subscribe( - messages -> { - for (Message message : messages) { - Message echo = Message.withData("echo/" + message.qualifier()).build(); - TransportWrapper.send(server, message.sender(), echo) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - }); - - final CompletableFuture> targetFuture = new CompletableFuture<>(); - client.listen().buffer(2).subscribe(targetFuture::complete); - - Message q1 = Message.withData("q1").build(); - Message q2 = Message.withData("q2").build(); - - client - .send(server.address(), q1) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - client - .send(server.address(), q2) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - - List target = targetFuture.get(1, TimeUnit.SECONDS); - assertNotNull(target); - assertEquals(2, target.size()); - } - - @Test - public void testShouldRequestResponseSuccess() { - client = createWebsocketTransport(); - server = createWebsocketTransport(); - - server - .listen() - .filter(req -> req.qualifier().equals("hello/server")) - .subscribe( - message -> - send( - server, - message.sender(), - Message.builder() - .correlationId(message.correlationId()) - .data("hello: " + message.data()) - .build()) - .subscribe()); - - String result = - client - .requestResponse( - server.address(), - Message.builder() - .qualifier("hello/server") - .correlationId("123xyz") - .data("server") - .build()) - .map(msg -> msg.data().toString()) - .block(Duration.ofSeconds(1)); - - assertEquals("hello: server", result); - } - - @Test - public void testPingPongOnSeparateChannel() throws Exception { - server = createWebsocketTransport(); - client = createWebsocketTransport(); - - server - .listen() - .buffer(2) - .subscribe( - messages -> { - for (Message message : messages) { - Message echo = Message.withData("echo/" + message.qualifier()).build(); - TransportWrapper.send(server, message.sender(), echo) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - }); - - final CompletableFuture> targetFuture = new CompletableFuture<>(); - client.listen().buffer(2).subscribe(targetFuture::complete); - - Message q1 = Message.withData("q1").build(); - Message q2 = Message.withData("q2").build(); - - client - .send(server.address(), q1) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - client - .send(server.address(), q2) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - - List target = targetFuture.get(1, TimeUnit.SECONDS); - assertNotNull(target); - assertEquals(2, target.size()); - } - - @Test - public void testCompleteObserver() throws Exception { - server = createWebsocketTransport(); - client = createWebsocketTransport(); - - final CompletableFuture completeLatch = new CompletableFuture<>(); - final CompletableFuture messageLatch = new CompletableFuture<>(); - - server - .listen() - .subscribe( - messageLatch::complete, - errorConsumer -> { - // no-op - }, - () -> completeLatch.complete(true)); - - client.send(server.address(), Message.withData("q").build()).block(Duration.ofSeconds(1)); - - assertNotNull(messageLatch.get(1, TimeUnit.SECONDS)); - - server.stop().block(TIMEOUT); - - assertTrue(completeLatch.get(1, TimeUnit.SECONDS)); - } - - @Test - public void testObserverThrowsException() throws Exception { - server = createWebsocketTransport(); - client = createWebsocketTransport(); - - server - .listen() - .subscribe( - message -> { - String qualifier = message.data(); - if (qualifier.startsWith("throw")) { - throw new RuntimeException("" + message); - } - if (qualifier.startsWith("q")) { - Message echo = Message.withData("echo/" + message.qualifier()).build(); - TransportWrapper.send(server, message.sender(), echo) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - }, - Throwable::printStackTrace); - - // send "throw" and raise exception on server subscriber - final CompletableFuture messageFuture0 = new CompletableFuture<>(); - client.listen().subscribe(messageFuture0::complete); - Message message = Message.withData("throw").build(); - client - .send(server.address(), message) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - Message message0 = null; - try { - message0 = messageFuture0.get(1, TimeUnit.SECONDS); - } catch (TimeoutException e) { - // ignore since expected behavior - } - assertNull(message0); - - // send normal message and check whether server subscriber is broken (no response) - final CompletableFuture messageFuture1 = new CompletableFuture<>(); - client.listen().subscribe(messageFuture1::complete); - client.send(server.address(), Message.withData("q").build()); - Message transportMessage1 = null; - try { - transportMessage1 = messageFuture1.get(1, TimeUnit.SECONDS); - } catch (TimeoutException e) { - // ignore since expected behavior - } - assertNull(transportMessage1); - } - - @Test - public void testBlockAndUnblockTraffic() throws Exception { - client = createWebsocketTransport(); - server = createWebsocketTransport(); - - server - .listen() - .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); - - Sinks.Many responses = Sinks.many().replay().all(); - client - .listen() - .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); - - // test at unblocked transport - send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); - - // then block client->server messages - Thread.sleep(1000); - client.networkEmulator().blockOutbound(server.address()); - send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); - - StepVerifier.create(responses.asFlux()) - .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) - .expectNoEvent(Duration.ofMillis(300)) - .thenCancel() - .verify(TIMEOUT); - } -} +//package io.scalecube.transport.netty.websocket; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertNotNull; +//import static org.junit.jupiter.api.Assertions.assertNull; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static org.junit.jupiter.api.Assertions.fail; +// +//import io.scalecube.cluster.transport.api.Message; +//import io.scalecube.cluster.transport.api.TransportWrapper; +//import io.scalecube.cluster.utils.NetworkEmulatorTransport; +//import io.scalecube.net.Address; +//import io.scalecube.transport.netty.BaseTest; +//import java.io.IOException; +//import java.net.UnknownHostException; +//import java.time.Duration; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.CompletableFuture; +//import java.util.concurrent.TimeUnit; +//import java.util.concurrent.TimeoutException; +//import org.junit.jupiter.api.AfterEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.TestInfo; +//import reactor.core.publisher.Flux; +//import reactor.core.publisher.Sinks; +//import reactor.test.StepVerifier; +// +//public class WebsocketTransportTest extends BaseTest { +// +// public static final Duration TIMEOUT = Duration.ofSeconds(10); +// +// // Auto-destroyed on tear down +// private NetworkEmulatorTransport client; +// private NetworkEmulatorTransport server; +// +// /** Tear down. */ +// @AfterEach +// public final void tearDown() { +// destroyTransport(client); +// destroyTransport(server); +// } +// +// @Test +// public void testUnresolvedHostConnection() { +// client = createWebsocketTransport(); +// // create transport with wrong host +// try { +// Address address = Address.from("wronghost:49255"); +// Message message = Message.withData("q").build(); +// client.send(address, message).block(Duration.ofSeconds(20)); +// fail("fail"); +// } catch (Exception e) { +// assertEquals( +// UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); +// } +// } +// +// @Test +// public void testInteractWithNoConnection(TestInfo testInfo) { +// Address serverAddress = Address.from("localhost:49255"); +// for (int i = 0; i < 10; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// +// client = createWebsocketTransport(); +// +// // create transport and don't wait just send message +// try { +// Message msg = Message.withData("q").build(); +// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); +// fail("fail"); +// } catch (Exception e) { +// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); +// } +// +// // send second message: no connection yet and it's clear that there's no connection +// try { +// Message msg = Message.withData("q").build(); +// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); +// fail("fail"); +// } catch (Exception e) { +// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); +// } +// +// destroyTransport(client); +// } +// } +// +// @Test +// public void testPingPongClientTfListenAndServerTfListen() throws Exception { +// client = createWebsocketTransport(); +// server = createWebsocketTransport(); +// +// server +// .listen() +// .subscribe( +// message -> { +// List
addresses = message.sender(); +// assertTrue( +// addresses.stream().anyMatch(a -> client.address().equals(a)), +// "Expected clientAddress"); +// send(server, addresses, Message.fromQualifier("hi client")).subscribe(); +// }); +// +// CompletableFuture messageFuture = new CompletableFuture<>(); +// client.listen().subscribe(messageFuture::complete); +// +// send(client, server.address(), Message.fromQualifier("hello server")).subscribe(); +// +// Message result = messageFuture.get(3, TimeUnit.SECONDS); +// assertNotNull(result, "No response from serverAddress"); +// assertEquals("hi client", result.qualifier()); +// } +// +// @Test +// public void testNetworkSettings() { +// client = createWebsocketTransport(); +// server = createWebsocketTransport(); +// +// int lostPercent = 50; +// int mean = 0; +// client.networkEmulator().outboundSettings(server.address(), lostPercent, mean); +// +// final List serverMessageList = new ArrayList<>(); +// server.listen().subscribe(serverMessageList::add); +// +// int total = 1000; +// Flux.range(0, total) +// .flatMap(i -> client.send(server.address(), Message.withData("q" + i).build())) +// .onErrorContinue((th, o) -> {}) +// .blockLast(TIMEOUT); +// +// int expectedMax = +// total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages +// int size = serverMessageList.size(); +// assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); +// } +// +// @Test +// public void testPingPongOnSingleChannel() throws Exception { +// server = createWebsocketTransport(); +// client = createWebsocketTransport(); +// +// server +// .listen() +// .buffer(2) +// .subscribe( +// messages -> { +// for (Message message : messages) { +// Message echo = Message.withData("echo/" + message.qualifier()).build(); +// TransportWrapper.send(server, message.sender(), echo) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// }); +// +// final CompletableFuture> targetFuture = new CompletableFuture<>(); +// client.listen().buffer(2).subscribe(targetFuture::complete); +// +// Message q1 = Message.withData("q1").build(); +// Message q2 = Message.withData("q2").build(); +// +// client +// .send(server.address(), q1) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// client +// .send(server.address(), q2) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// +// List target = targetFuture.get(1, TimeUnit.SECONDS); +// assertNotNull(target); +// assertEquals(2, target.size()); +// } +// +// @Test +// public void testShouldRequestResponseSuccess() { +// client = createWebsocketTransport(); +// server = createWebsocketTransport(); +// +// server +// .listen() +// .filter(req -> req.qualifier().equals("hello/server")) +// .subscribe( +// message -> +// send( +// server, +// message.sender(), +// Message.builder() +// .correlationId(message.correlationId()) +// .data("hello: " + message.data()) +// .build()) +// .subscribe()); +// +// String result = +// client +// .requestResponse( +// server.address(), +// Message.builder() +// .qualifier("hello/server") +// .correlationId("123xyz") +// .data("server") +// .build()) +// .map(msg -> msg.data().toString()) +// .block(Duration.ofSeconds(1)); +// +// assertEquals("hello: server", result); +// } +// +// @Test +// public void testPingPongOnSeparateChannel() throws Exception { +// server = createWebsocketTransport(); +// client = createWebsocketTransport(); +// +// server +// .listen() +// .buffer(2) +// .subscribe( +// messages -> { +// for (Message message : messages) { +// Message echo = Message.withData("echo/" + message.qualifier()).build(); +// TransportWrapper.send(server, message.sender(), echo) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// }); +// +// final CompletableFuture> targetFuture = new CompletableFuture<>(); +// client.listen().buffer(2).subscribe(targetFuture::complete); +// +// Message q1 = Message.withData("q1").build(); +// Message q2 = Message.withData("q2").build(); +// +// client +// .send(server.address(), q1) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// client +// .send(server.address(), q2) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// +// List target = targetFuture.get(1, TimeUnit.SECONDS); +// assertNotNull(target); +// assertEquals(2, target.size()); +// } +// +// @Test +// public void testCompleteObserver() throws Exception { +// server = createWebsocketTransport(); +// client = createWebsocketTransport(); +// +// final CompletableFuture completeLatch = new CompletableFuture<>(); +// final CompletableFuture messageLatch = new CompletableFuture<>(); +// +// server +// .listen() +// .subscribe( +// messageLatch::complete, +// errorConsumer -> { +// // no-op +// }, +// () -> completeLatch.complete(true)); +// +// client.send(server.address(), Message.withData("q").build()).block(Duration.ofSeconds(1)); +// +// assertNotNull(messageLatch.get(1, TimeUnit.SECONDS)); +// +// server.stop().block(TIMEOUT); +// +// assertTrue(completeLatch.get(1, TimeUnit.SECONDS)); +// } +// +// @Test +// public void testObserverThrowsException() throws Exception { +// server = createWebsocketTransport(); +// client = createWebsocketTransport(); +// +// server +// .listen() +// .subscribe( +// message -> { +// String qualifier = message.data(); +// if (qualifier.startsWith("throw")) { +// throw new RuntimeException("" + message); +// } +// if (qualifier.startsWith("q")) { +// Message echo = Message.withData("echo/" + message.qualifier()).build(); +// TransportWrapper.send(server, message.sender(), echo) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// }, +// Throwable::printStackTrace); +// +// // send "throw" and raise exception on server subscriber +// final CompletableFuture messageFuture0 = new CompletableFuture<>(); +// client.listen().subscribe(messageFuture0::complete); +// Message message = Message.withData("throw").build(); +// client +// .send(server.address(), message) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// Message message0 = null; +// try { +// message0 = messageFuture0.get(1, TimeUnit.SECONDS); +// } catch (TimeoutException e) { +// // ignore since expected behavior +// } +// assertNull(message0); +// +// // send normal message and check whether server subscriber is broken (no response) +// final CompletableFuture messageFuture1 = new CompletableFuture<>(); +// client.listen().subscribe(messageFuture1::complete); +// client.send(server.address(), Message.withData("q").build()); +// Message transportMessage1 = null; +// try { +// transportMessage1 = messageFuture1.get(1, TimeUnit.SECONDS); +// } catch (TimeoutException e) { +// // ignore since expected behavior +// } +// assertNull(transportMessage1); +// } +// +// @Test +// public void testBlockAndUnblockTraffic() throws Exception { +// client = createWebsocketTransport(); +// server = createWebsocketTransport(); +// +// server +// .listen() +// .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); +// +// Sinks.Many responses = Sinks.many().replay().all(); +// client +// .listen() +// .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); +// +// // test at unblocked transport +// send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); +// +// // then block client->server messages +// Thread.sleep(1000); +// client.networkEmulator().blockOutbound(server.address()); +// send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); +// +// StepVerifier.create(responses.asFlux()) +// .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) +// .expectNoEvent(Duration.ofMillis(300)) +// .thenCancel() +// .verify(TIMEOUT); +// } +//} diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 6055f5b7..cbbc9185 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -44,18 +44,21 @@ public Mono requestResponse(Member member, Message request) { } private Mono requestResponse(List
addresses, Message request) { - final AtomicInteger currentIndex = new AtomicInteger(); return Mono.defer( - () -> { - final int index = currentIndex.getAndIncrement(); - return transport.requestResponse(addresses.get(index), request); - }) - .retry(addresses.size() - 1) - .map( - message -> { - final int index = currentIndex.get(); - return new Result(addresses.get(index), message); - }); + () -> { + final AtomicInteger currentIndex = new AtomicInteger(); + return Mono.defer( + () -> { + final int index = currentIndex.getAndIncrement(); + return transport.requestResponse(addresses.get(index), request); + }) + .retry(addresses.size() - 1) + .map( + message -> { + final int index = currentIndex.get() - 1; + return new Result(addresses.get(index), message); + }); + }); } /** @@ -83,19 +86,22 @@ public Mono send(Member member, Message request) { } private Mono send(List
addresses, Message request) { - final AtomicInteger currentIndex = new AtomicInteger(); return Mono.defer( - () -> { - final int index = currentIndex.getAndIncrement(); - return transport.send(addresses.get(index), request); - }) - .retry(addresses.size() - 1) - .then( - Mono.fromCallable( - () -> { - final int index = currentIndex.get(); - return new Result(addresses.get(index)); - })); + () -> { + final AtomicInteger currentIndex = new AtomicInteger(); + return Mono.defer( + () -> { + final int index = currentIndex.getAndIncrement(); + return transport.send(addresses.get(index), request); + }) + .retry(addresses.size() - 1) + .then( + Mono.fromCallable( + () -> { + final int index = currentIndex.get() - 1; + return new Result(addresses.get(index)); + })); + }); } private static class Result { diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index 606a62a8..ee394b4d 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -353,7 +353,7 @@ private void doSync() { Message message = prepareSyncDataMsg(SYNC, null); LOGGER.debug("[{}][doSync] Send Sync to {}", localMember, addresses); - TransportWrapper.send(transport, addresses, message) + send(transport, addresses, message) .subscribe( null, ex -> @@ -976,4 +976,19 @@ private void onMemberRemoved(MembershipEvent event) { removedMembersHistory.remove(0); } } + + public static Mono send(Transport transport, List
addresses, Message request) { + return send(transport, addresses, 0, request); + } + + private static Mono send( + Transport transport, List
addresses, int currentIndex, Message request) { + if (currentIndex >= addresses.size()) { + return Mono.error(new RuntimeException("All addresses have been tried and failed")); + } + + return transport + .send(addresses.get(currentIndex), request) + .onErrorResume(th -> send(transport, addresses, currentIndex + 1, request)); + } } diff --git a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java index 7b89e6ee..c2646477 100644 --- a/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/metadata/MetadataStoreImpl.java @@ -157,6 +157,7 @@ public Mono fetchMetadata(Member member) { Message.builder() .qualifier(GET_METADATA_REQ) .correlationId(cid) + .sender(localMember) .data(new GetMetadataRequest(member)) .build(); @@ -197,7 +198,7 @@ private void onMessage(Message message) { } private void onMetadataRequest(Message message) { - final Member sender = (Member) message.sender(); + final Member sender = message.sender(); LOGGER.debug("[{}] Received GetMetadataReq from {}", localMember, sender); GetMetadataRequest reqData = message.data(); @@ -221,6 +222,7 @@ private void onMetadataRequest(Message message) { Message.builder() .qualifier(GET_METADATA_RESP) .correlationId(message.correlationId()) + .sender(localMember) .data(respData) .build(); diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java new file mode 100644 index 00000000..854d49af --- /dev/null +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -0,0 +1,164 @@ +package io.scalecube.cluster; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.scalecube.cluster.transport.api.Message; +import io.scalecube.cluster.transport.api.Transport; +import io.scalecube.net.Address; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +class TransportWrapperTest { + + private final Message request = + Message.builder() + .sender( + new Member( + "request", + null, + Collections.singletonList(Address.from("request:0")), + "namespace")) + .data("" + System.currentTimeMillis()) + .build(); + + private final Message response = + Message.builder() + .sender( + new Member( + "response", + null, + Collections.singletonList(Address.from("response:0")), + "namespace")) + .data("" + System.currentTimeMillis()) + .build(); + + @Test + void requestResponseShouldWork() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } + + @Test + void requestResponseShouldWorkMemberSingleAddress() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } + + @Test + void requestResponseShouldWorkMemberTwoAddresses() { + final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.requestResponse(addresses.get(1), request)).thenReturn(Mono.just(response)); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } + + @Test + void requestResponseShouldWorkMemberThreeAddresses() { + final List
addresses = + Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.requestResponse(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.requestResponse(addresses.get(2), request)).thenReturn(Mono.just(response)); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } + + @Test + void requestResponseShouldFailMemberSingleAddress() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } + + @Test + void requestResponseShouldFailMemberTwoAddresses() { + final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 0"))); + when(transport.requestResponse(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); + } + + @Test + void requestResponseShouldFailMemberThreeAddresses() { + final List
addresses = + Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 0"))); + when(transport.requestResponse(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + when(transport.requestResponse(addresses.get(2), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 2"))); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); + } +} diff --git a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java index 3857d1e6..125a8dac 100644 --- a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java @@ -57,7 +57,10 @@ public void testMessageDelayMoreThanGossipSweepTime() throws InterruptedExceptio gossipProtocol3.listen().subscribe(message -> protocol3GossipCounter.incrementAndGet()); for (int i = 0; i < 3; i++) { - gossipProtocol1.spread(Message.fromData("message: " + i)).subscribe(); + final Member member = gossipProtocol1.getMember(); + gossipProtocol1 + .spread(Message.builder().sender(member).data("message: " + i).build()) + .subscribe(); } TimeUnit.MILLISECONDS.sleep( diff --git a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java index 384c0c23..868794bd 100644 --- a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java @@ -146,7 +146,9 @@ void testGossipProtocol(int membersNum, int lossPercent, int meanDelay) throws E // Spread gossip, measure and verify delivery metrics long start = System.currentTimeMillis(); - gossipProtocols.get(0).spread(Message.fromData(gossipData)).subscribe(); + final GossipProtocolImpl gossipProtocol = gossipProtocols.get(0); + final Member member = gossipProtocol.getMember(); + gossipProtocol.spread(Message.builder().sender(member).data(gossipData).build()).subscribe(); latch.await(2 * gossipTimeout, TimeUnit.MILLISECONDS); // Await for double gossip timeout disseminationTime = System.currentTimeMillis() - start; messageSentStatsDissemination = computeMessageSentStats(gossipProtocols); diff --git a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipRequestTest.java b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipRequestTest.java index e2dfa1fc..81531a09 100644 --- a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipRequestTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipRequestTest.java @@ -27,6 +27,7 @@ public class GossipRequestTest extends BaseTest { private TestData testData; private MessageCodec messageCodec; + private Member sender; @BeforeEach public void init() { @@ -35,14 +36,18 @@ public void init() { testData = new TestData(); testData.setProperties(properties); messageCodec = MessageCodec.INSTANCE; + sender = new Member("0", null, Address.from("localhost:1234"), NAMESPACE); } @Test public void testSerializationAndDeserialization() throws Exception { - Member from = new Member("0", null, Address.from("localhost:1234"), NAMESPACE); List gossips = getGossips(); Message message = - Message.withData(new GossipRequest(gossips, from.id())).correlationId("CORR_ID").build(); + Message.builder() + .sender(sender) + .data(new GossipRequest(gossips, sender.id())) + .correlationId("CORR_ID") + .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); messageCodec.serialize(message, out); @@ -68,10 +73,10 @@ public void testSerializationAndDeserialization() throws Exception { } private List getGossips() { - Gossip request = - new Gossip("idGossip", Message.withData(testData).qualifier(testDataQualifier).build(), 0); - Gossip request2 = - new Gossip("idGossip2", Message.withData(testData).qualifier(testDataQualifier).build(), 1); + final Message message = + Message.builder().sender(sender).data(testData).qualifier(testDataQualifier).build(); + Gossip request = new Gossip("idGossip", message, 0); + Gossip request2 = new Gossip("idGossip2", message, 1); List gossips = new ArrayList<>(2); gossips.add(request); gossips.add(request2); diff --git a/examples/src/main/java/io/scalecube/examples/GossipExample.java b/examples/src/main/java/io/scalecube/examples/GossipExample.java index d4919541..e0012d03 100644 --- a/examples/src/main/java/io/scalecube/examples/GossipExample.java +++ b/examples/src/main/java/io/scalecube/examples/GossipExample.java @@ -3,6 +3,7 @@ import io.scalecube.cluster.Cluster; import io.scalecube.cluster.ClusterImpl; import io.scalecube.cluster.ClusterMessageHandler; +import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.transport.netty.tcp.TcpTransportFactory; @@ -85,7 +86,11 @@ public void onGossip(Message gossip) { .membership(opts -> opts.seedMembers(alice.addresses())) .transportFactory(TcpTransportFactory::new) .startAwait(); - eve.spreadGossip(Message.fromData("Gossip from Eve")) + + final Member member = eve.member(); + final Message message = Message.builder().sender(member).data("Gossip from Eve").build(); + + eve.spreadGossip(message) .doOnError(System.err::println) .subscribe(null, Throwable::printStackTrace); From b0f31556167bf5240c61940330a06075fc4ee231 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 17:09:10 +0300 Subject: [PATCH 12/34] WIP --- .../utils/NetworkEmulatorTransport.java | 9 +- .../netty/tcp/TcpTransportSendOrderTest.java | 504 +++++++++--------- .../WebsocketTransportSendOrderTest.java | 504 +++++++++--------- 3 files changed, 506 insertions(+), 511 deletions(-) diff --git a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java index 4812c34f..c47ebf34 100644 --- a/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java +++ b/cluster-testlib/src/main/java/io/scalecube/cluster/utils/NetworkEmulatorTransport.java @@ -4,7 +4,6 @@ import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; -import java.util.Collections; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -51,7 +50,7 @@ public boolean isStopped() { public Mono send(Address address, Message message) { return Mono.defer( () -> - Mono.just(enhanceWithSender(message)) + Mono.just(Message.with(message).build()) .flatMap(msg -> networkEmulator.tryFailOutbound(msg, address)) .flatMap(msg -> networkEmulator.tryDelayOutbound(msg, address)) .flatMap(msg -> transport.send(address, msg))); @@ -61,7 +60,7 @@ public Mono send(Address address, Message message) { public Mono requestResponse(Address address, Message request) { return Mono.defer( () -> - Mono.just(enhanceWithSender(request)) + Mono.just(Message.with(request).build()) .flatMap(msg -> networkEmulator.tryFailOutbound(msg, address)) .flatMap(msg -> networkEmulator.tryDelayOutbound(msg, address)) .flatMap( @@ -85,8 +84,4 @@ public Flux listen() { .filter(message -> networkEmulator.inboundSettings((Member) message.sender()).shallPass()) .onBackpressureBuffer(); } - - private Message enhanceWithSender(Message message) { - return Message.with(message).sender(Collections.singletonList(transport.address())).build(); - } } diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java index 753622d7..9568c548 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java @@ -1,252 +1,252 @@ -package io.scalecube.transport.netty.tcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import io.scalecube.cluster.transport.api.Message; -import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.net.Address; -import io.scalecube.transport.netty.BaseTest; -import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.LongSummaryStatistics; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.LongStream; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import reactor.core.Disposable; -import reactor.core.Exceptions; - -public class TcpTransportSendOrderTest extends BaseTest { - - // Auto-destroyed on tear down - private Transport client; - private Transport server; - - /** Tear down. */ - @AfterEach - public final void tearDown() { - destroyTransport(client); - destroyTransport(server); - } - - @Test - public void testSendOrderSingleThreadWithoutPromises(TestInfo testInfo) throws Exception { - server = createTcpTransport(); - - int iterationNum = 11; // +1 warm up iteration - int sentPerIteration = 1000; - long[] iterationTimeSeries = new long[iterationNum - 1]; - for (int i = 0; i < iterationNum; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - - client = createTcpTransport(); - final List received = new ArrayList<>(); - final CountDownLatch latch = new CountDownLatch(sentPerIteration); - - final Disposable serverSubscriber = - server - .listen() - .subscribe( - message -> { - received.add(message); - latch.countDown(); - }); - - long startAt = System.currentTimeMillis(); - for (int j = 0; j < sentPerIteration; j++) { - Message message = Message.withQualifier("q" + j).build(); - client - .send(server.address(), message) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - latch.await(20, TimeUnit.SECONDS); - long iterationTime = System.currentTimeMillis() - startAt; - if (i > 0) { // exclude warm up iteration - iterationTimeSeries[i - 1] = iterationTime; - } - assertSendOrder(sentPerIteration, received); - - LOGGER.debug("Iteration time: {} ms", iterationTime); - - serverSubscriber.dispose(); - destroyTransport(client); - } - - LongSummaryStatistics iterationTimeStats = - LongStream.of(iterationTimeSeries).summaryStatistics(); - LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); - } - - @Test - public void testSendOrderSingleThread(TestInfo testInfo) throws Exception { - server = createTcpTransport(); - - int iterationNum = 11; // +1 warm up iteration - int sentPerIteration = 1000; - long[] iterationTimeSeries = new long[iterationNum - 1]; - List totalSentTimeSeries = new ArrayList<>(sentPerIteration * (iterationNum - 1)); - for (int i = 0; i < iterationNum; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - List iterSentTimeSeries = new ArrayList<>(sentPerIteration); - - client = createTcpTransport(); - final List received = new ArrayList<>(); - final CountDownLatch latch = new CountDownLatch(sentPerIteration); - - final Disposable serverSubscriber = - server - .listen() - .subscribe( - message -> { - received.add(message); - latch.countDown(); - }); - - long startAt = System.currentTimeMillis(); - for (int j = 0; j < sentPerIteration; j++) { - long sentAt = System.currentTimeMillis(); - Message message = Message.withQualifier("q" + j).build(); - client - .send(server.address(), message) - .subscribe( - avoid -> iterSentTimeSeries.add(System.currentTimeMillis() - sentAt), - th -> - LOGGER.error( - "Failed to send message in {} ms", - System.currentTimeMillis() - sentAt, - th)); - } - - latch.await(20, TimeUnit.SECONDS); - long iterationTime = System.currentTimeMillis() - startAt; - if (i > 0) { // exclude warm up iteration - iterationTimeSeries[i - 1] = iterationTime; - } - assertSendOrder(sentPerIteration, received); - - Thread.sleep(10); // await a bit for last msg confirmation - - LongSummaryStatistics iterSentTimeStats = - iterSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); - if (i == 0) { // warm up iteration - LOGGER.debug("Warm up iteration time: {} ms", iterationTime); - LOGGER.debug("Sent time stats warm up iter (ms): {}", iterSentTimeStats); - } else { - totalSentTimeSeries.addAll(iterSentTimeSeries); - LongSummaryStatistics totalSentTimeStats = - totalSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); - LOGGER.debug("Iteration time: {} ms", iterationTime); - LOGGER.debug("Sent time stats iter (ms): {}", iterSentTimeStats); - LOGGER.debug("Sent time stats total (ms): {}", totalSentTimeStats); - } - - serverSubscriber.dispose(); - destroyTransport(client); - } - - LongSummaryStatistics iterationTimeStats = - LongStream.of(iterationTimeSeries).summaryStatistics(); - LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); - } - - @Test - public void testSendOrderMultiThread(TestInfo testInfo) throws Exception { - Transport server = createTcpTransport(); - - final int total = 1000; - for (int i = 0; i < 10; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - ExecutorService exec = - Executors.newFixedThreadPool( - 4, - r -> { - Thread thread = new Thread(r); - thread.setName("testSendOrderMultiThread"); - thread.setDaemon(true); - return thread; - }); - - Transport client = createTcpTransport(); - final List received = new ArrayList<>(); - final CountDownLatch latch = new CountDownLatch(4 * total); - server - .listen() - .subscribe( - message -> { - received.add(message); - latch.countDown(); - }); - - final Future f0 = exec.submit(sender(0, client, server.address(), total)); - final Future f1 = exec.submit(sender(1, client, server.address(), total)); - final Future f2 = exec.submit(sender(2, client, server.address(), total)); - final Future f3 = exec.submit(sender(3, client, server.address(), total)); - - latch.await(20, TimeUnit.SECONDS); - - f0.get(1, TimeUnit.SECONDS); - f1.get(1, TimeUnit.SECONDS); - f2.get(1, TimeUnit.SECONDS); - f3.get(1, TimeUnit.SECONDS); - - exec.shutdownNow(); - - assertSenderOrder(0, total, received); - assertSenderOrder(1, total, received); - assertSenderOrder(2, total, received); - assertSenderOrder(3, total, received); - - destroyTransport(client); - } - - destroyTransport(client); - destroyTransport(server); - } - - private void assertSendOrder(int total, List received) { - ArrayList messages = new ArrayList<>(received); - assertEquals(total, messages.size()); - for (int k = 0; k < total; k++) { - assertEquals("q" + k, messages.get(k).qualifier()); - } - } - - private Callable sender(int id, Transport client, Address address, int total) { - return () -> { - for (int j = 0; j < total; j++) { - String correlationId = id + "/" + j; - try { - Message message = Message.withQualifier("q").correlationId(correlationId).build(); - client.send(address, message).block(Duration.ofSeconds(3)); - } catch (Exception e) { - LOGGER.error("Failed to send message: j = {} id = {}", j, id, e); - throw Exceptions.propagate(e); - } - } - return null; - }; - } - - private void assertSenderOrder(int id, int total, List received) { - ArrayList messages = new ArrayList<>(received); - Map> group = new HashMap<>(); - for (Message message : messages) { - Integer key = Integer.valueOf(message.correlationId().split("/")[0]); - group.computeIfAbsent(key, ArrayList::new).add(message); - } - - assertEquals(total, group.get(id).size()); - for (int k = 0; k < total; k++) { - assertEquals(id + "/" + k, group.get(id).get(k).correlationId()); - } - } -} +//package io.scalecube.transport.netty.tcp; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +// +//import io.scalecube.cluster.transport.api.Message; +//import io.scalecube.cluster.transport.api.Transport; +//import io.scalecube.net.Address; +//import io.scalecube.transport.netty.BaseTest; +//import java.time.Duration; +//import java.util.ArrayList; +//import java.util.HashMap; +//import java.util.List; +//import java.util.LongSummaryStatistics; +//import java.util.Map; +//import java.util.concurrent.Callable; +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.ExecutorService; +//import java.util.concurrent.Executors; +//import java.util.concurrent.Future; +//import java.util.concurrent.TimeUnit; +//import java.util.stream.LongStream; +//import org.junit.jupiter.api.AfterEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.TestInfo; +//import reactor.core.Disposable; +//import reactor.core.Exceptions; +// +//public class TcpTransportSendOrderTest extends BaseTest { +// +// // Auto-destroyed on tear down +// private Transport client; +// private Transport server; +// +// /** Tear down. */ +// @AfterEach +// public final void tearDown() { +// destroyTransport(client); +// destroyTransport(server); +// } +// +// @Test +// public void testSendOrderSingleThreadWithoutPromises(TestInfo testInfo) throws Exception { +// server = createTcpTransport(); +// +// int iterationNum = 11; // +1 warm up iteration +// int sentPerIteration = 1000; +// long[] iterationTimeSeries = new long[iterationNum - 1]; +// for (int i = 0; i < iterationNum; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// +// client = createTcpTransport(); +// final List received = new ArrayList<>(); +// final CountDownLatch latch = new CountDownLatch(sentPerIteration); +// +// final Disposable serverSubscriber = +// server +// .listen() +// .subscribe( +// message -> { +// received.add(message); +// latch.countDown(); +// }); +// +// long startAt = System.currentTimeMillis(); +// for (int j = 0; j < sentPerIteration; j++) { +// Message message = Message.withQualifier("q" + j).build(); +// client +// .send(server.address(), message) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// latch.await(20, TimeUnit.SECONDS); +// long iterationTime = System.currentTimeMillis() - startAt; +// if (i > 0) { // exclude warm up iteration +// iterationTimeSeries[i - 1] = iterationTime; +// } +// assertSendOrder(sentPerIteration, received); +// +// LOGGER.debug("Iteration time: {} ms", iterationTime); +// +// serverSubscriber.dispose(); +// destroyTransport(client); +// } +// +// LongSummaryStatistics iterationTimeStats = +// LongStream.of(iterationTimeSeries).summaryStatistics(); +// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); +// } +// +// @Test +// public void testSendOrderSingleThread(TestInfo testInfo) throws Exception { +// server = createTcpTransport(); +// +// int iterationNum = 11; // +1 warm up iteration +// int sentPerIteration = 1000; +// long[] iterationTimeSeries = new long[iterationNum - 1]; +// List totalSentTimeSeries = new ArrayList<>(sentPerIteration * (iterationNum - 1)); +// for (int i = 0; i < iterationNum; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// List iterSentTimeSeries = new ArrayList<>(sentPerIteration); +// +// client = createTcpTransport(); +// final List received = new ArrayList<>(); +// final CountDownLatch latch = new CountDownLatch(sentPerIteration); +// +// final Disposable serverSubscriber = +// server +// .listen() +// .subscribe( +// message -> { +// received.add(message); +// latch.countDown(); +// }); +// +// long startAt = System.currentTimeMillis(); +// for (int j = 0; j < sentPerIteration; j++) { +// long sentAt = System.currentTimeMillis(); +// Message message = Message.withQualifier("q" + j).build(); +// client +// .send(server.address(), message) +// .subscribe( +// avoid -> iterSentTimeSeries.add(System.currentTimeMillis() - sentAt), +// th -> +// LOGGER.error( +// "Failed to send message in {} ms", +// System.currentTimeMillis() - sentAt, +// th)); +// } +// +// latch.await(20, TimeUnit.SECONDS); +// long iterationTime = System.currentTimeMillis() - startAt; +// if (i > 0) { // exclude warm up iteration +// iterationTimeSeries[i - 1] = iterationTime; +// } +// assertSendOrder(sentPerIteration, received); +// +// Thread.sleep(10); // await a bit for last msg confirmation +// +// LongSummaryStatistics iterSentTimeStats = +// iterSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); +// if (i == 0) { // warm up iteration +// LOGGER.debug("Warm up iteration time: {} ms", iterationTime); +// LOGGER.debug("Sent time stats warm up iter (ms): {}", iterSentTimeStats); +// } else { +// totalSentTimeSeries.addAll(iterSentTimeSeries); +// LongSummaryStatistics totalSentTimeStats = +// totalSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); +// LOGGER.debug("Iteration time: {} ms", iterationTime); +// LOGGER.debug("Sent time stats iter (ms): {}", iterSentTimeStats); +// LOGGER.debug("Sent time stats total (ms): {}", totalSentTimeStats); +// } +// +// serverSubscriber.dispose(); +// destroyTransport(client); +// } +// +// LongSummaryStatistics iterationTimeStats = +// LongStream.of(iterationTimeSeries).summaryStatistics(); +// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); +// } +// +// @Test +// public void testSendOrderMultiThread(TestInfo testInfo) throws Exception { +// Transport server = createTcpTransport(); +// +// final int total = 1000; +// for (int i = 0; i < 10; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// ExecutorService exec = +// Executors.newFixedThreadPool( +// 4, +// r -> { +// Thread thread = new Thread(r); +// thread.setName("testSendOrderMultiThread"); +// thread.setDaemon(true); +// return thread; +// }); +// +// Transport client = createTcpTransport(); +// final List received = new ArrayList<>(); +// final CountDownLatch latch = new CountDownLatch(4 * total); +// server +// .listen() +// .subscribe( +// message -> { +// received.add(message); +// latch.countDown(); +// }); +// +// final Future f0 = exec.submit(sender(0, client, server.address(), total)); +// final Future f1 = exec.submit(sender(1, client, server.address(), total)); +// final Future f2 = exec.submit(sender(2, client, server.address(), total)); +// final Future f3 = exec.submit(sender(3, client, server.address(), total)); +// +// latch.await(20, TimeUnit.SECONDS); +// +// f0.get(1, TimeUnit.SECONDS); +// f1.get(1, TimeUnit.SECONDS); +// f2.get(1, TimeUnit.SECONDS); +// f3.get(1, TimeUnit.SECONDS); +// +// exec.shutdownNow(); +// +// assertSenderOrder(0, total, received); +// assertSenderOrder(1, total, received); +// assertSenderOrder(2, total, received); +// assertSenderOrder(3, total, received); +// +// destroyTransport(client); +// } +// +// destroyTransport(client); +// destroyTransport(server); +// } +// +// private void assertSendOrder(int total, List received) { +// ArrayList messages = new ArrayList<>(received); +// assertEquals(total, messages.size()); +// for (int k = 0; k < total; k++) { +// assertEquals("q" + k, messages.get(k).qualifier()); +// } +// } +// +// private Callable sender(int id, Transport client, Address address, int total) { +// return () -> { +// for (int j = 0; j < total; j++) { +// String correlationId = id + "/" + j; +// try { +// Message message = Message.withQualifier("q").correlationId(correlationId).build(); +// client.send(address, message).block(Duration.ofSeconds(3)); +// } catch (Exception e) { +// LOGGER.error("Failed to send message: j = {} id = {}", j, id, e); +// throw Exceptions.propagate(e); +// } +// } +// return null; +// }; +// } +// +// private void assertSenderOrder(int id, int total, List received) { +// ArrayList messages = new ArrayList<>(received); +// Map> group = new HashMap<>(); +// for (Message message : messages) { +// Integer key = Integer.valueOf(message.correlationId().split("/")[0]); +// group.computeIfAbsent(key, ArrayList::new).add(message); +// } +// +// assertEquals(total, group.get(id).size()); +// for (int k = 0; k < total; k++) { +// assertEquals(id + "/" + k, group.get(id).get(k).correlationId()); +// } +// } +//} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java index ab17c5b1..0f8aa334 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java @@ -1,252 +1,252 @@ -package io.scalecube.transport.netty.websocket; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import io.scalecube.cluster.transport.api.Message; -import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.net.Address; -import io.scalecube.transport.netty.BaseTest; -import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.LongSummaryStatistics; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.LongStream; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import reactor.core.Disposable; -import reactor.core.Exceptions; - -public class WebsocketTransportSendOrderTest extends BaseTest { - - // Auto-destroyed on tear down - private Transport client; - private Transport server; - - /** Tear down. */ - @AfterEach - public final void tearDown() { - destroyTransport(client); - destroyTransport(server); - } - - @Test - public void testSendOrderSingleThreadWithoutPromises(TestInfo testInfo) throws Exception { - server = createWebsocketTransport(); - - int iterationNum = 11; // +1 warm up iteration - int sentPerIteration = 1000; - long[] iterationTimeSeries = new long[iterationNum - 1]; - for (int i = 0; i < iterationNum; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - - client = createWebsocketTransport(); - final List received = new ArrayList<>(); - final CountDownLatch latch = new CountDownLatch(sentPerIteration); - - final Disposable serverSubscriber = - server - .listen() - .subscribe( - message -> { - received.add(message); - latch.countDown(); - }); - - long startAt = System.currentTimeMillis(); - for (int j = 0; j < sentPerIteration; j++) { - Message message = Message.withQualifier("q" + j).build(); - client - .send(server.address(), message) - .subscribe(null, th -> LOGGER.error("Failed to send message", th)); - } - latch.await(20, TimeUnit.SECONDS); - long iterationTime = System.currentTimeMillis() - startAt; - if (i > 0) { // exclude warm up iteration - iterationTimeSeries[i - 1] = iterationTime; - } - assertSendOrder(sentPerIteration, received); - - LOGGER.debug("Iteration time: {} ms", iterationTime); - - serverSubscriber.dispose(); - destroyTransport(client); - } - - LongSummaryStatistics iterationTimeStats = - LongStream.of(iterationTimeSeries).summaryStatistics(); - LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); - } - - @Test - public void testSendOrderSingleThread(TestInfo testInfo) throws Exception { - server = createWebsocketTransport(); - - int iterationNum = 11; // +1 warm up iteration - int sentPerIteration = 1000; - long[] iterationTimeSeries = new long[iterationNum - 1]; - List totalSentTimeSeries = new ArrayList<>(sentPerIteration * (iterationNum - 1)); - for (int i = 0; i < iterationNum; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - List iterSentTimeSeries = new ArrayList<>(sentPerIteration); - - client = createWebsocketTransport(); - final List received = new ArrayList<>(); - final CountDownLatch latch = new CountDownLatch(sentPerIteration); - - final Disposable serverSubscriber = - server - .listen() - .subscribe( - message -> { - received.add(message); - latch.countDown(); - }); - - long startAt = System.currentTimeMillis(); - for (int j = 0; j < sentPerIteration; j++) { - long sentAt = System.currentTimeMillis(); - Message message = Message.withQualifier("q" + j).build(); - client - .send(server.address(), message) - .subscribe( - avoid -> iterSentTimeSeries.add(System.currentTimeMillis() - sentAt), - th -> - LOGGER.error( - "Failed to send message in {} ms", - System.currentTimeMillis() - sentAt, - th)); - } - - latch.await(20, TimeUnit.SECONDS); - long iterationTime = System.currentTimeMillis() - startAt; - if (i > 0) { // exclude warm up iteration - iterationTimeSeries[i - 1] = iterationTime; - } - assertSendOrder(sentPerIteration, received); - - Thread.sleep(10); // await a bit for last msg confirmation - - LongSummaryStatistics iterSentTimeStats = - iterSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); - if (i == 0) { // warm up iteration - LOGGER.debug("Warm up iteration time: {} ms", iterationTime); - LOGGER.debug("Sent time stats warm up iter (ms): {}", iterSentTimeStats); - } else { - totalSentTimeSeries.addAll(iterSentTimeSeries); - LongSummaryStatistics totalSentTimeStats = - totalSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); - LOGGER.debug("Iteration time: {} ms", iterationTime); - LOGGER.debug("Sent time stats iter (ms): {}", iterSentTimeStats); - LOGGER.debug("Sent time stats total (ms): {}", totalSentTimeStats); - } - - serverSubscriber.dispose(); - destroyTransport(client); - } - - LongSummaryStatistics iterationTimeStats = - LongStream.of(iterationTimeSeries).summaryStatistics(); - LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); - } - - @Test - public void testSendOrderMultiThread(TestInfo testInfo) throws Exception { - Transport server = createWebsocketTransport(); - - final int total = 1000; - for (int i = 0; i < 10; i++) { - LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); - ExecutorService exec = - Executors.newFixedThreadPool( - 4, - r -> { - Thread thread = new Thread(r); - thread.setName("testSendOrderMultiThread"); - thread.setDaemon(true); - return thread; - }); - - Transport client = createWebsocketTransport(); - final List received = new ArrayList<>(); - final CountDownLatch latch = new CountDownLatch(4 * total); - server - .listen() - .subscribe( - message -> { - received.add(message); - latch.countDown(); - }); - - final Future f0 = exec.submit(sender(0, client, server.address(), total)); - final Future f1 = exec.submit(sender(1, client, server.address(), total)); - final Future f2 = exec.submit(sender(2, client, server.address(), total)); - final Future f3 = exec.submit(sender(3, client, server.address(), total)); - - latch.await(20, TimeUnit.SECONDS); - - f0.get(1, TimeUnit.SECONDS); - f1.get(1, TimeUnit.SECONDS); - f2.get(1, TimeUnit.SECONDS); - f3.get(1, TimeUnit.SECONDS); - - exec.shutdownNow(); - - assertSenderOrder(0, total, received); - assertSenderOrder(1, total, received); - assertSenderOrder(2, total, received); - assertSenderOrder(3, total, received); - - destroyTransport(client); - } - - destroyTransport(client); - destroyTransport(server); - } - - private void assertSendOrder(int total, List received) { - ArrayList messages = new ArrayList<>(received); - assertEquals(total, messages.size()); - for (int k = 0; k < total; k++) { - assertEquals("q" + k, messages.get(k).qualifier()); - } - } - - private Callable sender(int id, Transport client, Address address, int total) { - return () -> { - for (int j = 0; j < total; j++) { - String correlationId = id + "/" + j; - try { - Message message = Message.withQualifier("q").correlationId(correlationId).build(); - client.send(address, message).block(Duration.ofSeconds(3)); - } catch (Exception e) { - LOGGER.error("Failed to send message: j = {} id = {}", j, id, e); - throw Exceptions.propagate(e); - } - } - return null; - }; - } - - private void assertSenderOrder(int id, int total, List received) { - ArrayList messages = new ArrayList<>(received); - Map> group = new HashMap<>(); - for (Message message : messages) { - Integer key = Integer.valueOf(message.correlationId().split("/")[0]); - group.computeIfAbsent(key, ArrayList::new).add(message); - } - - assertEquals(total, group.get(id).size()); - for (int k = 0; k < total; k++) { - assertEquals(id + "/" + k, group.get(id).get(k).correlationId()); - } - } -} +//package io.scalecube.transport.netty.websocket; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +// +//import io.scalecube.cluster.transport.api.Message; +//import io.scalecube.cluster.transport.api.Transport; +//import io.scalecube.net.Address; +//import io.scalecube.transport.netty.BaseTest; +//import java.time.Duration; +//import java.util.ArrayList; +//import java.util.HashMap; +//import java.util.List; +//import java.util.LongSummaryStatistics; +//import java.util.Map; +//import java.util.concurrent.Callable; +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.ExecutorService; +//import java.util.concurrent.Executors; +//import java.util.concurrent.Future; +//import java.util.concurrent.TimeUnit; +//import java.util.stream.LongStream; +//import org.junit.jupiter.api.AfterEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.TestInfo; +//import reactor.core.Disposable; +//import reactor.core.Exceptions; +// +//public class WebsocketTransportSendOrderTest extends BaseTest { +// +// // Auto-destroyed on tear down +// private Transport client; +// private Transport server; +// +// /** Tear down. */ +// @AfterEach +// public final void tearDown() { +// destroyTransport(client); +// destroyTransport(server); +// } +// +// @Test +// public void testSendOrderSingleThreadWithoutPromises(TestInfo testInfo) throws Exception { +// server = createWebsocketTransport(); +// +// int iterationNum = 11; // +1 warm up iteration +// int sentPerIteration = 1000; +// long[] iterationTimeSeries = new long[iterationNum - 1]; +// for (int i = 0; i < iterationNum; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// +// client = createWebsocketTransport(); +// final List received = new ArrayList<>(); +// final CountDownLatch latch = new CountDownLatch(sentPerIteration); +// +// final Disposable serverSubscriber = +// server +// .listen() +// .subscribe( +// message -> { +// received.add(message); +// latch.countDown(); +// }); +// +// long startAt = System.currentTimeMillis(); +// for (int j = 0; j < sentPerIteration; j++) { +// Message message = Message.withQualifier("q" + j).build(); +// client +// .send(server.address(), message) +// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); +// } +// latch.await(20, TimeUnit.SECONDS); +// long iterationTime = System.currentTimeMillis() - startAt; +// if (i > 0) { // exclude warm up iteration +// iterationTimeSeries[i - 1] = iterationTime; +// } +// assertSendOrder(sentPerIteration, received); +// +// LOGGER.debug("Iteration time: {} ms", iterationTime); +// +// serverSubscriber.dispose(); +// destroyTransport(client); +// } +// +// LongSummaryStatistics iterationTimeStats = +// LongStream.of(iterationTimeSeries).summaryStatistics(); +// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); +// } +// +// @Test +// public void testSendOrderSingleThread(TestInfo testInfo) throws Exception { +// server = createWebsocketTransport(); +// +// int iterationNum = 11; // +1 warm up iteration +// int sentPerIteration = 1000; +// long[] iterationTimeSeries = new long[iterationNum - 1]; +// List totalSentTimeSeries = new ArrayList<>(sentPerIteration * (iterationNum - 1)); +// for (int i = 0; i < iterationNum; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// List iterSentTimeSeries = new ArrayList<>(sentPerIteration); +// +// client = createWebsocketTransport(); +// final List received = new ArrayList<>(); +// final CountDownLatch latch = new CountDownLatch(sentPerIteration); +// +// final Disposable serverSubscriber = +// server +// .listen() +// .subscribe( +// message -> { +// received.add(message); +// latch.countDown(); +// }); +// +// long startAt = System.currentTimeMillis(); +// for (int j = 0; j < sentPerIteration; j++) { +// long sentAt = System.currentTimeMillis(); +// Message message = Message.withQualifier("q" + j).build(); +// client +// .send(server.address(), message) +// .subscribe( +// avoid -> iterSentTimeSeries.add(System.currentTimeMillis() - sentAt), +// th -> +// LOGGER.error( +// "Failed to send message in {} ms", +// System.currentTimeMillis() - sentAt, +// th)); +// } +// +// latch.await(20, TimeUnit.SECONDS); +// long iterationTime = System.currentTimeMillis() - startAt; +// if (i > 0) { // exclude warm up iteration +// iterationTimeSeries[i - 1] = iterationTime; +// } +// assertSendOrder(sentPerIteration, received); +// +// Thread.sleep(10); // await a bit for last msg confirmation +// +// LongSummaryStatistics iterSentTimeStats = +// iterSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); +// if (i == 0) { // warm up iteration +// LOGGER.debug("Warm up iteration time: {} ms", iterationTime); +// LOGGER.debug("Sent time stats warm up iter (ms): {}", iterSentTimeStats); +// } else { +// totalSentTimeSeries.addAll(iterSentTimeSeries); +// LongSummaryStatistics totalSentTimeStats = +// totalSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); +// LOGGER.debug("Iteration time: {} ms", iterationTime); +// LOGGER.debug("Sent time stats iter (ms): {}", iterSentTimeStats); +// LOGGER.debug("Sent time stats total (ms): {}", totalSentTimeStats); +// } +// +// serverSubscriber.dispose(); +// destroyTransport(client); +// } +// +// LongSummaryStatistics iterationTimeStats = +// LongStream.of(iterationTimeSeries).summaryStatistics(); +// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); +// } +// +// @Test +// public void testSendOrderMultiThread(TestInfo testInfo) throws Exception { +// Transport server = createWebsocketTransport(); +// +// final int total = 1000; +// for (int i = 0; i < 10; i++) { +// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); +// ExecutorService exec = +// Executors.newFixedThreadPool( +// 4, +// r -> { +// Thread thread = new Thread(r); +// thread.setName("testSendOrderMultiThread"); +// thread.setDaemon(true); +// return thread; +// }); +// +// Transport client = createWebsocketTransport(); +// final List received = new ArrayList<>(); +// final CountDownLatch latch = new CountDownLatch(4 * total); +// server +// .listen() +// .subscribe( +// message -> { +// received.add(message); +// latch.countDown(); +// }); +// +// final Future f0 = exec.submit(sender(0, client, server.address(), total)); +// final Future f1 = exec.submit(sender(1, client, server.address(), total)); +// final Future f2 = exec.submit(sender(2, client, server.address(), total)); +// final Future f3 = exec.submit(sender(3, client, server.address(), total)); +// +// latch.await(20, TimeUnit.SECONDS); +// +// f0.get(1, TimeUnit.SECONDS); +// f1.get(1, TimeUnit.SECONDS); +// f2.get(1, TimeUnit.SECONDS); +// f3.get(1, TimeUnit.SECONDS); +// +// exec.shutdownNow(); +// +// assertSenderOrder(0, total, received); +// assertSenderOrder(1, total, received); +// assertSenderOrder(2, total, received); +// assertSenderOrder(3, total, received); +// +// destroyTransport(client); +// } +// +// destroyTransport(client); +// destroyTransport(server); +// } +// +// private void assertSendOrder(int total, List received) { +// ArrayList messages = new ArrayList<>(received); +// assertEquals(total, messages.size()); +// for (int k = 0; k < total; k++) { +// assertEquals("q" + k, messages.get(k).qualifier()); +// } +// } +// +// private Callable sender(int id, Transport client, Address address, int total) { +// return () -> { +// for (int j = 0; j < total; j++) { +// String correlationId = id + "/" + j; +// try { +// Message message = Message.withQualifier("q").correlationId(correlationId).build(); +// client.send(address, message).block(Duration.ofSeconds(3)); +// } catch (Exception e) { +// LOGGER.error("Failed to send message: j = {} id = {}", j, id, e); +// throw Exceptions.propagate(e); +// } +// } +// return null; +// }; +// } +// +// private void assertSenderOrder(int id, int total, List received) { +// ArrayList messages = new ArrayList<>(received); +// Map> group = new HashMap<>(); +// for (Message message : messages) { +// Integer key = Integer.valueOf(message.correlationId().split("/")[0]); +// group.computeIfAbsent(key, ArrayList::new).add(message); +// } +// +// assertEquals(total, group.get(id).size()); +// for (int k = 0; k < total; k++) { +// assertEquals(id + "/" + k, group.get(id).get(k).correlationId()); +// } +// } +//} From 78aae1473494249cf06ca2d3c1938df643cce418 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 17:18:37 +0300 Subject: [PATCH 13/34] Set back mockito and junit to old versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ec37b81a..a105d8d3 100644 --- a/pom.xml +++ b/pom.xml @@ -42,8 +42,8 @@ 2020.0.32 2.15.1 - 5.3.1 - 5.9.3 + 4.6.1 + 5.8.2 1.3 https://maven.pkg.github.com/scalecube/scalecube-cluster From 78798e06b6b9822379323f636cab060e58d321d8 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 18:24:49 +0300 Subject: [PATCH 14/34] Added tests to TransportWrapperTest --- .../cluster/TransportWrapperTest.java | 333 ++++++++++++------ 1 file changed, 225 insertions(+), 108 deletions(-) diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index 854d49af..ee708747 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -38,127 +39,243 @@ class TransportWrapperTest { .data("" + System.currentTimeMillis()) .build(); - @Test - void requestResponseShouldWork() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); + @Nested + class RequestResponseTests { - when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); + @Test + void requestResponseShouldWork() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); + when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); - } + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); - @Test - void requestResponseShouldWorkMemberSingleAddress() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } - when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); + @Test + void requestResponseShouldWorkMemberSingleAddress() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); - } + when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - @Test - void requestResponseShouldWorkMemberTwoAddresses() { - final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(1), request)).thenReturn(Mono.just(response)); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); - } + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } - @Test - void requestResponseShouldWorkMemberThreeAddresses() { - final List
addresses = - Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(2), request)).thenReturn(Mono.just(response)); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); - } + @Test + void requestResponseShouldWorkMemberTwoAddresses() { + final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); - @Test - void requestResponseShouldFailMemberSingleAddress() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.requestResponse(addresses.get(1), request)).thenReturn(Mono.just(response)); - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); - } + @Test + void requestResponseShouldWorkMemberThreeAddresses() { + final List
addresses = + Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.requestResponse(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.requestResponse(addresses.get(2), request)).thenReturn(Mono.just(response)); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } + + @Test + void requestResponseShouldFailMemberSingleAddress() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); - @Test - void requestResponseShouldFailMemberTwoAddresses() { - final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 0"))); - when(transport.requestResponse(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } + + @Test + void requestResponseShouldFailMemberTwoAddresses() { + final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 0"))); + when(transport.requestResponse(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); + } + + @Test + void requestResponseShouldFailMemberThreeAddresses() { + final List
addresses = + Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 0"))); + when(transport.requestResponse(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + when(transport.requestResponse(addresses.get(2), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 2"))); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); + } } - @Test - void requestResponseShouldFailMemberThreeAddresses() { - final List
addresses = - Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); - final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 0"))); - when(transport.requestResponse(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - when(transport.requestResponse(addresses.get(2), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); + @Nested + class SendTests { + + @Test + void sendShouldWork() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); + + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + } + + @Test + void sendShouldWorkMemberSingleAddress() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); + + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + } + + @Test + void sendShouldWorkMemberTwoAddresses() { + final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.send(addresses.get(1), request)).thenReturn(Mono.empty()); + + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + } + + @Test + void sendShouldWorkMemberThreeAddresses() { + final List
addresses = + Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.send(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + when(transport.send(addresses.get(2), request)).thenReturn(Mono.empty()); + + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + } + + @Test + void sendShouldFailMemberSingleAddress() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } + + @Test + void sendShouldFailMemberTwoAddresses() { + final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 0"))); + when(transport.send(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); + } + + @Test + void sendShouldFailMemberThreeAddresses() { + final List
addresses = + Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); + final Member member = new Member("test", null, addresses, "namespace"); + final Transport transport = mock(Transport.class); + final TransportWrapper transportWrapper = new TransportWrapper(transport); + + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 0"))); + when(transport.send(addresses.get(1), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + when(transport.send(addresses.get(2), request)) + .thenReturn(Mono.error(new RuntimeException("Error - 2"))); + + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); + } } } From 9950b1c4f7c0ff1b2276d74818795612e08503a5 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 21:48:04 +0300 Subject: [PATCH 15/34] WIP --- .../scalecube/transport/netty/BaseTest.java | 109 ------ .../transport/netty/TcpTransportTest.java | 101 +++++ .../transport/netty/TransportTests.java | 343 ++++++++++++++++ .../netty/tcp/TcpTransportSendOrderTest.java | 252 ------------ .../transport/netty/tcp/TcpTransportTest.java | 366 ------------------ .../WebsocketTransportSendOrderTest.java | 252 ------------ .../websocket/WebsocketTransportTest.java | 346 ----------------- 7 files changed, 444 insertions(+), 1325 deletions(-) delete mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java create mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/TcpTransportTest.java create mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java delete mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java delete mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java delete mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java delete mode 100644 cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java deleted file mode 100644 index 2cb27b01..00000000 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/BaseTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.scalecube.transport.netty; - -import io.scalecube.cluster.transport.api.Message; -import io.scalecube.cluster.transport.api.Transport; -import io.scalecube.cluster.transport.api.TransportConfig; -import io.scalecube.cluster.utils.NetworkEmulatorTransport; -import io.scalecube.net.Address; -import io.scalecube.transport.netty.tcp.TcpTransportFactory; -import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; -import java.time.Duration; -import java.util.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.TestInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Mono; - -/** Base test class. */ -public class BaseTest { - - protected static final Logger LOGGER = LoggerFactory.getLogger(BaseTest.class); - - @BeforeEach - public final void baseSetUp(TestInfo testInfo) { - LOGGER.info("***** Test started : " + testInfo.getDisplayName() + " *****"); - } - - @AfterEach - public final void baseTearDown(TestInfo testInfo) { - LOGGER.info("***** Test finished : " + testInfo.getDisplayName() + " *****"); - } - - /** - * Sending message from src to destination. - * - * @param transport src - * @param to destination - * @param msg request - */ - protected Mono send(Transport transport, Address to, Message msg) { - return transport - .send(to, msg) - .doOnError( - th -> - LOGGER.error( - "Failed to send {} to {} from transport: {}, cause: {}", - msg, - to, - transport, - th.toString())); - } - -// /** -// * Sending message from src to destination. -// * -// * @param transport src -// * @param to destinations -// * @param msg request -// */ -// protected Mono send(Transport transport, List
to, Message msg) { -// return TransportWrapper.send(transport, to, msg) -// .doOnError( -// th -> -// LOGGER.error( -// "Failed to send {} to {} from transport: {}, cause: {}", -// msg, -// to, -// transport, -// th.toString())); -// } - - /** - * Stopping transport. - * - * @param transport trnasport object - */ - protected void destroyTransport(Transport transport) { - if (transport != null && !transport.isStopped()) { - try { - transport.stop().block(Duration.ofSeconds(1)); - } catch (Exception ex) { - LOGGER.warn("Failed to await transport termination: " + ex); - } - } - } - - /** - * Factory method to create a transport. - * - * @return tramsprot - */ - protected NetworkEmulatorTransport createTcpTransport() { - return new NetworkEmulatorTransport( - Transport.bindAwait( - TransportConfig.defaultConfig().transportFactory(new TcpTransportFactory()))); - } - - /** - * Factory method to create a transport. - * - * @return tramsprot - */ - protected NetworkEmulatorTransport createWebsocketTransport() { - return new NetworkEmulatorTransport( - Transport.bindAwait( - TransportConfig.defaultConfig().transportFactory(new WebsocketTransportFactory()))); - } -} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/TcpTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/TcpTransportTest.java new file mode 100644 index 00000000..4a21f74b --- /dev/null +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/TcpTransportTest.java @@ -0,0 +1,101 @@ +//package io.scalecube.transport.netty.tcp; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertNotNull; +//import static org.junit.jupiter.api.Assertions.assertNull; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static org.junit.jupiter.api.Assertions.fail; +// +//import io.scalecube.cluster.Member; +//import io.scalecube.cluster.TransportWrapper; +//import io.scalecube.cluster.transport.api.Message; +//import io.scalecube.cluster.utils.NetworkEmulatorTransport; +//import io.scalecube.net.Address; +//import io.scalecube.transport.netty.BaseTest; +//import java.io.IOException; +//import java.net.UnknownHostException; +//import java.time.Duration; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.CompletableFuture; +//import java.util.concurrent.TimeUnit; +//import java.util.concurrent.TimeoutException; +//import org.junit.jupiter.api.AfterEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.TestInfo; +//import reactor.core.publisher.Flux; +//import reactor.core.publisher.Sinks; +//import reactor.test.StepVerifier; +// +//public class TcpTransportTest extends BaseTest { +// +// public static final Duration TIMEOUT = Duration.ofSeconds(10); +// +// // Auto-destroyed on tear down +// private NetworkEmulatorTransport client; +// private NetworkEmulatorTransport server; +// +// /** Tear down. */ +// @AfterEach +// public final void tearDown() { +// destroyTransport(client); +// destroyTransport(server); +// } +// +// @Test +// public void testNetworkSettings() { +// client = createTcpTransport(); +// server = createTcpTransport(); +// +// int lostPercent = 50; +// int mean = 0; +// final Address address = server.address(); +// client.networkEmulator().outboundSettings(address, lostPercent, mean); +// +// final List serverMessageList = new ArrayList<>(); +// server.listen().subscribe(serverMessageList::add); +// +// int total = 1000; +// Flux.range(0, total) +// .flatMap( +// i -> +// client.send( +// address, Message.builder().sender(createMember(address)).data("q" + i).build())) +// .onErrorContinue((th, o) -> {}) +// .blockLast(TIMEOUT); +// +// int expectedMax = +// total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages +// int size = serverMessageList.size(); +// assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); +// } +// +// @Test +// public void testBlockAndUnblockTraffic() throws Exception { +// client = createTcpTransport(); +// server = createTcpTransport(); +// +// server +// .listen() +// .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); +// +// Sinks.Many responses = Sinks.many().replay().all(); +// client +// .listen() +// .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); +// +// // test at unblocked transport +// send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); +// +// // then block client->server messages +// Thread.sleep(1000); +// client.networkEmulator().blockOutbound(server.address()); +// send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); +// +// StepVerifier.create(responses.asFlux()) +// .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) +// .expectNoEvent(Duration.ofMillis(300)) +// .thenCancel() +// .verify(TIMEOUT); +// } +//} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java new file mode 100644 index 00000000..43e44893 --- /dev/null +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java @@ -0,0 +1,343 @@ +package io.scalecube.transport.netty; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import io.scalecube.cluster.Member; +import io.scalecube.cluster.TransportWrapper; +import io.scalecube.cluster.transport.api.Message; +import io.scalecube.cluster.transport.api.Transport; +import io.scalecube.cluster.transport.api.TransportConfig; +import io.scalecube.cluster.transport.api.TransportFactory; +import io.scalecube.net.Address; +import io.scalecube.transport.netty.tcp.TcpTransportFactory; +import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; +import java.io.IOException; +import java.net.UnknownHostException; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.StringJoiner; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; +import java.util.stream.Stream.Builder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +public class TransportTests { + + public static final Logger LOGGER = LoggerFactory.getLogger(TransportTests.class); + + public static final TransportConfig TRANSPORT_CONFIG = TransportConfig.defaultConfig(); + public static final String NS = "namespace"; + public static final Duration TIMEOUT = Duration.ofSeconds(3); + + private Context context; + + @BeforeEach + public final void baseSetUp(TestInfo testInfo) { + LOGGER.info("***** Test started : " + testInfo.getDisplayName() + " *****"); + } + + @AfterEach + void afterEach(TestInfo testInfo) { + if (context != null) { + context.close(); + } + + LOGGER.info("***** Test finished : " + testInfo.getDisplayName() + " *****"); + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testUnresolvedHostConnection(Context context) { + this.context = context; + + try { + Address address = Address.from("wronghost:49255"); + Message message = Message.builder().sender(createMember(address)).data("q").build(); + context.sender.send(address, message).block(Duration.ofSeconds(20)); + fail("fail"); + } catch (Exception e) { + assertEquals( + UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); + } + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testInteractWithNoConnection(Context context) { + this.context = context; + + Address serverAddress = Address.from("localhost:49255"); + Member sender = createMember(serverAddress); + + for (int i = 0; i < 10; i++) { + Transport transport = context.createTransport(); + + // create transport and don't wait just send message + try { + Message msg = Message.builder().sender(sender).data("q").build(); + transport.send(serverAddress, msg).block(TIMEOUT); + fail("fail"); + } catch (Exception e) { + assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); + } + + // send second message: no connection yet and it's clear that there's no connection + try { + Message msg = Message.builder().sender(sender).data("q").build(); + transport.send(serverAddress, msg).block(TIMEOUT); + fail("fail"); + } catch (Exception e) { + assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); + } + + transport.stop().block(TIMEOUT); + } + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testConnect() { + // TODO + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testConnectWithTransportWrapper() { + // TODO + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testPingPong(Context context) { + this.context = context; + + final Transport receiver = context.receiver; + final Member senderMember = context.senderMember; + final Member receiverMember = context.receiverMember; + + receiver + .listen() + .subscribe( + message -> { + final Member sender = message.sender(); + final List
addresses = sender.addresses(); + + assertEquals(senderMember, sender, "sender"); + + receiver + .send( + addresses.get(0), + Message.builder().sender(receiverMember).data("hi client").build()) + .subscribe(); + }); + + final Transport sender = context.sender; + CompletableFuture messageFuture = new CompletableFuture<>(); + sender.listen().subscribe(messageFuture::complete); + + sender + .send( + receiver.address(), Message.builder().sender(senderMember).data("hello server").build()) + .subscribe(); + + Message result = Mono.fromFuture(messageFuture).block(TIMEOUT); + assertNotNull(result, "No response from serverAddress"); + assertEquals("hi client", result.data()); + assertEquals(receiverMember, result.sender(), "sender"); + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testPingPongWithTransportWrapper(Context context) { + this.context = context; + + final Transport receiver = context.receiver; + final Member senderMember = context.senderMember; + final TransportWrapper receiverWrapper = context.receiverWrapper; + final Member receiverMember = context.receiverMember; + + receiver + .listen() + .subscribe( + message -> { + assertEquals(senderMember, message.sender(), "sender"); + + receiverWrapper + .send( + message.sender(), + Message.builder().sender(receiverMember).data("hi client").build()) + .subscribe(); + }); + + CompletableFuture messageFuture = new CompletableFuture<>(); + context.sender.listen().subscribe(messageFuture::complete); + + final Message ping = Message.builder().sender(senderMember).data("hello server").build(); + context.senderWrapper.send(receiverMember, ping).subscribe(); + + Message result = Mono.fromFuture(messageFuture).block(TIMEOUT); + assertNotNull(result, "No response from serverAddress"); + assertEquals("hi client", result.data()); + assertEquals(receiverMember, result.sender(), "sender"); + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testRequestResponse(Context context) { + this.context = context; + + final Transport receiver = context.receiver; + final Member senderMember = context.senderMember; + final Member receiverMember = context.receiverMember; + + receiver + .listen() + .filter(req -> req.qualifier().equals("hello/server")) + .subscribe( + message -> { + final Member sender = message.sender(); + final List
addresses = sender.addresses(); + + assertEquals(senderMember, sender, "sender"); + + receiver + .send( + addresses.get(0), + Message.builder() + .correlationId(message.correlationId()) + .sender(receiverMember) + .data("hello: " + message.data()) + .build()) + .subscribe(); + }); + + Message result = + context + .sender + .requestResponse( + receiver.address(), + Message.builder() + .qualifier("hello/server") + .correlationId("" + System.nanoTime()) + .sender(senderMember) + .data("server") + .build()) + .block(TIMEOUT); + + //noinspection ConstantConditions + assertEquals("hello: server", result.data().toString(), "data"); + assertEquals(receiverMember, result.sender(), "sender"); + } + + @ParameterizedTest + @MethodSource("transportContexts") + public void testRequestResponseWithTransportWrapper(Context context) { + this.context = context; + + final Transport receiver = context.receiver; + final Member senderMember = context.senderMember; + final TransportWrapper receiverWrapper = context.receiverWrapper; + final Member receiverMember = context.receiverMember; + + receiver + .listen() + .filter(req -> req.qualifier().equals("hello/server")) + .subscribe( + message -> { + assertEquals(senderMember, message.sender(), "sender"); + + receiverWrapper + .send( + message.sender(), + Message.builder() + .correlationId(message.correlationId()) + .sender(receiverMember) + .data("hello: " + message.data()) + .build()) + .subscribe(); + }); + + Message result = + context + .sender + .requestResponse( + receiver.address(), + Message.builder() + .qualifier("hello/server") + .correlationId("" + System.nanoTime()) + .sender(senderMember) + .data("server") + .build()) + .block(TIMEOUT); + + //noinspection ConstantConditions + assertEquals("hello: server", result.data().toString(), "data"); + assertEquals(receiverMember, result.sender(), "sender"); + } + + private static Stream transportContexts() { + final Builder builder = Stream.builder(); + + builder.add(Arguments.of(new Context(new TcpTransportFactory()))); + builder.add(Arguments.of(new Context(new WebsocketTransportFactory()))); + + return builder.build(); + } + + private static Member createMember(Address address) { + return new Member("0", null, address, "NAMESPACE"); + } + + private static class Context implements AutoCloseable { + + private final Transport receiver; + private final Transport sender; + private final Member receiverMember; + private final Member senderMember; + private final TransportFactory transportFactory; + private final TransportWrapper receiverWrapper; + private final TransportWrapper senderWrapper; + + public Context(TransportFactory transportFactory) { + this.transportFactory = transportFactory; + receiver = Transport.bindAwait(TRANSPORT_CONFIG.transportFactory(transportFactory)); + sender = Transport.bindAwait(TRANSPORT_CONFIG.transportFactory(transportFactory)); + + receiverMember = + new Member("receiver", null, Collections.singletonList(receiver.address()), NS); + senderMember = new Member("sender", null, Collections.singletonList(sender.address()), NS); + + receiverWrapper = new TransportWrapper(receiver); + senderWrapper = new TransportWrapper(sender); + } + + private Transport createTransport() { + return Transport.bindAwait(TRANSPORT_CONFIG.transportFactory(transportFactory)); + } + + @Override + public void close() { + receiver.stop().block(TIMEOUT); + sender.stop().block(TIMEOUT); + } + + @Override + public String toString() { + return new StringJoiner(", ", Context.class.getSimpleName() + "[", "]") + .add("transportFactory=" + transportFactory.getClass().getSimpleName()) + .toString(); + } + } +} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java deleted file mode 100644 index 9568c548..00000000 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportSendOrderTest.java +++ /dev/null @@ -1,252 +0,0 @@ -//package io.scalecube.transport.netty.tcp; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -// -//import io.scalecube.cluster.transport.api.Message; -//import io.scalecube.cluster.transport.api.Transport; -//import io.scalecube.net.Address; -//import io.scalecube.transport.netty.BaseTest; -//import java.time.Duration; -//import java.util.ArrayList; -//import java.util.HashMap; -//import java.util.List; -//import java.util.LongSummaryStatistics; -//import java.util.Map; -//import java.util.concurrent.Callable; -//import java.util.concurrent.CountDownLatch; -//import java.util.concurrent.ExecutorService; -//import java.util.concurrent.Executors; -//import java.util.concurrent.Future; -//import java.util.concurrent.TimeUnit; -//import java.util.stream.LongStream; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.TestInfo; -//import reactor.core.Disposable; -//import reactor.core.Exceptions; -// -//public class TcpTransportSendOrderTest extends BaseTest { -// -// // Auto-destroyed on tear down -// private Transport client; -// private Transport server; -// -// /** Tear down. */ -// @AfterEach -// public final void tearDown() { -// destroyTransport(client); -// destroyTransport(server); -// } -// -// @Test -// public void testSendOrderSingleThreadWithoutPromises(TestInfo testInfo) throws Exception { -// server = createTcpTransport(); -// -// int iterationNum = 11; // +1 warm up iteration -// int sentPerIteration = 1000; -// long[] iterationTimeSeries = new long[iterationNum - 1]; -// for (int i = 0; i < iterationNum; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// -// client = createTcpTransport(); -// final List received = new ArrayList<>(); -// final CountDownLatch latch = new CountDownLatch(sentPerIteration); -// -// final Disposable serverSubscriber = -// server -// .listen() -// .subscribe( -// message -> { -// received.add(message); -// latch.countDown(); -// }); -// -// long startAt = System.currentTimeMillis(); -// for (int j = 0; j < sentPerIteration; j++) { -// Message message = Message.withQualifier("q" + j).build(); -// client -// .send(server.address(), message) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// latch.await(20, TimeUnit.SECONDS); -// long iterationTime = System.currentTimeMillis() - startAt; -// if (i > 0) { // exclude warm up iteration -// iterationTimeSeries[i - 1] = iterationTime; -// } -// assertSendOrder(sentPerIteration, received); -// -// LOGGER.debug("Iteration time: {} ms", iterationTime); -// -// serverSubscriber.dispose(); -// destroyTransport(client); -// } -// -// LongSummaryStatistics iterationTimeStats = -// LongStream.of(iterationTimeSeries).summaryStatistics(); -// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); -// } -// -// @Test -// public void testSendOrderSingleThread(TestInfo testInfo) throws Exception { -// server = createTcpTransport(); -// -// int iterationNum = 11; // +1 warm up iteration -// int sentPerIteration = 1000; -// long[] iterationTimeSeries = new long[iterationNum - 1]; -// List totalSentTimeSeries = new ArrayList<>(sentPerIteration * (iterationNum - 1)); -// for (int i = 0; i < iterationNum; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// List iterSentTimeSeries = new ArrayList<>(sentPerIteration); -// -// client = createTcpTransport(); -// final List received = new ArrayList<>(); -// final CountDownLatch latch = new CountDownLatch(sentPerIteration); -// -// final Disposable serverSubscriber = -// server -// .listen() -// .subscribe( -// message -> { -// received.add(message); -// latch.countDown(); -// }); -// -// long startAt = System.currentTimeMillis(); -// for (int j = 0; j < sentPerIteration; j++) { -// long sentAt = System.currentTimeMillis(); -// Message message = Message.withQualifier("q" + j).build(); -// client -// .send(server.address(), message) -// .subscribe( -// avoid -> iterSentTimeSeries.add(System.currentTimeMillis() - sentAt), -// th -> -// LOGGER.error( -// "Failed to send message in {} ms", -// System.currentTimeMillis() - sentAt, -// th)); -// } -// -// latch.await(20, TimeUnit.SECONDS); -// long iterationTime = System.currentTimeMillis() - startAt; -// if (i > 0) { // exclude warm up iteration -// iterationTimeSeries[i - 1] = iterationTime; -// } -// assertSendOrder(sentPerIteration, received); -// -// Thread.sleep(10); // await a bit for last msg confirmation -// -// LongSummaryStatistics iterSentTimeStats = -// iterSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); -// if (i == 0) { // warm up iteration -// LOGGER.debug("Warm up iteration time: {} ms", iterationTime); -// LOGGER.debug("Sent time stats warm up iter (ms): {}", iterSentTimeStats); -// } else { -// totalSentTimeSeries.addAll(iterSentTimeSeries); -// LongSummaryStatistics totalSentTimeStats = -// totalSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); -// LOGGER.debug("Iteration time: {} ms", iterationTime); -// LOGGER.debug("Sent time stats iter (ms): {}", iterSentTimeStats); -// LOGGER.debug("Sent time stats total (ms): {}", totalSentTimeStats); -// } -// -// serverSubscriber.dispose(); -// destroyTransport(client); -// } -// -// LongSummaryStatistics iterationTimeStats = -// LongStream.of(iterationTimeSeries).summaryStatistics(); -// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); -// } -// -// @Test -// public void testSendOrderMultiThread(TestInfo testInfo) throws Exception { -// Transport server = createTcpTransport(); -// -// final int total = 1000; -// for (int i = 0; i < 10; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// ExecutorService exec = -// Executors.newFixedThreadPool( -// 4, -// r -> { -// Thread thread = new Thread(r); -// thread.setName("testSendOrderMultiThread"); -// thread.setDaemon(true); -// return thread; -// }); -// -// Transport client = createTcpTransport(); -// final List received = new ArrayList<>(); -// final CountDownLatch latch = new CountDownLatch(4 * total); -// server -// .listen() -// .subscribe( -// message -> { -// received.add(message); -// latch.countDown(); -// }); -// -// final Future f0 = exec.submit(sender(0, client, server.address(), total)); -// final Future f1 = exec.submit(sender(1, client, server.address(), total)); -// final Future f2 = exec.submit(sender(2, client, server.address(), total)); -// final Future f3 = exec.submit(sender(3, client, server.address(), total)); -// -// latch.await(20, TimeUnit.SECONDS); -// -// f0.get(1, TimeUnit.SECONDS); -// f1.get(1, TimeUnit.SECONDS); -// f2.get(1, TimeUnit.SECONDS); -// f3.get(1, TimeUnit.SECONDS); -// -// exec.shutdownNow(); -// -// assertSenderOrder(0, total, received); -// assertSenderOrder(1, total, received); -// assertSenderOrder(2, total, received); -// assertSenderOrder(3, total, received); -// -// destroyTransport(client); -// } -// -// destroyTransport(client); -// destroyTransport(server); -// } -// -// private void assertSendOrder(int total, List received) { -// ArrayList messages = new ArrayList<>(received); -// assertEquals(total, messages.size()); -// for (int k = 0; k < total; k++) { -// assertEquals("q" + k, messages.get(k).qualifier()); -// } -// } -// -// private Callable sender(int id, Transport client, Address address, int total) { -// return () -> { -// for (int j = 0; j < total; j++) { -// String correlationId = id + "/" + j; -// try { -// Message message = Message.withQualifier("q").correlationId(correlationId).build(); -// client.send(address, message).block(Duration.ofSeconds(3)); -// } catch (Exception e) { -// LOGGER.error("Failed to send message: j = {} id = {}", j, id, e); -// throw Exceptions.propagate(e); -// } -// } -// return null; -// }; -// } -// -// private void assertSenderOrder(int id, int total, List received) { -// ArrayList messages = new ArrayList<>(received); -// Map> group = new HashMap<>(); -// for (Message message : messages) { -// Integer key = Integer.valueOf(message.correlationId().split("/")[0]); -// group.computeIfAbsent(key, ArrayList::new).add(message); -// } -// -// assertEquals(total, group.get(id).size()); -// for (int k = 0; k < total; k++) { -// assertEquals(id + "/" + k, group.get(id).get(k).correlationId()); -// } -// } -//} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java deleted file mode 100644 index 12314882..00000000 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/tcp/TcpTransportTest.java +++ /dev/null @@ -1,366 +0,0 @@ -//package io.scalecube.transport.netty.tcp; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertNotNull; -//import static org.junit.jupiter.api.Assertions.assertNull; -//import static org.junit.jupiter.api.Assertions.assertTrue; -//import static org.junit.jupiter.api.Assertions.fail; -// -//import io.scalecube.cluster.Member; -//import io.scalecube.cluster.TransportWrapper; -//import io.scalecube.cluster.transport.api.Message; -//import io.scalecube.cluster.utils.NetworkEmulatorTransport; -//import io.scalecube.net.Address; -//import io.scalecube.transport.netty.BaseTest; -//import java.io.IOException; -//import java.net.UnknownHostException; -//import java.time.Duration; -//import java.util.ArrayList; -//import java.util.List; -//import java.util.concurrent.CompletableFuture; -//import java.util.concurrent.TimeUnit; -//import java.util.concurrent.TimeoutException; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.TestInfo; -//import reactor.core.publisher.Flux; -//import reactor.core.publisher.Sinks; -//import reactor.test.StepVerifier; -// -//public class TcpTransportTest extends BaseTest { -// -// public static final Duration TIMEOUT = Duration.ofSeconds(10); -// -// // Auto-destroyed on tear down -// private NetworkEmulatorTransport client; -// private NetworkEmulatorTransport server; -// -// /** Tear down. */ -// @AfterEach -// public final void tearDown() { -// destroyTransport(client); -// destroyTransport(server); -// } -// -// private static Member createMember(Address address) { -// return new Member("0", null, address, "NAMESPACE"); -// } -// -// @Test -// public void testUnresolvedHostConnection() { -// client = createTcpTransport(); -// // create transport with wrong host -// try { -// Address address = Address.from("wronghost:49255"); -// Member sender = createMember(address); -// Message message = Message.builder().sender(sender).data("q").build(); -// client.send(address, message).block(Duration.ofSeconds(20)); -// fail("fail"); -// } catch (Exception e) { -// assertEquals( -// UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); -// } -// } -// -// @Test -// public void testInteractWithNoConnection(TestInfo testInfo) { -// Address serverAddress = Address.from("localhost:49255"); -// Member sender = createMember(serverAddress); -// -// for (int i = 0; i < 10; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// -// client = createTcpTransport(); -// -// // create transport and don't wait just send message -// try { -// Message msg = Message.builder().sender(sender).data("q").build(); -// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); -// fail("fail"); -// } catch (Exception e) { -// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); -// } -// -// // send second message: no connection yet and it's clear that there's no connection -// try { -// Message msg = Message.builder().sender(sender).data("q").build(); -// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); -// fail("fail"); -// } catch (Exception e) { -// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); -// } -// -// destroyTransport(client); -// } -// } -// -// @Test -// public void testPingPongClientTfListenAndServerTfListen() throws Exception { -// client = createTcpTransport(); -// server = createTcpTransport(); -// -// server -// .listen() -// .subscribe( -// message -> { -// Member sender = message.sender(); -// final List
addresses = sender.addresses(); -// assertTrue( -// addresses.stream().anyMatch(a -> client.address().equals(a)), -// "Expected clientAddress"); -// send(server, addresses, Message.fromQualifier("hi client")).subscribe(); -// }); -// -// CompletableFuture messageFuture = new CompletableFuture<>(); -// client.listen().subscribe(messageFuture::complete); -// -// send(client, server.address(), Message.fromQualifier("hello server")).subscribe(); -// -// Message result = messageFuture.get(3, TimeUnit.SECONDS); -// assertNotNull(result, "No response from serverAddress"); -// assertEquals("hi client", result.qualifier()); -// } -// -// @Test -// public void testNetworkSettings() { -// client = createTcpTransport(); -// server = createTcpTransport(); -// -// int lostPercent = 50; -// int mean = 0; -// final Address address = server.address(); -// client.networkEmulator().outboundSettings(address, lostPercent, mean); -// -// final List serverMessageList = new ArrayList<>(); -// server.listen().subscribe(serverMessageList::add); -// -// int total = 1000; -// Flux.range(0, total) -// .flatMap( -// i -> -// client.send( -// address, Message.builder().sender(createMember(address)).data("q" + i).build())) -// .onErrorContinue((th, o) -> {}) -// .blockLast(TIMEOUT); -// -// int expectedMax = -// total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages -// int size = serverMessageList.size(); -// assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); -// } -// -// @Test -// public void testPingPongOnSingleChannel() throws Exception { -// server = createTcpTransport(); -// client = createTcpTransport(); -// final TransportWrapper transportWrapper = new TransportWrapper(server); -// -// server -// .listen() -// .buffer(2) -// .subscribe( -// messages -> { -// for (Message message : messages) { -// Message echo = -// Message.builder() -// .sender(createMember(server.address())) -// .data("echo/" + message.qualifier()) -// .build(); -// -// transportWrapper -// .send(message.sender(), echo) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// }); -// -// final CompletableFuture> targetFuture = new CompletableFuture<>(); -// client.listen().buffer(2).subscribe(targetFuture::complete); -// -// Message q1 = Message.withData("q1").build(); -// Message q2 = Message.withData("q2").build(); -// -// client -// .send(server.address(), q1) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// client -// .send(server.address(), q2) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// -// List target = targetFuture.get(1, TimeUnit.SECONDS); -// assertNotNull(target); -// assertEquals(2, target.size()); -// } -// -// @Test -// public void testShouldRequestResponseSuccess() { -// client = createTcpTransport(); -// server = createTcpTransport(); -// -// server -// .listen() -// .filter(req -> req.qualifier().equals("hello/server")) -// .subscribe( -// message -> -// send( -// server, -// message.sender(), -// Message.builder() -// .correlationId(message.correlationId()) -// .data("hello: " + message.data()) -// .build()) -// .subscribe()); -// -// String result = -// client -// .requestResponse( -// server.address(), -// Message.builder() -// .qualifier("hello/server") -// .correlationId("123xyz") -// .data("server") -// .build()) -// .map(msg -> msg.data().toString()) -// .block(Duration.ofSeconds(1)); -// -// assertEquals("hello: server", result); -// } -// -// @Test -// public void testPingPongOnSeparateChannel() throws Exception { -// server = createTcpTransport(); -// client = createTcpTransport(); -// -// server -// .listen() -// .buffer(2) -// .subscribe( -// messages -> { -// for (Message message : messages) { -// Message echo = Message.withData("echo/" + message.qualifier()).build(); -// TransportWrapper.send(server, message.sender(), echo) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// }); -// -// final CompletableFuture> targetFuture = new CompletableFuture<>(); -// client.listen().buffer(2).subscribe(targetFuture::complete); -// -// Message q1 = Message.withData("q1").build(); -// Message q2 = Message.withData("q2").build(); -// -// client -// .send(server.address(), q1) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// client -// .send(server.address(), q2) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// -// List target = targetFuture.get(1, TimeUnit.SECONDS); -// assertNotNull(target); -// assertEquals(2, target.size()); -// } -// -// @Test -// public void testCompleteObserver() throws Exception { -// server = createTcpTransport(); -// client = createTcpTransport(); -// -// final CompletableFuture completeLatch = new CompletableFuture<>(); -// final CompletableFuture messageLatch = new CompletableFuture<>(); -// -// server -// .listen() -// .subscribe( -// messageLatch::complete, -// errorConsumer -> { -// // no-op -// }, -// () -> completeLatch.complete(true)); -// -// client.send(server.address(), Message.withData("q").build()).block(Duration.ofSeconds(1)); -// -// assertNotNull(messageLatch.get(1, TimeUnit.SECONDS)); -// -// server.stop().block(TIMEOUT); -// -// assertTrue(completeLatch.get(1, TimeUnit.SECONDS)); -// } -// -// @Test -// public void testObserverThrowsException() throws Exception { -// server = createTcpTransport(); -// client = createTcpTransport(); -// -// server -// .listen() -// .subscribe( -// message -> { -// String qualifier = message.data(); -// if (qualifier.startsWith("throw")) { -// throw new RuntimeException("" + message); -// } -// if (qualifier.startsWith("q")) { -// Message echo = Message.withData("echo/" + message.qualifier()).build(); -// TransportWrapper.send(server, message.sender(), echo) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// }, -// Throwable::printStackTrace); -// -// // send "throw" and raise exception on server subscriber -// final CompletableFuture messageFuture0 = new CompletableFuture<>(); -// client.listen().subscribe(messageFuture0::complete); -// Message message = Message.withData("throw").build(); -// client -// .send(server.address(), message) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// Message message0 = null; -// try { -// message0 = messageFuture0.get(1, TimeUnit.SECONDS); -// } catch (TimeoutException e) { -// // ignore since expected behavior -// } -// assertNull(message0); -// -// // send normal message and check whether server subscriber is broken (no response) -// final CompletableFuture messageFuture1 = new CompletableFuture<>(); -// client.listen().subscribe(messageFuture1::complete); -// client.send(server.address(), Message.withData("q").build()); -// Message transportMessage1 = null; -// try { -// transportMessage1 = messageFuture1.get(1, TimeUnit.SECONDS); -// } catch (TimeoutException e) { -// // ignore since expected behavior -// } -// assertNull(transportMessage1); -// } -// -// @Test -// public void testBlockAndUnblockTraffic() throws Exception { -// client = createTcpTransport(); -// server = createTcpTransport(); -// -// server -// .listen() -// .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); -// -// Sinks.Many responses = Sinks.many().replay().all(); -// client -// .listen() -// .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); -// -// // test at unblocked transport -// send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); -// -// // then block client->server messages -// Thread.sleep(1000); -// client.networkEmulator().blockOutbound(server.address()); -// send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); -// -// StepVerifier.create(responses.asFlux()) -// .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) -// .expectNoEvent(Duration.ofMillis(300)) -// .thenCancel() -// .verify(TIMEOUT); -// } -//} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java deleted file mode 100644 index 0f8aa334..00000000 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportSendOrderTest.java +++ /dev/null @@ -1,252 +0,0 @@ -//package io.scalecube.transport.netty.websocket; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -// -//import io.scalecube.cluster.transport.api.Message; -//import io.scalecube.cluster.transport.api.Transport; -//import io.scalecube.net.Address; -//import io.scalecube.transport.netty.BaseTest; -//import java.time.Duration; -//import java.util.ArrayList; -//import java.util.HashMap; -//import java.util.List; -//import java.util.LongSummaryStatistics; -//import java.util.Map; -//import java.util.concurrent.Callable; -//import java.util.concurrent.CountDownLatch; -//import java.util.concurrent.ExecutorService; -//import java.util.concurrent.Executors; -//import java.util.concurrent.Future; -//import java.util.concurrent.TimeUnit; -//import java.util.stream.LongStream; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.TestInfo; -//import reactor.core.Disposable; -//import reactor.core.Exceptions; -// -//public class WebsocketTransportSendOrderTest extends BaseTest { -// -// // Auto-destroyed on tear down -// private Transport client; -// private Transport server; -// -// /** Tear down. */ -// @AfterEach -// public final void tearDown() { -// destroyTransport(client); -// destroyTransport(server); -// } -// -// @Test -// public void testSendOrderSingleThreadWithoutPromises(TestInfo testInfo) throws Exception { -// server = createWebsocketTransport(); -// -// int iterationNum = 11; // +1 warm up iteration -// int sentPerIteration = 1000; -// long[] iterationTimeSeries = new long[iterationNum - 1]; -// for (int i = 0; i < iterationNum; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// -// client = createWebsocketTransport(); -// final List received = new ArrayList<>(); -// final CountDownLatch latch = new CountDownLatch(sentPerIteration); -// -// final Disposable serverSubscriber = -// server -// .listen() -// .subscribe( -// message -> { -// received.add(message); -// latch.countDown(); -// }); -// -// long startAt = System.currentTimeMillis(); -// for (int j = 0; j < sentPerIteration; j++) { -// Message message = Message.withQualifier("q" + j).build(); -// client -// .send(server.address(), message) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// latch.await(20, TimeUnit.SECONDS); -// long iterationTime = System.currentTimeMillis() - startAt; -// if (i > 0) { // exclude warm up iteration -// iterationTimeSeries[i - 1] = iterationTime; -// } -// assertSendOrder(sentPerIteration, received); -// -// LOGGER.debug("Iteration time: {} ms", iterationTime); -// -// serverSubscriber.dispose(); -// destroyTransport(client); -// } -// -// LongSummaryStatistics iterationTimeStats = -// LongStream.of(iterationTimeSeries).summaryStatistics(); -// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); -// } -// -// @Test -// public void testSendOrderSingleThread(TestInfo testInfo) throws Exception { -// server = createWebsocketTransport(); -// -// int iterationNum = 11; // +1 warm up iteration -// int sentPerIteration = 1000; -// long[] iterationTimeSeries = new long[iterationNum - 1]; -// List totalSentTimeSeries = new ArrayList<>(sentPerIteration * (iterationNum - 1)); -// for (int i = 0; i < iterationNum; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// List iterSentTimeSeries = new ArrayList<>(sentPerIteration); -// -// client = createWebsocketTransport(); -// final List received = new ArrayList<>(); -// final CountDownLatch latch = new CountDownLatch(sentPerIteration); -// -// final Disposable serverSubscriber = -// server -// .listen() -// .subscribe( -// message -> { -// received.add(message); -// latch.countDown(); -// }); -// -// long startAt = System.currentTimeMillis(); -// for (int j = 0; j < sentPerIteration; j++) { -// long sentAt = System.currentTimeMillis(); -// Message message = Message.withQualifier("q" + j).build(); -// client -// .send(server.address(), message) -// .subscribe( -// avoid -> iterSentTimeSeries.add(System.currentTimeMillis() - sentAt), -// th -> -// LOGGER.error( -// "Failed to send message in {} ms", -// System.currentTimeMillis() - sentAt, -// th)); -// } -// -// latch.await(20, TimeUnit.SECONDS); -// long iterationTime = System.currentTimeMillis() - startAt; -// if (i > 0) { // exclude warm up iteration -// iterationTimeSeries[i - 1] = iterationTime; -// } -// assertSendOrder(sentPerIteration, received); -// -// Thread.sleep(10); // await a bit for last msg confirmation -// -// LongSummaryStatistics iterSentTimeStats = -// iterSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); -// if (i == 0) { // warm up iteration -// LOGGER.debug("Warm up iteration time: {} ms", iterationTime); -// LOGGER.debug("Sent time stats warm up iter (ms): {}", iterSentTimeStats); -// } else { -// totalSentTimeSeries.addAll(iterSentTimeSeries); -// LongSummaryStatistics totalSentTimeStats = -// totalSentTimeSeries.stream().mapToLong(v -> v).summaryStatistics(); -// LOGGER.debug("Iteration time: {} ms", iterationTime); -// LOGGER.debug("Sent time stats iter (ms): {}", iterSentTimeStats); -// LOGGER.debug("Sent time stats total (ms): {}", totalSentTimeStats); -// } -// -// serverSubscriber.dispose(); -// destroyTransport(client); -// } -// -// LongSummaryStatistics iterationTimeStats = -// LongStream.of(iterationTimeSeries).summaryStatistics(); -// LOGGER.debug("Iteration time stats (ms): {}", iterationTimeStats); -// } -// -// @Test -// public void testSendOrderMultiThread(TestInfo testInfo) throws Exception { -// Transport server = createWebsocketTransport(); -// -// final int total = 1000; -// for (int i = 0; i < 10; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// ExecutorService exec = -// Executors.newFixedThreadPool( -// 4, -// r -> { -// Thread thread = new Thread(r); -// thread.setName("testSendOrderMultiThread"); -// thread.setDaemon(true); -// return thread; -// }); -// -// Transport client = createWebsocketTransport(); -// final List received = new ArrayList<>(); -// final CountDownLatch latch = new CountDownLatch(4 * total); -// server -// .listen() -// .subscribe( -// message -> { -// received.add(message); -// latch.countDown(); -// }); -// -// final Future f0 = exec.submit(sender(0, client, server.address(), total)); -// final Future f1 = exec.submit(sender(1, client, server.address(), total)); -// final Future f2 = exec.submit(sender(2, client, server.address(), total)); -// final Future f3 = exec.submit(sender(3, client, server.address(), total)); -// -// latch.await(20, TimeUnit.SECONDS); -// -// f0.get(1, TimeUnit.SECONDS); -// f1.get(1, TimeUnit.SECONDS); -// f2.get(1, TimeUnit.SECONDS); -// f3.get(1, TimeUnit.SECONDS); -// -// exec.shutdownNow(); -// -// assertSenderOrder(0, total, received); -// assertSenderOrder(1, total, received); -// assertSenderOrder(2, total, received); -// assertSenderOrder(3, total, received); -// -// destroyTransport(client); -// } -// -// destroyTransport(client); -// destroyTransport(server); -// } -// -// private void assertSendOrder(int total, List received) { -// ArrayList messages = new ArrayList<>(received); -// assertEquals(total, messages.size()); -// for (int k = 0; k < total; k++) { -// assertEquals("q" + k, messages.get(k).qualifier()); -// } -// } -// -// private Callable sender(int id, Transport client, Address address, int total) { -// return () -> { -// for (int j = 0; j < total; j++) { -// String correlationId = id + "/" + j; -// try { -// Message message = Message.withQualifier("q").correlationId(correlationId).build(); -// client.send(address, message).block(Duration.ofSeconds(3)); -// } catch (Exception e) { -// LOGGER.error("Failed to send message: j = {} id = {}", j, id, e); -// throw Exceptions.propagate(e); -// } -// } -// return null; -// }; -// } -// -// private void assertSenderOrder(int id, int total, List received) { -// ArrayList messages = new ArrayList<>(received); -// Map> group = new HashMap<>(); -// for (Message message : messages) { -// Integer key = Integer.valueOf(message.correlationId().split("/")[0]); -// group.computeIfAbsent(key, ArrayList::new).add(message); -// } -// -// assertEquals(total, group.get(id).size()); -// for (int k = 0; k < total; k++) { -// assertEquals(id + "/" + k, group.get(id).get(k).correlationId()); -// } -// } -//} diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java deleted file mode 100644 index d9434490..00000000 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/websocket/WebsocketTransportTest.java +++ /dev/null @@ -1,346 +0,0 @@ -//package io.scalecube.transport.netty.websocket; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertNotNull; -//import static org.junit.jupiter.api.Assertions.assertNull; -//import static org.junit.jupiter.api.Assertions.assertTrue; -//import static org.junit.jupiter.api.Assertions.fail; -// -//import io.scalecube.cluster.transport.api.Message; -//import io.scalecube.cluster.transport.api.TransportWrapper; -//import io.scalecube.cluster.utils.NetworkEmulatorTransport; -//import io.scalecube.net.Address; -//import io.scalecube.transport.netty.BaseTest; -//import java.io.IOException; -//import java.net.UnknownHostException; -//import java.time.Duration; -//import java.util.ArrayList; -//import java.util.List; -//import java.util.concurrent.CompletableFuture; -//import java.util.concurrent.TimeUnit; -//import java.util.concurrent.TimeoutException; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.TestInfo; -//import reactor.core.publisher.Flux; -//import reactor.core.publisher.Sinks; -//import reactor.test.StepVerifier; -// -//public class WebsocketTransportTest extends BaseTest { -// -// public static final Duration TIMEOUT = Duration.ofSeconds(10); -// -// // Auto-destroyed on tear down -// private NetworkEmulatorTransport client; -// private NetworkEmulatorTransport server; -// -// /** Tear down. */ -// @AfterEach -// public final void tearDown() { -// destroyTransport(client); -// destroyTransport(server); -// } -// -// @Test -// public void testUnresolvedHostConnection() { -// client = createWebsocketTransport(); -// // create transport with wrong host -// try { -// Address address = Address.from("wronghost:49255"); -// Message message = Message.withData("q").build(); -// client.send(address, message).block(Duration.ofSeconds(20)); -// fail("fail"); -// } catch (Exception e) { -// assertEquals( -// UnknownHostException.class, e.getCause().getClass(), "Unexpected exception class"); -// } -// } -// -// @Test -// public void testInteractWithNoConnection(TestInfo testInfo) { -// Address serverAddress = Address.from("localhost:49255"); -// for (int i = 0; i < 10; i++) { -// LOGGER.debug("####### {} : iteration = {}", testInfo.getDisplayName(), i); -// -// client = createWebsocketTransport(); -// -// // create transport and don't wait just send message -// try { -// Message msg = Message.withData("q").build(); -// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); -// fail("fail"); -// } catch (Exception e) { -// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); -// } -// -// // send second message: no connection yet and it's clear that there's no connection -// try { -// Message msg = Message.withData("q").build(); -// client.send(serverAddress, msg).block(Duration.ofSeconds(3)); -// fail("fail"); -// } catch (Exception e) { -// assertTrue(e.getCause() instanceof IOException, "Unexpected exception type: " + e); -// } -// -// destroyTransport(client); -// } -// } -// -// @Test -// public void testPingPongClientTfListenAndServerTfListen() throws Exception { -// client = createWebsocketTransport(); -// server = createWebsocketTransport(); -// -// server -// .listen() -// .subscribe( -// message -> { -// List
addresses = message.sender(); -// assertTrue( -// addresses.stream().anyMatch(a -> client.address().equals(a)), -// "Expected clientAddress"); -// send(server, addresses, Message.fromQualifier("hi client")).subscribe(); -// }); -// -// CompletableFuture messageFuture = new CompletableFuture<>(); -// client.listen().subscribe(messageFuture::complete); -// -// send(client, server.address(), Message.fromQualifier("hello server")).subscribe(); -// -// Message result = messageFuture.get(3, TimeUnit.SECONDS); -// assertNotNull(result, "No response from serverAddress"); -// assertEquals("hi client", result.qualifier()); -// } -// -// @Test -// public void testNetworkSettings() { -// client = createWebsocketTransport(); -// server = createWebsocketTransport(); -// -// int lostPercent = 50; -// int mean = 0; -// client.networkEmulator().outboundSettings(server.address(), lostPercent, mean); -// -// final List serverMessageList = new ArrayList<>(); -// server.listen().subscribe(serverMessageList::add); -// -// int total = 1000; -// Flux.range(0, total) -// .flatMap(i -> client.send(server.address(), Message.withData("q" + i).build())) -// .onErrorContinue((th, o) -> {}) -// .blockLast(TIMEOUT); -// -// int expectedMax = -// total / 100 * lostPercent + total / 100 * 5; // +5% for maximum possible lost messages -// int size = serverMessageList.size(); -// assertTrue(size < expectedMax, "expectedMax=" + expectedMax + ", actual size=" + size); -// } -// -// @Test -// public void testPingPongOnSingleChannel() throws Exception { -// server = createWebsocketTransport(); -// client = createWebsocketTransport(); -// -// server -// .listen() -// .buffer(2) -// .subscribe( -// messages -> { -// for (Message message : messages) { -// Message echo = Message.withData("echo/" + message.qualifier()).build(); -// TransportWrapper.send(server, message.sender(), echo) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// }); -// -// final CompletableFuture> targetFuture = new CompletableFuture<>(); -// client.listen().buffer(2).subscribe(targetFuture::complete); -// -// Message q1 = Message.withData("q1").build(); -// Message q2 = Message.withData("q2").build(); -// -// client -// .send(server.address(), q1) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// client -// .send(server.address(), q2) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// -// List target = targetFuture.get(1, TimeUnit.SECONDS); -// assertNotNull(target); -// assertEquals(2, target.size()); -// } -// -// @Test -// public void testShouldRequestResponseSuccess() { -// client = createWebsocketTransport(); -// server = createWebsocketTransport(); -// -// server -// .listen() -// .filter(req -> req.qualifier().equals("hello/server")) -// .subscribe( -// message -> -// send( -// server, -// message.sender(), -// Message.builder() -// .correlationId(message.correlationId()) -// .data("hello: " + message.data()) -// .build()) -// .subscribe()); -// -// String result = -// client -// .requestResponse( -// server.address(), -// Message.builder() -// .qualifier("hello/server") -// .correlationId("123xyz") -// .data("server") -// .build()) -// .map(msg -> msg.data().toString()) -// .block(Duration.ofSeconds(1)); -// -// assertEquals("hello: server", result); -// } -// -// @Test -// public void testPingPongOnSeparateChannel() throws Exception { -// server = createWebsocketTransport(); -// client = createWebsocketTransport(); -// -// server -// .listen() -// .buffer(2) -// .subscribe( -// messages -> { -// for (Message message : messages) { -// Message echo = Message.withData("echo/" + message.qualifier()).build(); -// TransportWrapper.send(server, message.sender(), echo) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// }); -// -// final CompletableFuture> targetFuture = new CompletableFuture<>(); -// client.listen().buffer(2).subscribe(targetFuture::complete); -// -// Message q1 = Message.withData("q1").build(); -// Message q2 = Message.withData("q2").build(); -// -// client -// .send(server.address(), q1) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// client -// .send(server.address(), q2) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// -// List target = targetFuture.get(1, TimeUnit.SECONDS); -// assertNotNull(target); -// assertEquals(2, target.size()); -// } -// -// @Test -// public void testCompleteObserver() throws Exception { -// server = createWebsocketTransport(); -// client = createWebsocketTransport(); -// -// final CompletableFuture completeLatch = new CompletableFuture<>(); -// final CompletableFuture messageLatch = new CompletableFuture<>(); -// -// server -// .listen() -// .subscribe( -// messageLatch::complete, -// errorConsumer -> { -// // no-op -// }, -// () -> completeLatch.complete(true)); -// -// client.send(server.address(), Message.withData("q").build()).block(Duration.ofSeconds(1)); -// -// assertNotNull(messageLatch.get(1, TimeUnit.SECONDS)); -// -// server.stop().block(TIMEOUT); -// -// assertTrue(completeLatch.get(1, TimeUnit.SECONDS)); -// } -// -// @Test -// public void testObserverThrowsException() throws Exception { -// server = createWebsocketTransport(); -// client = createWebsocketTransport(); -// -// server -// .listen() -// .subscribe( -// message -> { -// String qualifier = message.data(); -// if (qualifier.startsWith("throw")) { -// throw new RuntimeException("" + message); -// } -// if (qualifier.startsWith("q")) { -// Message echo = Message.withData("echo/" + message.qualifier()).build(); -// TransportWrapper.send(server, message.sender(), echo) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// } -// }, -// Throwable::printStackTrace); -// -// // send "throw" and raise exception on server subscriber -// final CompletableFuture messageFuture0 = new CompletableFuture<>(); -// client.listen().subscribe(messageFuture0::complete); -// Message message = Message.withData("throw").build(); -// client -// .send(server.address(), message) -// .subscribe(null, th -> LOGGER.error("Failed to send message", th)); -// Message message0 = null; -// try { -// message0 = messageFuture0.get(1, TimeUnit.SECONDS); -// } catch (TimeoutException e) { -// // ignore since expected behavior -// } -// assertNull(message0); -// -// // send normal message and check whether server subscriber is broken (no response) -// final CompletableFuture messageFuture1 = new CompletableFuture<>(); -// client.listen().subscribe(messageFuture1::complete); -// client.send(server.address(), Message.withData("q").build()); -// Message transportMessage1 = null; -// try { -// transportMessage1 = messageFuture1.get(1, TimeUnit.SECONDS); -// } catch (TimeoutException e) { -// // ignore since expected behavior -// } -// assertNull(transportMessage1); -// } -// -// @Test -// public void testBlockAndUnblockTraffic() throws Exception { -// client = createWebsocketTransport(); -// server = createWebsocketTransport(); -// -// server -// .listen() -// .subscribe(message -> TransportWrapper.send(server, message.sender(), message).subscribe()); -// -// Sinks.Many responses = Sinks.many().replay().all(); -// client -// .listen() -// .subscribe(responses::tryEmitNext, responses::tryEmitError, responses::tryEmitComplete); -// -// // test at unblocked transport -// send(client, server.address(), Message.fromQualifier("q/unblocked")).subscribe(); -// -// // then block client->server messages -// Thread.sleep(1000); -// client.networkEmulator().blockOutbound(server.address()); -// send(client, server.address(), Message.fromQualifier("q/blocked")).subscribe(); -// -// StepVerifier.create(responses.asFlux()) -// .assertNext(message -> assertEquals("q/unblocked", message.qualifier())) -// .expectNoEvent(Duration.ofMillis(300)) -// .thenCancel() -// .verify(TIMEOUT); -// } -//} From 1b0adc06ab253bac62e74ad776da2fb4599cfe0e Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 22:18:48 +0300 Subject: [PATCH 16/34] WIP --- .../transport/netty/TransportTests.java | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java index 43e44893..9109f66e 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java @@ -32,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; public class TransportTests { @@ -108,14 +109,44 @@ public void testInteractWithNoConnection(Context context) { @ParameterizedTest @MethodSource("transportContexts") - public void testConnect() { - // TODO - } + public void testConnect(Context context) { + this.context = context; - @ParameterizedTest - @MethodSource("transportContexts") - public void testConnectWithTransportWrapper() { - // TODO + // Create client and try send msg to not-yet existing server + + final int serverPort = 4801; + final Transport client = context.createTransport(); + final Member clientMember = + new Member("client", null, Collections.singletonList(client.address()), NS); + final Member serverMember = + new Member( + "server", null, Collections.singletonList(Address.create("localhost", serverPort)), NS); + + // Verify error + + StepVerifier.create( + new TransportWrapper(client) + .send(serverMember, Message.builder().sender(clientMember).data("Hola!").build()) + .retry(2)) + .verifyError(); + + // Start server + + final Transport server = + Transport.bindAwait( + TransportConfig.defaultConfig() + .port(serverPort) + .transportFactory(context.transportFactory)); + + // Verify success + + StepVerifier.create( + new TransportWrapper(client) + .send(serverMember, Message.builder().sender(clientMember).data("Hola!").build())) + .verifyComplete(); + + client.stop().block(TIMEOUT); + server.stop().block(TIMEOUT); } @ParameterizedTest From c06a65f91035a554ab35d410766cac6cb67f98c5 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 22:19:47 +0300 Subject: [PATCH 17/34] WIP --- .../test/java/io/scalecube/transport/netty/TransportTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java index 9109f66e..d6fabb60 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java @@ -142,7 +142,8 @@ public void testConnect(Context context) { StepVerifier.create( new TransportWrapper(client) - .send(serverMember, Message.builder().sender(clientMember).data("Hola!").build())) + .send(serverMember, Message.builder().sender(clientMember).data("Hola!").build()) + .repeat(2)) .verifyComplete(); client.stop().block(TIMEOUT); From 90e2d8d609e68ec43fa4ad3c4e1dc6838decc08b Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 22:26:48 +0300 Subject: [PATCH 18/34] WIP --- .../io/scalecube/transport/netty/TransportTests.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java index d6fabb60..065894a0 100644 --- a/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java +++ b/cluster-tests/src/test/java/io/scalecube/transport/netty/TransportTests.java @@ -45,8 +45,8 @@ public class TransportTests { private Context context; @BeforeEach - public final void baseSetUp(TestInfo testInfo) { - LOGGER.info("***** Test started : " + testInfo.getDisplayName() + " *****"); + void beforeEach(TestInfo testInfo) { + LOGGER.info("***** Test started - " + testInfo.getDisplayName() + " *****"); } @AfterEach @@ -55,7 +55,7 @@ void afterEach(TestInfo testInfo) { context.close(); } - LOGGER.info("***** Test finished : " + testInfo.getDisplayName() + " *****"); + LOGGER.info("***** Test finished - " + testInfo.getDisplayName() + " *****"); } @ParameterizedTest @@ -122,7 +122,7 @@ public void testConnect(Context context) { new Member( "server", null, Collections.singletonList(Address.create("localhost", serverPort)), NS); - // Verify error + // Verify send-error on client StepVerifier.create( new TransportWrapper(client) @@ -138,7 +138,7 @@ public void testConnect(Context context) { .port(serverPort) .transportFactory(context.transportFactory)); - // Verify success + // Verify send-success on client StepVerifier.create( new TransportWrapper(client) From fca28ce5b9aa2c075b0d5949a574f62d0fdbdc0e Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 22:38:22 +0300 Subject: [PATCH 19/34] Enhance transport tests --- .../cluster/TransportWrapperTest.java | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index ee708747..bab79338 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -51,12 +51,7 @@ void requestResponseShouldWork() { when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -71,7 +66,7 @@ void requestResponseShouldWorkMemberSingleAddress() { when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -88,7 +83,7 @@ void requestResponseShouldWorkMemberTwoAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.requestResponse(addresses.get(1), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -108,7 +103,7 @@ void requestResponseShouldWorkMemberThreeAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.requestResponse(addresses.get(2), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -124,7 +119,7 @@ void requestResponseShouldFailMemberSingleAddress() { when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error", throwable.getMessage())); } @@ -141,7 +136,7 @@ void requestResponseShouldFailMemberTwoAddresses() { when(transport.requestResponse(addresses.get(1), request)) .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); } @@ -161,7 +156,7 @@ void requestResponseShouldFailMemberThreeAddresses() { when(transport.requestResponse(addresses.get(2), request)) .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - StepVerifier.create(transportWrapper.requestResponse(member, request)) + StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); } @@ -179,8 +174,7 @@ void sendShouldWork() { when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); } @Test @@ -192,7 +186,7 @@ void sendShouldWorkMemberSingleAddress() { when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); } @Test @@ -206,7 +200,7 @@ void sendShouldWorkMemberTwoAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.send(addresses.get(1), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); } @Test @@ -223,7 +217,7 @@ void sendShouldWorkMemberThreeAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.send(addresses.get(2), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); } @Test @@ -236,7 +230,7 @@ void sendShouldFailMemberSingleAddress() { when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); - StepVerifier.create(transportWrapper.send(member, request)) + StepVerifier.create(transportWrapper.send(member, request).retry(2)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error", throwable.getMessage())); } @@ -253,7 +247,7 @@ void sendShouldFailMemberTwoAddresses() { when(transport.send(addresses.get(1), request)) .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - StepVerifier.create(transportWrapper.send(member, request)) + StepVerifier.create(transportWrapper.send(member, request).retry(2)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); } @@ -273,7 +267,7 @@ void sendShouldFailMemberThreeAddresses() { when(transport.send(addresses.get(2), request)) .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - StepVerifier.create(transportWrapper.send(member, request)) + StepVerifier.create(transportWrapper.send(member, request).retry(2)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); } From 491f3ebf3f0e87d5d788606d8abd384cf98e93a7 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 22:40:14 +0300 Subject: [PATCH 20/34] Enhance transport tests --- transport-parent/transport-netty/pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/transport-parent/transport-netty/pom.xml b/transport-parent/transport-netty/pom.xml index a362787b..fcc4941f 100644 --- a/transport-parent/transport-netty/pom.xml +++ b/transport-parent/transport-netty/pom.xml @@ -22,14 +22,6 @@ io.projectreactor.netty reactor-netty - - - - ${project.groupId} - scalecube-cluster-testlib - ${project.version} - test - From 9ed5ba2751ede9d1be01f6bb22cf2c16d578bddd Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 22:41:31 +0300 Subject: [PATCH 21/34] Enhance transport tests --- examples/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index a90c00a3..1456aee9 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,11 +21,6 @@ scalecube-transport-netty ${project.version} - - io.scalecube - scalecube-cluster-testlib - ${project.version} - io.scalecube scalecube-codec-jackson-smile From 16fe20e7bb06f5ac192bc9d2b0dd9ae7b74f81d6 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Thu, 31 Aug 2023 23:08:06 +0300 Subject: [PATCH 22/34] Cosmetic changes --- .../io/scalecube/cluster/fdetector/FailureDetectorImpl.java | 2 +- .../io/scalecube/cluster/membership/MembershipProtocolImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index a63d255f..b2cb123b 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -42,11 +42,11 @@ public final class FailureDetectorImpl implements FailureDetector { private final Member localMember; private final Transport transport; private final FailureDetectorConfig config; + private final TransportWrapper transportWrapper; // State private final List pingMembers = new ArrayList<>(); - private final TransportWrapper transportWrapper; private long currentPeriod = 0; private int pingMemberIndex = 0; // index for sequential ping member selection diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index ee394b4d..e86f5bd1 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -53,7 +53,6 @@ public final class MembershipProtocolImpl implements MembershipProtocol { private static final Logger LOGGER = LoggerFactory.getLogger(MembershipProtocol.class); - private final TransportWrapper transportWrapper; private enum MembershipUpdateReason { FAILURE_DETECTOR_EVENT, @@ -80,6 +79,7 @@ private enum MembershipUpdateReason { private final FailureDetector failureDetector; private final GossipProtocol gossipProtocol; private final MetadataStore metadataStore; + private final TransportWrapper transportWrapper; // State From 837d09e743c983ab142cb0f7cea749a62d00c406 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 09:35:23 +0300 Subject: [PATCH 23/34] WIP --- .../cluster/TransportWrapperTest.java | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index bab79338..b3876d2f 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -39,6 +39,14 @@ class TransportWrapperTest { .data("" + System.currentTimeMillis()) .build(); + private final Transport transport; + private final TransportWrapper transportWrapper; + + public TransportWrapperTest() { + transport = mock(Transport.class); + transportWrapper = new TransportWrapper(transport); + } + @Nested class RequestResponseTests { @@ -46,8 +54,6 @@ class RequestResponseTests { void requestResponseShouldWork() { final List
addresses = Collections.singletonList(Address.from("test:0")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); @@ -57,12 +63,29 @@ void requestResponseShouldWork() { .verify(); } + @Test + void requestResponseShouldWorkThenFail() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.just(response)) + .thenReturn(Mono.error(new RuntimeException("Error"))); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } + @Test void requestResponseShouldWorkMemberSingleAddress() { final List
addresses = Collections.singletonList(Address.from("test:0")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); @@ -76,8 +99,6 @@ void requestResponseShouldWorkMemberSingleAddress() { void requestResponseShouldWorkMemberTwoAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); @@ -94,8 +115,6 @@ void requestResponseShouldWorkMemberThreeAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); @@ -113,8 +132,6 @@ void requestResponseShouldWorkMemberThreeAddresses() { void requestResponseShouldFailMemberSingleAddress() { final List
addresses = Collections.singletonList(Address.from("test:0")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); @@ -128,8 +145,6 @@ void requestResponseShouldFailMemberSingleAddress() { void requestResponseShouldFailMemberTwoAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error - 0"))); @@ -146,8 +161,6 @@ void requestResponseShouldFailMemberThreeAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error - 0"))); @@ -169,20 +182,31 @@ class SendTests { void sendShouldWork() { final List
addresses = Collections.singletonList(Address.from("test:0")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); } + @Test + void sendShouldWorkThenFail() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.empty()) + .thenReturn(Mono.error(new RuntimeException("Error"))); + + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } + @Test void sendShouldWorkMemberSingleAddress() { final List
addresses = Collections.singletonList(Address.from("test:0")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); @@ -193,8 +217,6 @@ void sendShouldWorkMemberSingleAddress() { void sendShouldWorkMemberTwoAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); @@ -208,8 +230,6 @@ void sendShouldWorkMemberThreeAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); @@ -224,8 +244,6 @@ void sendShouldWorkMemberThreeAddresses() { void sendShouldFailMemberSingleAddress() { final List
addresses = Collections.singletonList(Address.from("test:0")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); @@ -239,8 +257,6 @@ void sendShouldFailMemberSingleAddress() { void sendShouldFailMemberTwoAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error - 0"))); @@ -257,8 +273,6 @@ void sendShouldFailMemberThreeAddresses() { final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); final Member member = new Member("test", null, addresses, "namespace"); - final Transport transport = mock(Transport.class); - final TransportWrapper transportWrapper = new TransportWrapper(transport); when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error - 0"))); From 93cf26aea80e496685dddf2d45873650fd0a353b Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 11:23:11 +0300 Subject: [PATCH 24/34] Reverted TransportWrapper to its simplest version, fixed tests --- .../scalecube/cluster/TransportWrapper.java | 74 ++----------------- .../cluster/TransportWrapperTest.java | 28 +++---- .../membership/MembershipProtocolTest.java | 5 ++ 3 files changed, 25 insertions(+), 82 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index cbbc9185..9ae21faf 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -4,8 +4,6 @@ import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import reactor.core.publisher.Mono; @@ -13,8 +11,6 @@ public class TransportWrapper { private final Transport transport; - private final Map> connections = new ConcurrentHashMap<>(); - public TransportWrapper(Transport transport) { this.transport = transport; } @@ -27,37 +23,16 @@ public TransportWrapper(Transport transport) { * @return mono result */ public Mono requestResponse(Member member, Message request) { - return connections - .compute( - member, - (m, resultMono) -> { - if (resultMono == null) { - return requestResponse(member.addresses(), request); - } - return resultMono.flatMap( - result -> - transport - .requestResponse(result.address, request) - .map(message -> new Result(result.address, message))); - }) - .map(result -> result.message); - } - - private Mono requestResponse(List
addresses, Message request) { return Mono.defer( () -> { + final List
addresses = member.addresses(); final AtomicInteger currentIndex = new AtomicInteger(); return Mono.defer( () -> { - final int index = currentIndex.getAndIncrement(); - return transport.requestResponse(addresses.get(index), request); + final Address address = addresses.get(currentIndex.getAndIncrement()); + return transport.requestResponse(address, request); }) - .retry(addresses.size() - 1) - .map( - message -> { - final int index = currentIndex.get() - 1; - return new Result(addresses.get(index), message); - }); + .retry(addresses.size() - 1); }); } @@ -69,53 +44,16 @@ private Mono requestResponse(List
addresses, Message request) { * @return mono result */ public Mono send(Member member, Message request) { - return connections - .compute( - member, - (m, resultMono) -> { - if (resultMono == null) { - return send(member.addresses(), request); - } - return resultMono.flatMap( - result -> - transport - .send(result.address, request) - .thenReturn(new Result(result.address))); - }) - .then(); - } - - private Mono send(List
addresses, Message request) { return Mono.defer( () -> { + final List
addresses = member.addresses(); final AtomicInteger currentIndex = new AtomicInteger(); return Mono.defer( () -> { final int index = currentIndex.getAndIncrement(); return transport.send(addresses.get(index), request); }) - .retry(addresses.size() - 1) - .then( - Mono.fromCallable( - () -> { - final int index = currentIndex.get() - 1; - return new Result(addresses.get(index)); - })); + .retry(addresses.size() - 1); }); } - - private static class Result { - - private final Address address; - private final Message message; - - private Result(Address address) { - this(address, null); - } - - private Result(Address address, Message message) { - this.address = address; - this.message = message; - } - } } diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index b3876d2f..e4f181cf 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -57,7 +57,7 @@ void requestResponseShouldWork() { when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -89,7 +89,7 @@ void requestResponseShouldWorkMemberSingleAddress() { when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -104,7 +104,7 @@ void requestResponseShouldWorkMemberTwoAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.requestResponse(addresses.get(1), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -122,7 +122,7 @@ void requestResponseShouldWorkMemberThreeAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.requestResponse(addresses.get(2), request)).thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .assertNext(message -> Assertions.assertSame(response, message, "response")) .thenCancel() .verify(); @@ -136,7 +136,7 @@ void requestResponseShouldFailMemberSingleAddress() { when(transport.requestResponse(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error", throwable.getMessage())); } @@ -151,7 +151,7 @@ void requestResponseShouldFailMemberTwoAddresses() { when(transport.requestResponse(addresses.get(1), request)) .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); } @@ -169,7 +169,7 @@ void requestResponseShouldFailMemberThreeAddresses() { when(transport.requestResponse(addresses.get(2), request)) .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - StepVerifier.create(transportWrapper.requestResponse(member, request).retry(2)) + StepVerifier.create(transportWrapper.requestResponse(member, request)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); } @@ -185,7 +185,7 @@ void sendShouldWork() { when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); } @Test @@ -210,7 +210,7 @@ void sendShouldWorkMemberSingleAddress() { when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); } @Test @@ -222,7 +222,7 @@ void sendShouldWorkMemberTwoAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.send(addresses.get(1), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); } @Test @@ -237,7 +237,7 @@ void sendShouldWorkMemberThreeAddresses() { .thenReturn(Mono.error(new RuntimeException("Error"))); when(transport.send(addresses.get(2), request)).thenReturn(Mono.empty()); - StepVerifier.create(transportWrapper.send(member, request).retry(2)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); } @Test @@ -248,7 +248,7 @@ void sendShouldFailMemberSingleAddress() { when(transport.send(addresses.get(0), request)) .thenReturn(Mono.error(new RuntimeException("Error"))); - StepVerifier.create(transportWrapper.send(member, request).retry(2)) + StepVerifier.create(transportWrapper.send(member, request)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error", throwable.getMessage())); } @@ -263,7 +263,7 @@ void sendShouldFailMemberTwoAddresses() { when(transport.send(addresses.get(1), request)) .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - StepVerifier.create(transportWrapper.send(member, request).retry(2)) + StepVerifier.create(transportWrapper.send(member, request)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); } @@ -281,7 +281,7 @@ void sendShouldFailMemberThreeAddresses() { when(transport.send(addresses.get(2), request)) .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - StepVerifier.create(transportWrapper.send(member, request).retry(2)) + StepVerifier.create(transportWrapper.send(member, request)) .verifyErrorSatisfies( throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); } diff --git a/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java b/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java index 0678bd45..3e126fc6 100644 --- a/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/membership/MembershipProtocolTest.java @@ -122,6 +122,7 @@ public void testLeaveClusterCameBeforeAlive() { final Message leavingMessage = Message.builder() .qualifier(MembershipProtocolImpl.MEMBERSHIP_GOSSIP) + .sender(anotherMember) .data(leavingRecord) .build(); @@ -131,6 +132,7 @@ public void testLeaveClusterCameBeforeAlive() { final Message addedMessage = Message.builder() .qualifier(MembershipProtocolImpl.MEMBERSHIP_GOSSIP) + .sender(anotherMember) .data(addedRecord) .build(); @@ -165,6 +167,7 @@ public void testLeaveClusterOnly() { final Message leavingMessage = Message.builder() .qualifier(MembershipProtocolImpl.MEMBERSHIP_GOSSIP) + .sender(anotherMember) .data(leavingRecord) .build(); @@ -197,6 +200,7 @@ public void testLeaveClusterOnSuspectedNode() { final Message suspectMessage = Message.builder() .qualifier(MembershipProtocolImpl.MEMBERSHIP_GOSSIP) + .sender(anotherMember) .data(suspectedNode) .build(); @@ -208,6 +212,7 @@ public void testLeaveClusterOnSuspectedNode() { final Message leavingMessage = Message.builder() .qualifier(MembershipProtocolImpl.MEMBERSHIP_GOSSIP) + .sender(anotherMember) .data(leavingRecord) .build(); From ad9db196429a13e0ac9327341dcd5dec825063fe Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 12:02:09 +0300 Subject: [PATCH 25/34] WIP --- .../scalecube/cluster/TransportWrapper.java | 19 ++++++++++++++++--- .../cluster/TransportWrapperTest.java | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 9ae21faf..f207c994 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -4,6 +4,8 @@ import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import reactor.core.publisher.Mono; @@ -11,6 +13,8 @@ public class TransportWrapper { private final Transport transport; + private final Map addressIndexByMember = new ConcurrentHashMap<>(); + public TransportWrapper(Transport transport) { this.transport = transport; } @@ -26,13 +30,22 @@ public Mono requestResponse(Member member, Message request) { return Mono.defer( () -> { final List
addresses = member.addresses(); - final AtomicInteger currentIndex = new AtomicInteger(); + final int numRetries = addresses.size() - 1; + final Integer index = addressIndexByMember.getOrDefault(member, 0); + final AtomicInteger currentIndex = new AtomicInteger(index); + return Mono.defer( () -> { - final Address address = addresses.get(currentIndex.getAndIncrement()); + int increment = currentIndex.getAndIncrement(); + + if (increment == addresses.size()) { + currentIndex.set(increment = 0); + } + + final Address address = addresses.get(increment); return transport.requestResponse(address, request); }) - .retry(addresses.size() - 1); + .retry(numRetries); }); } diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index e4f181cf..8bd56214 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -50,6 +50,20 @@ public TransportWrapperTest() { @Nested class RequestResponseTests { + @Test + void requestResponseShouldWorkIndex2() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); + + when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); + + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + + } + @Test void requestResponseShouldWork() { final List
addresses = Collections.singletonList(Address.from("test:0")); From 0f16819d8d00013388ee03d191c5a44e7d583e35 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 13:03:48 +0300 Subject: [PATCH 26/34] WIP --- .../scalecube/cluster/TransportWrapper.java | 21 +- .../cluster/TransportWrapperTest.java | 366 ++++++++---------- 2 files changed, 176 insertions(+), 211 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index f207c994..16d46490 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -39,7 +39,8 @@ public Mono requestResponse(Member member, Message request) { int increment = currentIndex.getAndIncrement(); if (increment == addresses.size()) { - currentIndex.set(increment = 0); + increment = 0; + currentIndex.set(1); } final Address address = addresses.get(increment); @@ -60,13 +61,23 @@ public Mono send(Member member, Message request) { return Mono.defer( () -> { final List
addresses = member.addresses(); - final AtomicInteger currentIndex = new AtomicInteger(); + final int numRetries = addresses.size() - 1; + final Integer index = addressIndexByMember.getOrDefault(member, 0); + final AtomicInteger currentIndex = new AtomicInteger(index); + return Mono.defer( () -> { - final int index = currentIndex.getAndIncrement(); - return transport.send(addresses.get(index), request); + int increment = currentIndex.getAndIncrement(); + + if (increment == addresses.size()) { + increment = 0; + currentIndex.set(1); + } + + final Address address = addresses.get(increment); + return transport.send(address, request); }) - .retry(addresses.size() - 1); + .retry(numRetries); }); } } diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index 8bd56214..b7e111d3 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -6,12 +6,18 @@ import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.Transport; import io.scalecube.net.Address; -import java.util.Arrays; +import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import java.util.stream.Stream.Builder; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -47,257 +53,205 @@ public TransportWrapperTest() { transportWrapper = new TransportWrapper(transport); } - @Nested - class RequestResponseTests { + static Stream methodSource() { + final Builder builder = Stream.builder(); - @Test - void requestResponseShouldWorkIndex2() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); + // int size, int startIndex, int successIndex + for (int size = 0; size < 5; size++) { + populateBuilder(builder, size); } - @Test - void requestResponseShouldWork() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); + return builder.build(); + } - when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); + static void populateBuilder(Builder builder, int size) { + // int startIndex, int successIndex - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); + for (int startIndex = 0; startIndex < size; startIndex++) { + for (int successIndex = 0; successIndex < size; successIndex++) { + builder.add(Arguments.of(size, startIndex, successIndex)); + } } + } - @Test - void requestResponseShouldWorkThenFail() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.just(response)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); + private Map addressIndexByMember() + throws NoSuchFieldException, IllegalAccessException { + final Field field = TransportWrapper.class.getDeclaredField("addressIndexByMember"); + field.setAccessible(true); + //noinspection unchecked + return (Map) field.get(transportWrapper); + } - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + @ParameterizedTest + @MethodSource("methodSource") + void requestResponseShouldWorkByRoundRobin(int size, int startIndex, int successIndex) + throws Exception { + final List
addresses = new ArrayList<>(); + final Member member = new Member("test", null, addresses, "namespace"); + for (int i = 0; i < size; i++) { + addresses.add(Address.from("test:" + i)); } - @Test - void requestResponseShouldWorkMemberSingleAddress() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.requestResponse(addresses.get(0), request)).thenReturn(Mono.just(response)); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); + if (startIndex > 0) { + addressIndexByMember().put(member, startIndex); } - @Test - void requestResponseShouldWorkMemberTwoAddresses() { - final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(1), request)).thenReturn(Mono.just(response)); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); + for (int i = 0; i < size; i++) { + final Address address = addresses.get(i); + if (i == successIndex) { + when(transport.requestResponse(address, request)).thenReturn(Mono.just(response)); + } else { + when(transport.requestResponse(address, request)) + .thenReturn(Mono.error(new RuntimeException("Error - " + i))); + } } - @Test - void requestResponseShouldWorkMemberThreeAddresses() { - final List
addresses = - Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(2), request)).thenReturn(Mono.just(response)); + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); + } - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) - .thenCancel() - .verify(); - } + @Test + void requestResponseShouldWorkThenFail() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); - @Test - void requestResponseShouldFailMemberSingleAddress() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.just(response)) + .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); - } + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } - @Test - void requestResponseShouldFailMemberTwoAddresses() { - final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); - final Member member = new Member("test", null, addresses, "namespace"); + @Test + void requestResponseShouldFailThenWork() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 0"))); - when(transport.requestResponse(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + when(transport.requestResponse(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))) + .thenReturn(Mono.just(response)); - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); - } + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); - @Test - void requestResponseShouldFailMemberThreeAddresses() { - final List
addresses = - Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.requestResponse(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 0"))); - when(transport.requestResponse(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - when(transport.requestResponse(addresses.get(2), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - - StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); - } + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .assertNext(message -> Assertions.assertSame(response, message, "response")) + .thenCancel() + .verify(); } - @Nested - class SendTests { - - @Test - void sendShouldWork() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); - - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + @ParameterizedTest + @MethodSource("methodSource") + void requestResponseShouldFailByRoundRobin(int size, int startIndex, int ignore) + throws Exception { + final List
addresses = new ArrayList<>(); + final Member member = new Member("test", null, addresses, "namespace"); + for (int i = 0; i < size; i++) { + addresses.add(Address.from("test:" + i)); } - @Test - void sendShouldWorkThenFail() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); + if (startIndex > 0) { + addressIndexByMember().put(member, startIndex); + } - when(transport.send(addresses.get(0), request)) - .thenReturn(Mono.empty()) + for (int i = 0; i < size; i++) { + final Address address = addresses.get(i); + when(transport.requestResponse(address, request)) .thenReturn(Mono.error(new RuntimeException("Error"))); - - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); - StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); } - @Test - void sendShouldWorkMemberSingleAddress() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); + StepVerifier.create(transportWrapper.requestResponse(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } - when(transport.send(addresses.get(0), request)).thenReturn(Mono.empty()); + @ParameterizedTest + @MethodSource("methodSource") + void sendShouldWorkByRoundRobin(int size, int startIndex, int successIndex) throws Exception { + final List
addresses = new ArrayList<>(); + final Member member = new Member("test", null, addresses, "namespace"); + for (int i = 0; i < size; i++) { + addresses.add(Address.from("test:" + i)); + } - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + if (startIndex > 0) { + addressIndexByMember().put(member, startIndex); } - @Test - void sendShouldWorkMemberTwoAddresses() { - final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); - final Member member = new Member("test", null, addresses, "namespace"); + for (int i = 0; i < size; i++) { + final Address address = addresses.get(i); + if (i == successIndex) { + when(transport.send(address, request)).thenReturn(Mono.empty()); + } else { + when(transport.send(address, request)) + .thenReturn(Mono.error(new RuntimeException("Error - " + i))); + } + } - when(transport.send(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.send(addresses.get(1), request)).thenReturn(Mono.empty()); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + } - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + @ParameterizedTest + @MethodSource("methodSource") + void sendShouldFailByRoundRobin(int size, int startIndex, int ignore) throws Exception { + final List
addresses = new ArrayList<>(); + final Member member = new Member("test", null, addresses, "namespace"); + for (int i = 0; i < size; i++) { + addresses.add(Address.from("test:" + i)); } - @Test - void sendShouldWorkMemberThreeAddresses() { - final List
addresses = - Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.send(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.send(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); - when(transport.send(addresses.get(2), request)).thenReturn(Mono.empty()); + if (startIndex > 0) { + addressIndexByMember().put(member, startIndex); + } - StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + for (int i = 0; i < size; i++) { + final Address address = addresses.get(i); + when(transport.send(address, request)).thenReturn(Mono.error(new RuntimeException("Error"))); } - @Test - void sendShouldFailMemberSingleAddress() { - final List
addresses = Collections.singletonList(Address.from("test:0")); - final Member member = new Member("test", null, addresses, "namespace"); + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } - when(transport.send(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error"))); + @Test + void sendShouldWorkThenFail() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); - StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); - } + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.empty()) + .thenReturn(Mono.error(new RuntimeException("Error"))); - @Test - void sendShouldFailMemberTwoAddresses() { - final List
addresses = Arrays.asList(Address.from("test:0"), Address.from("test:1")); - final Member member = new Member("test", null, addresses, "namespace"); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + } - when(transport.send(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 0"))); - when(transport.send(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 1"))); + @Test + void sendShouldFailThenWork() { + final List
addresses = Collections.singletonList(Address.from("test:0")); + final Member member = new Member("test", null, addresses, "namespace"); - StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error - 1", throwable.getMessage())); - } + when(transport.send(addresses.get(0), request)) + .thenReturn(Mono.error(new RuntimeException("Error"))) + .thenReturn(Mono.empty()); - @Test - void sendShouldFailMemberThreeAddresses() { - final List
addresses = - Arrays.asList(Address.from("test:0"), Address.from("test:1"), Address.from("test:2")); - final Member member = new Member("test", null, addresses, "namespace"); - - when(transport.send(addresses.get(0), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 0"))); - when(transport.send(addresses.get(1), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 1"))); - when(transport.send(addresses.get(2), request)) - .thenReturn(Mono.error(new RuntimeException("Error - 2"))); - - StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error - 2", throwable.getMessage())); - } + StepVerifier.create(transportWrapper.send(member, request)) + .verifyErrorSatisfies( + throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); } } From 43c0ea79243d10c39719bb36074a1042264a1137 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 13:35:04 +0300 Subject: [PATCH 27/34] Done with TransportWrapperTest.java --- .../scalecube/cluster/TransportWrapper.java | 70 ++++++++----------- .../cluster/TransportWrapperTest.java | 13 ++-- 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index 16d46490..c37bc4e7 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -13,7 +13,7 @@ public class TransportWrapper { private final Transport transport; - private final Map addressIndexByMember = new ConcurrentHashMap<>(); + private final Map addressIndexByMember = new ConcurrentHashMap<>(); public TransportWrapper(Transport transport) { this.transport = transport; @@ -27,27 +27,21 @@ public TransportWrapper(Transport transport) { * @return mono result */ public Mono requestResponse(Member member, Message request) { + final List
addresses = member.addresses(); + final AtomicInteger currentIndex = + addressIndexByMember.computeIfAbsent(member, m -> new AtomicInteger()); return Mono.defer( - () -> { - final List
addresses = member.addresses(); - final int numRetries = addresses.size() - 1; - final Integer index = addressIndexByMember.getOrDefault(member, 0); - final AtomicInteger currentIndex = new AtomicInteger(index); - - return Mono.defer( - () -> { - int increment = currentIndex.getAndIncrement(); - - if (increment == addresses.size()) { - increment = 0; - currentIndex.set(1); - } - - final Address address = addresses.get(increment); - return transport.requestResponse(address, request); - }) - .retry(numRetries); - }); + () -> { + synchronized (this) { + if (currentIndex.get() == addresses.size()) { + currentIndex.set(0); + } + final Address address = addresses.get(currentIndex.getAndIncrement()); + return transport.requestResponse(address, request); + } + }) + .retry(addresses.size() - 1) + .doOnError(throwable -> addressIndexByMember.remove(member, currentIndex)); } /** @@ -58,26 +52,20 @@ public Mono requestResponse(Member member, Message request) { * @return mono result */ public Mono send(Member member, Message request) { + final List
addresses = member.addresses(); + final AtomicInteger currentIndex = + addressIndexByMember.computeIfAbsent(member, m -> new AtomicInteger()); return Mono.defer( - () -> { - final List
addresses = member.addresses(); - final int numRetries = addresses.size() - 1; - final Integer index = addressIndexByMember.getOrDefault(member, 0); - final AtomicInteger currentIndex = new AtomicInteger(index); - - return Mono.defer( - () -> { - int increment = currentIndex.getAndIncrement(); - - if (increment == addresses.size()) { - increment = 0; - currentIndex.set(1); - } - - final Address address = addresses.get(increment); - return transport.send(address, request); - }) - .retry(numRetries); - }); + () -> { + synchronized (this) { + if (currentIndex.get() == addresses.size()) { + currentIndex.set(0); + } + final Address address = addresses.get(currentIndex.getAndIncrement()); + return transport.send(address, request); + } + }) + .retry(addresses.size() - 1) + .doOnError(throwable -> addressIndexByMember.remove(member, currentIndex)); } } diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index b7e111d3..eb0882bc 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import java.util.stream.Stream.Builder; import org.junit.jupiter.api.Assertions; @@ -75,12 +76,12 @@ static void populateBuilder(Builder builder, int size) { } } - private Map addressIndexByMember() + private Map addressIndexByMember() throws NoSuchFieldException, IllegalAccessException { final Field field = TransportWrapper.class.getDeclaredField("addressIndexByMember"); field.setAccessible(true); //noinspection unchecked - return (Map) field.get(transportWrapper); + return (Map) field.get(transportWrapper); } @ParameterizedTest @@ -94,7 +95,7 @@ void requestResponseShouldWorkByRoundRobin(int size, int startIndex, int success } if (startIndex > 0) { - addressIndexByMember().put(member, startIndex); + addressIndexByMember().put(member, new AtomicInteger(startIndex)); } for (int i = 0; i < size; i++) { @@ -162,7 +163,7 @@ void requestResponseShouldFailByRoundRobin(int size, int startIndex, int ignore) } if (startIndex > 0) { - addressIndexByMember().put(member, startIndex); + addressIndexByMember().put(member, new AtomicInteger(startIndex)); } for (int i = 0; i < size; i++) { @@ -186,7 +187,7 @@ void sendShouldWorkByRoundRobin(int size, int startIndex, int successIndex) thro } if (startIndex > 0) { - addressIndexByMember().put(member, startIndex); + addressIndexByMember().put(member, new AtomicInteger(startIndex)); } for (int i = 0; i < size; i++) { @@ -212,7 +213,7 @@ void sendShouldFailByRoundRobin(int size, int startIndex, int ignore) throws Exc } if (startIndex > 0) { - addressIndexByMember().put(member, startIndex); + addressIndexByMember().put(member, new AtomicInteger(startIndex)); } for (int i = 0; i < size; i++) { From 249bd7661024ae3092d68dfd0ee0645a59e7a72b Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 13:37:49 +0300 Subject: [PATCH 28/34] Fixed unit test JacksonSmileMessageCodecTest --- .../smile/JacksonSmileMessageCodecTest.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/codec-parent/codec-jackson-smile/src/test/java/io/scalecube/cluster/codec/jackson/smile/JacksonSmileMessageCodecTest.java b/codec-parent/codec-jackson-smile/src/test/java/io/scalecube/cluster/codec/jackson/smile/JacksonSmileMessageCodecTest.java index 36e1dcd8..a1bd5146 100644 --- a/codec-parent/codec-jackson-smile/src/test/java/io/scalecube/cluster/codec/jackson/smile/JacksonSmileMessageCodecTest.java +++ b/codec-parent/codec-jackson-smile/src/test/java/io/scalecube/cluster/codec/jackson/smile/JacksonSmileMessageCodecTest.java @@ -3,8 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.MessageCodec; +import io.scalecube.net.Address; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; @@ -17,12 +19,13 @@ class JacksonSmileMessageCodecTest { private static final MessageCodec messageCodec = MessageCodec.INSTANCE; private static final Random random = new Random(); + private static final Member member = new Member("0", null, Address.NULL_ADDRESS, "NAMESPACE"); @Test void serializeAndDeserializeByteBuffer() throws Exception { byte[] bytes = "hello".getBytes(); - Message to = Message.builder().data(new Entity(ByteBuffer.wrap(bytes))).build(); + Message to = Message.builder().sender(member).data(new Entity(ByteBuffer.wrap(bytes))).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -42,7 +45,7 @@ void serializeAndDeserializeDirectByteBuffer() throws Exception { byteBuffer.put(bytes); byteBuffer.flip(); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -57,7 +60,7 @@ void serializeAndDeserializeDirectByteBuffer() throws Exception { void serializeAndDeserializeEmptyByteBuffer() throws Exception { byte[] bytes = new byte[0]; - Message to = Message.builder().data(new Entity(ByteBuffer.wrap(bytes))).build(); + Message to = Message.builder().sender(member).data(new Entity(ByteBuffer.wrap(bytes))).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -82,7 +85,7 @@ void serializeAndDeserializeByteBufferWithOffset() throws Exception { assertEquals(offset, byteBuffer.position()); assertEquals(bytes.length - offset, byteBuffer.remaining()); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -106,7 +109,7 @@ void serializeAndDeserializeByteBufferWithOffsetSlice() throws Exception { assertEquals(0, byteBuffer.position()); assertEquals(bytes.length - offset, byteBuffer.remaining()); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -134,7 +137,7 @@ void serializeAndDeserializeDirectByteBufferWithOffset() throws Exception { assertEquals(offset, byteBuffer.position()); assertEquals(bytes.length - offset, byteBuffer.remaining()); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -162,7 +165,7 @@ void serializeAndDeserializeDirectByteBufferWithOffsetSlice() throws Exception { assertEquals(0, slice.position()); assertEquals(bytes.length - offset, slice.remaining()); - Message to = Message.builder().data(new Entity(slice)).build(); + Message to = Message.builder().sender(member).data(new Entity(slice)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -178,7 +181,7 @@ void serializeAndDeserializeDirectByteBufferWithOffsetSlice() throws Exception { void serializeAndDeserializeByteBufferWithoutEntity() throws Exception { byte[] bytes = "hello".getBytes(); - Message to = Message.builder().data(ByteBuffer.wrap(bytes)).build(); + Message to = Message.builder().sender(member).data(ByteBuffer.wrap(bytes)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); From 212808f11da9c0bd5e2a95e9612d6034eaa77ce9 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 13:46:28 +0300 Subject: [PATCH 29/34] Fixed unit test JacksonMessageCodecTest --- .../jackson/JacksonMessageCodecTest.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/codec-parent/codec-jackson/src/test/java/io/scalecube/cluster/codec/jackson/JacksonMessageCodecTest.java b/codec-parent/codec-jackson/src/test/java/io/scalecube/cluster/codec/jackson/JacksonMessageCodecTest.java index 191981d5..42898233 100644 --- a/codec-parent/codec-jackson/src/test/java/io/scalecube/cluster/codec/jackson/JacksonMessageCodecTest.java +++ b/codec-parent/codec-jackson/src/test/java/io/scalecube/cluster/codec/jackson/JacksonMessageCodecTest.java @@ -3,8 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import io.scalecube.cluster.Member; import io.scalecube.cluster.transport.api.Message; import io.scalecube.cluster.transport.api.MessageCodec; +import io.scalecube.net.Address; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; @@ -17,12 +19,13 @@ class JacksonMessageCodecTest { private static final MessageCodec messageCodec = MessageCodec.INSTANCE; private static final Random random = new Random(); + private static final Member member = new Member("0", null, Address.NULL_ADDRESS, "NAMESPACE"); @Test void serializeAndDeserializeByteBuffer() throws Exception { byte[] bytes = "hello".getBytes(); - Message to = Message.builder().data(new Entity(ByteBuffer.wrap(bytes))).build(); + Message to = Message.builder().sender(member).data(new Entity(ByteBuffer.wrap(bytes))).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -42,7 +45,7 @@ void serializeAndDeserializeDirectByteBuffer() throws Exception { byteBuffer.put(bytes); byteBuffer.flip(); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -57,7 +60,7 @@ void serializeAndDeserializeDirectByteBuffer() throws Exception { void serializeAndDeserializeEmptyByteBuffer() throws Exception { byte[] bytes = new byte[0]; - Message to = Message.builder().data(new Entity(ByteBuffer.wrap(bytes))).build(); + Message to = Message.builder().sender(member).data(new Entity(ByteBuffer.wrap(bytes))).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -82,7 +85,7 @@ void serializeAndDeserializeByteBufferWithOffset() throws Exception { assertEquals(offset, byteBuffer.position()); assertEquals(bytes.length - offset, byteBuffer.remaining()); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -106,7 +109,7 @@ void serializeAndDeserializeByteBufferWithOffsetSlice() throws Exception { assertEquals(0, byteBuffer.position()); assertEquals(bytes.length - offset, byteBuffer.remaining()); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -134,7 +137,7 @@ void serializeAndDeserializeDirectByteBufferWithOffset() throws Exception { assertEquals(offset, byteBuffer.position()); assertEquals(bytes.length - offset, byteBuffer.remaining()); - Message to = Message.builder().data(new Entity(byteBuffer)).build(); + Message to = Message.builder().sender(member).data(new Entity(byteBuffer)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -162,7 +165,7 @@ void serializeAndDeserializeDirectByteBufferWithOffsetSlice() throws Exception { assertEquals(0, slice.position()); assertEquals(bytes.length - offset, slice.remaining()); - Message to = Message.builder().data(new Entity(slice)).build(); + Message to = Message.builder().sender(member).data(new Entity(slice)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); @@ -178,7 +181,7 @@ void serializeAndDeserializeDirectByteBufferWithOffsetSlice() throws Exception { void serializeAndDeserializeByteBufferWithoutEntity() throws Exception { byte[] bytes = "hello".getBytes(); - Message to = Message.builder().data(ByteBuffer.wrap(bytes)).build(); + Message to = Message.builder().sender(member).data(ByteBuffer.wrap(bytes)).build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); messageCodec.serialize(to, output); From 22498a78f560de45bc97476d5709481e5e60f411 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Fri, 1 Sep 2023 20:16:01 +0300 Subject: [PATCH 30/34] Enhanced cluster-wrapper --- .../scalecube/cluster/TransportWrapper.java | 55 +++++++++---------- .../cluster/TransportWrapperTest.java | 48 ++++++++-------- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java index c37bc4e7..5e84f3b8 100644 --- a/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java +++ b/cluster/src/main/java/io/scalecube/cluster/TransportWrapper.java @@ -7,13 +7,14 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiFunction; import reactor.core.publisher.Mono; public class TransportWrapper { private final Transport transport; - private final Map addressIndexByMember = new ConcurrentHashMap<>(); + private final Map addressIndexByMember = new ConcurrentHashMap<>(); public TransportWrapper(Transport transport) { this.transport = transport; @@ -27,21 +28,7 @@ public TransportWrapper(Transport transport) { * @return mono result */ public Mono requestResponse(Member member, Message request) { - final List
addresses = member.addresses(); - final AtomicInteger currentIndex = - addressIndexByMember.computeIfAbsent(member, m -> new AtomicInteger()); - return Mono.defer( - () -> { - synchronized (this) { - if (currentIndex.get() == addresses.size()) { - currentIndex.set(0); - } - final Address address = addresses.get(currentIndex.getAndIncrement()); - return transport.requestResponse(address, request); - } - }) - .retry(addresses.size() - 1) - .doOnError(throwable -> addressIndexByMember.remove(member, currentIndex)); + return invokeWithRetry(member, request, transport::requestResponse); } /** @@ -52,20 +39,28 @@ public Mono requestResponse(Member member, Message request) { * @return mono result */ public Mono send(Member member, Message request) { - final List
addresses = member.addresses(); - final AtomicInteger currentIndex = - addressIndexByMember.computeIfAbsent(member, m -> new AtomicInteger()); + return invokeWithRetry(member, request, transport::send); + } + + private Mono invokeWithRetry( + Member member, Message request, BiFunction> function) { return Mono.defer( - () -> { - synchronized (this) { - if (currentIndex.get() == addresses.size()) { - currentIndex.set(0); - } - final Address address = addresses.get(currentIndex.getAndIncrement()); - return transport.send(address, request); - } - }) - .retry(addresses.size() - 1) - .doOnError(throwable -> addressIndexByMember.remove(member, currentIndex)); + () -> { + final List
addresses = member.addresses(); + final Integer index = addressIndexByMember.computeIfAbsent(member, m -> 0); + final AtomicInteger currentIndex = new AtomicInteger(index); + + return Mono.defer( + () -> { + if (currentIndex.get() == addresses.size()) { + currentIndex.set(0); + } + final Address address = addresses.get(currentIndex.get()); + return function.apply(address, request); + }) + .doOnSuccess(s -> addressIndexByMember.put(member, currentIndex.get())) + .doOnError(ex -> currentIndex.incrementAndGet()) + .retry(addresses.size() - 1); + }); } } diff --git a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java index eb0882bc..d3d15ec3 100644 --- a/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/TransportWrapperTest.java @@ -1,5 +1,7 @@ package io.scalecube.cluster; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -11,10 +13,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import java.util.stream.Stream.Builder; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -76,12 +76,12 @@ static void populateBuilder(Builder builder, int size) { } } - private Map addressIndexByMember() + private Map addressIndexByMember() throws NoSuchFieldException, IllegalAccessException { final Field field = TransportWrapper.class.getDeclaredField("addressIndexByMember"); field.setAccessible(true); //noinspection unchecked - return (Map) field.get(transportWrapper); + return (Map) field.get(transportWrapper); } @ParameterizedTest @@ -95,7 +95,7 @@ void requestResponseShouldWorkByRoundRobin(int size, int startIndex, int success } if (startIndex > 0) { - addressIndexByMember().put(member, new AtomicInteger(startIndex)); + addressIndexByMember().put(member, startIndex); } for (int i = 0; i < size; i++) { @@ -109,9 +109,11 @@ void requestResponseShouldWorkByRoundRobin(int size, int startIndex, int success } StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) + .assertNext(message -> assertSame(response, message, "response")) .thenCancel() .verify(); + + assertEquals(successIndex, addressIndexByMember().get(member), "successIndex"); } @Test @@ -124,13 +126,12 @@ void requestResponseShouldWorkThenFail() { .thenReturn(Mono.error(new RuntimeException("Error"))); StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) + .assertNext(message -> assertSame(response, message, "response")) .thenCancel() .verify(); StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + .verifyErrorSatisfies(throwable -> assertEquals("Error", throwable.getMessage())); } @Test @@ -143,11 +144,10 @@ void requestResponseShouldFailThenWork() { .thenReturn(Mono.just(response)); StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + .verifyErrorSatisfies(throwable -> assertEquals("Error", throwable.getMessage())); StepVerifier.create(transportWrapper.requestResponse(member, request)) - .assertNext(message -> Assertions.assertSame(response, message, "response")) + .assertNext(message -> assertSame(response, message, "response")) .thenCancel() .verify(); } @@ -163,7 +163,7 @@ void requestResponseShouldFailByRoundRobin(int size, int startIndex, int ignore) } if (startIndex > 0) { - addressIndexByMember().put(member, new AtomicInteger(startIndex)); + addressIndexByMember().put(member, startIndex); } for (int i = 0; i < size; i++) { @@ -173,8 +173,9 @@ void requestResponseShouldFailByRoundRobin(int size, int startIndex, int ignore) } StepVerifier.create(transportWrapper.requestResponse(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + .verifyErrorSatisfies(throwable -> assertEquals("Error", throwable.getMessage())); + + assertEquals(startIndex, addressIndexByMember().get(member), "startIndex"); } @ParameterizedTest @@ -187,7 +188,7 @@ void sendShouldWorkByRoundRobin(int size, int startIndex, int successIndex) thro } if (startIndex > 0) { - addressIndexByMember().put(member, new AtomicInteger(startIndex)); + addressIndexByMember().put(member, startIndex); } for (int i = 0; i < size; i++) { @@ -201,6 +202,8 @@ void sendShouldWorkByRoundRobin(int size, int startIndex, int successIndex) thro } StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); + + assertEquals(successIndex, addressIndexByMember().get(member), "successIndex"); } @ParameterizedTest @@ -213,7 +216,7 @@ void sendShouldFailByRoundRobin(int size, int startIndex, int ignore) throws Exc } if (startIndex > 0) { - addressIndexByMember().put(member, new AtomicInteger(startIndex)); + addressIndexByMember().put(member, startIndex); } for (int i = 0; i < size; i++) { @@ -222,8 +225,9 @@ void sendShouldFailByRoundRobin(int size, int startIndex, int ignore) throws Exc } StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + .verifyErrorSatisfies(throwable -> assertEquals("Error", throwable.getMessage())); + + assertEquals(startIndex, addressIndexByMember().get(member), "startIndex"); } @Test @@ -237,8 +241,7 @@ void sendShouldWorkThenFail() { StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + .verifyErrorSatisfies(throwable -> assertEquals("Error", throwable.getMessage())); } @Test @@ -251,8 +254,7 @@ void sendShouldFailThenWork() { .thenReturn(Mono.empty()); StepVerifier.create(transportWrapper.send(member, request)) - .verifyErrorSatisfies( - throwable -> Assertions.assertEquals("Error", throwable.getMessage())); + .verifyErrorSatisfies(throwable -> assertEquals("Error", throwable.getMessage())); StepVerifier.create(transportWrapper.send(member, request)).verifyComplete(); } } From 838b36f2972dd1afc48dd7e1402efc54f1f63381 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Sat, 2 Sep 2023 12:38:54 +0300 Subject: [PATCH 31/34] Added `exposeAddress` TransportConfig setting --- .../io/scalecube/cluster/ClusterImpl.java | 10 +++-- .../transport/api/TransportConfig.java | 41 ++++++++++++++----- .../transport/netty/tcp/TcpSender.java | 2 +- .../netty/websocket/WebsocketSender.java | 2 +- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java index 65f1df94..bac1870d 100644 --- a/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/ClusterImpl.java @@ -376,10 +376,10 @@ private Member createLocalMember(Address address) { final int port = address.port(); final List
memberAddresses = new ArrayList<>(); - // First address comes as "fair" listen address - memberAddresses.add(address); + if (config.transportConfig().exposeAddress()) { + memberAddresses.add(address); + } - // Tail goes as externalHosts, if exists final List externalHosts = config.externalHosts(); if (externalHosts != null) { for (String externalHost : externalHosts) { @@ -387,6 +387,10 @@ private Member createLocalMember(Address address) { } } + if (memberAddresses.isEmpty()) { + throw new IllegalArgumentException("Member addresses must not be empty"); + } + final String memberId = config.memberId() != null ? config.memberId() : UUID.randomUUID().toString(); final String memberAlias = config.memberAlias(); diff --git a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportConfig.java b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportConfig.java index 06129f83..847188db 100644 --- a/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportConfig.java +++ b/transport-parent/transport-api/src/main/java/io/scalecube/cluster/transport/api/TransportConfig.java @@ -23,6 +23,7 @@ public final class TransportConfig implements Cloneable { private int maxFrameLength = 2 * 1024 * 1024; // 2 MB private TransportFactory transportFactory; private Function addressMapper = Function.identity(); + private boolean exposeAddress = true; public TransportConfig() {} @@ -73,7 +74,7 @@ public TransportConfig port(int port) { return t; } - public boolean isClientSecured() { + public boolean clientSecured() { return clientSecured; } @@ -137,15 +138,19 @@ public TransportConfig maxFrameLength(int maxFrameLength) { return t; } + public TransportFactory transportFactory() { + return transportFactory; + } + /** - * Setter for {@code addressMapper}. + * Setter for {@code transportFactory}. * - * @param addressMapper address mapper + * @param transportFactory transport factory * @return new {@code TransportConfig} instance */ - public TransportConfig addressMapper(Function addressMapper) { + public TransportConfig transportFactory(TransportFactory transportFactory) { TransportConfig t = clone(); - t.addressMapper = addressMapper; + t.transportFactory = transportFactory; return t; } @@ -153,19 +158,32 @@ public Function addressMapper() { return addressMapper; } - public TransportFactory transportFactory() { - return transportFactory; + /** + * Setter for {@code addressMapper}. + * + * @param addressMapper address mapper + * @return new {@code TransportConfig} instance + */ + public TransportConfig addressMapper(Function addressMapper) { + TransportConfig t = clone(); + t.addressMapper = addressMapper; + return t; + } + + public boolean exposeAddress() { + return exposeAddress; } /** - * Setter for {@code transportFactory}. + * Setter for {@code exposeAddress}. When set to {@code true} - will make transport listening + * address be advertised to cluster. By default set to {@code true}. * - * @param transportFactory transport factory + * @param exposeAddress exposeAddress flag * @return new {@code TransportConfig} instance */ - public TransportConfig transportFactory(TransportFactory transportFactory) { + public TransportConfig exposeAddress(boolean exposeAddress) { TransportConfig t = clone(); - t.transportFactory = transportFactory; + t.exposeAddress = exposeAddress; return t; } @@ -188,6 +206,7 @@ public String toString() { .add("maxFrameLength=" + maxFrameLength) .add("transportFactory=" + transportFactory) .add("addressMapper=" + addressMapper) + .add("exposeAddress=" + exposeAddress) .toString(); } } diff --git a/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/tcp/TcpSender.java b/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/tcp/TcpSender.java index 708d1a95..a9883a49 100644 --- a/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/tcp/TcpSender.java +++ b/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/tcp/TcpSender.java @@ -52,6 +52,6 @@ private TcpClient newTcpClient(SenderContext context, Address address) { (connectionObserver, channel, remoteAddress) -> new TcpChannelInitializer(config.maxFrameLength()) .accept(connectionObserver, channel)); - return config.isClientSecured() ? tcpClient.secure() : tcpClient; + return config.clientSecured() ? tcpClient.secure() : tcpClient; } } diff --git a/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/websocket/WebsocketSender.java b/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/websocket/WebsocketSender.java index 704a7f3d..6a62c1ae 100644 --- a/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/websocket/WebsocketSender.java +++ b/transport-parent/transport-netty/src/main/java/io/scalecube/transport/netty/websocket/WebsocketSender.java @@ -55,7 +55,7 @@ private HttpClient.WebsocketSender newWebsocketSender(SenderContext context, Add .option(ChannelOption.SO_REUSEADDR, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.connectTimeout()); - if (config.isClientSecured()) { + if (config.clientSecured()) { httpClient = httpClient.secure(); } From 77e85529c9a279f8f1deac86fca8213388b07a6d Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Sat, 2 Sep 2023 12:49:46 +0300 Subject: [PATCH 32/34] Added `exposeAddress` TransportConfig setting --- .../cluster/fdetector/FailureDetectorImpl.java | 1 + .../cluster/gossip/GossipProtocolImpl.java | 1 + .../cluster/membership/MembershipProtocolImpl.java | 13 ++++++++++++- .../scalecube/cluster/gossip/GossipDelayTest.java | 2 +- .../cluster/gossip/GossipProtocolTest.java | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java index c6f27405..ed9aa2cf 100644 --- a/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/fdetector/FailureDetectorImpl.java @@ -27,6 +27,7 @@ import reactor.core.publisher.Sinks; import reactor.core.scheduler.Scheduler; +@SuppressWarnings({"FieldCanBeLocal", "unused"}) public final class FailureDetectorImpl implements FailureDetector { private static final Logger LOGGER = LoggerFactory.getLogger(FailureDetector.class); diff --git a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java index d42294da..9c9eacaa 100644 --- a/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/gossip/GossipProtocolImpl.java @@ -30,6 +30,7 @@ import reactor.core.publisher.Sinks; import reactor.core.scheduler.Scheduler; +@SuppressWarnings({"FieldCanBeLocal", "unused"}) public final class GossipProtocolImpl implements GossipProtocol { private static final Logger LOGGER = LoggerFactory.getLogger(GossipProtocol.class); diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index 59e01f1f..ad48b383 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -349,7 +349,7 @@ private void doSync() { Message message = prepareSyncDataMsg(SYNC, null); LOGGER.debug("[{}][doSync] Send Sync to {}", localMember, addresses); - send(transport, addresses, message) + send(transport, addresses, 0, message) .subscribe( null, ex -> @@ -889,4 +889,15 @@ private Mono spreadMembershipGossip(MembershipRecord record) { .then(); }); } + + private static Mono send( + Transport transport, List
addresses, int currentIndex, Message request) { + if (currentIndex >= addresses.size()) { + return Mono.error(new RuntimeException("All addresses have been tried and failed")); + } + + return transport + .send(addresses.get(currentIndex), request) + .onErrorResume(th -> send(transport, addresses, currentIndex + 1, request)); + } } diff --git a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java index 125a8dac..256ff0be 100644 --- a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipDelayTest.java @@ -57,7 +57,7 @@ public void testMessageDelayMoreThanGossipSweepTime() throws InterruptedExceptio gossipProtocol3.listen().subscribe(message -> protocol3GossipCounter.incrementAndGet()); for (int i = 0; i < 3; i++) { - final Member member = gossipProtocol1.getMember(); + final Member member = BaseTest.getField(gossipProtocol1, "localMember"); gossipProtocol1 .spread(Message.builder().sender(member).data("message: " + i).build()) .subscribe(); diff --git a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java index becaead6..35889aaa 100644 --- a/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java +++ b/cluster/src/test/java/io/scalecube/cluster/gossip/GossipProtocolTest.java @@ -148,7 +148,7 @@ void testGossipProtocol(int membersNum, int lossPercent, int meanDelay) throws E // Spread gossip, measure and verify delivery metrics long start = System.currentTimeMillis(); final GossipProtocolImpl gossipProtocol = gossipProtocols.get(0); - final Member member = gossipProtocol.getMember(); + final Member member = BaseTest.getField(gossipProtocol, "localMember"); gossipProtocol.spread(Message.builder().sender(member).data(gossipData).build()).subscribe(); latch.await(2 * gossipTimeout, TimeUnit.MILLISECONDS); // Await for double gossip timeout disseminationTime = System.currentTimeMillis() - start; From ccbe0ed3a031fa98bf17051b7e132d54549673e3 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Sat, 2 Sep 2023 13:00:24 +0300 Subject: [PATCH 33/34] Refactored io.scalecube.cluster.membership.MembershipProtocolImpl.send --- .../membership/MembershipProtocolImpl.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java index ad48b383..e7d8707a 100644 --- a/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java +++ b/cluster/src/main/java/io/scalecube/cluster/membership/MembershipProtocolImpl.java @@ -37,6 +37,7 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -349,7 +350,7 @@ private void doSync() { Message message = prepareSyncDataMsg(SYNC, null); LOGGER.debug("[{}][doSync] Send Sync to {}", localMember, addresses); - send(transport, addresses, 0, message) + send(transport, addresses, message) .subscribe( null, ex -> @@ -890,14 +891,14 @@ private Mono spreadMembershipGossip(MembershipRecord record) { }); } - private static Mono send( - Transport transport, List
addresses, int currentIndex, Message request) { - if (currentIndex >= addresses.size()) { - return Mono.error(new RuntimeException("All addresses have been tried and failed")); - } - - return transport - .send(addresses.get(currentIndex), request) - .onErrorResume(th -> send(transport, addresses, currentIndex + 1, request)); + private static Mono send(Transport transport, List
addresses, Message request) { + final AtomicInteger currentIndex = new AtomicInteger(); + return Mono.defer( + () -> { + final Address address = addresses.get(currentIndex.get()); + return transport.send(address, request); + }) + .doOnError(ex -> currentIndex.incrementAndGet()) + .retry(addresses.size() - 1); } } From 28156bbaef3407be61bd3b5c53f2f7a568752bbd Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Sun, 3 Sep 2023 10:58:28 +0300 Subject: [PATCH 34/34] Minor update --- .../src/main/java/io/scalecube/cluster/ClusterConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java b/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java index 5cbd641c..39feb4b5 100644 --- a/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java +++ b/cluster-api/src/main/java/io/scalecube/cluster/ClusterConfig.java @@ -5,7 +5,6 @@ import io.scalecube.cluster.membership.MembershipConfig; import io.scalecube.cluster.metadata.MetadataCodec; import io.scalecube.cluster.transport.api.TransportConfig; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -170,7 +169,7 @@ public ClusterConfig externalHosts(String... externalHosts) { */ public ClusterConfig externalHosts(List externalHosts) { ClusterConfig c = clone(); - c.externalHosts = new ArrayList<>(externalHosts); + c.externalHosts = externalHosts; return c; }