From 383bc46daf03e74a011cf897a770b70f0ead3996 Mon Sep 17 00:00:00 2001 From: Elvis Souza Date: Mon, 21 Oct 2024 12:29:49 -0300 Subject: [PATCH] Solver Remote Refactoring (#589) * refactoring solver remote class * fixing compiling errors * fixing compiling errors * reverting * fixing compiling errors * fixing compiling errors * release notes * [Gradle Release Plugin] - new version commit: '3.30.3-snapshot'. * refactoring --- RELEASE-NOTES.md | 3 + gradle.properties | 2 +- .../circuitbreaker/CircuitCheckException.java | 4 + .../dnsproxyserver/solver/SolverRemote.java | 93 +--------- .../dnsproxyserver/solver/remote/Request.java | 4 + .../application/CircuitBreakerService.java | 12 +- .../application/RemoteResultSupplier.java | 63 +++++++ .../remote/application/ResultSupplier.java | 10 ++ .../failsafe/CircuitBreakerFactory.java | 23 ++- .../CircuitBreakerPingCheckerService.java | 4 +- .../application/mapper/ResultMapper.java | 87 ++++++++++ .../CircuitBreakerExecutorService.java | 33 ---- .../solver/remote/mapper/ResolverMapper.java | 7 +- src/main/java/com/mageddo/net/IpAddr.java | 2 + .../solver/SolverRemoteTest.java | 161 ------------------ .../application/RemoteResultSupplierTest.java | 68 ++++++++ .../failsafe/CircuitBreakerFactoryTest.java | 12 +- .../application/mapper/ResultMapperTest.java | 88 ++++++++++ .../templates/InetSocketAddressTemplates.java | 9 + .../testing/templates/MessageTemplates.java | 7 + .../solver/remote/RequestTemplates.java | 4 +- 21 files changed, 387 insertions(+), 309 deletions(-) create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplier.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/ResultSupplier.java create mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapper.java delete mode 100644 src/main/java/com/mageddo/dnsproxyserver/solver/remote/circuitbreaker/application/CircuitBreakerExecutorService.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplierTest.java create mode 100644 src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapperTest.java diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 012d1831a..aabcbef8e 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,6 @@ +## 3.30.3 +* Solver Remote Refactoring. #589 + ## 3.30.2 * Detect which condition didn't match and log the cause when configuring DPS as default DNS #580 * Fixed backend develop docker-compose.yml diff --git a/gradle.properties b/gradle.properties index ae5e6db10..d9c33ccac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=3.30.2-snapshot +version=3.30.3-snapshot diff --git a/src/main/java/com/mageddo/commons/circuitbreaker/CircuitCheckException.java b/src/main/java/com/mageddo/commons/circuitbreaker/CircuitCheckException.java index 33ba6839b..b0c3e92cb 100644 --- a/src/main/java/com/mageddo/commons/circuitbreaker/CircuitCheckException.java +++ b/src/main/java/com/mageddo/commons/circuitbreaker/CircuitCheckException.java @@ -8,4 +8,8 @@ public CircuitCheckException(String message) { public CircuitCheckException(Throwable e) { super(e.getMessage(), e); } + + public CircuitCheckException(String message, Throwable e) { + super(message, e); + } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/SolverRemote.java b/src/main/java/com/mageddo/dnsproxyserver/solver/SolverRemote.java index f7aa84310..5a9c0f388 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/SolverRemote.java +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/SolverRemote.java @@ -1,42 +1,31 @@ package com.mageddo.dnsproxyserver.solver; -import com.mageddo.commons.circuitbreaker.CircuitCheckException; -import com.mageddo.dns.utils.Messages; import com.mageddo.dnsproxyserver.solver.remote.Request; import com.mageddo.dnsproxyserver.solver.remote.Result; import com.mageddo.dnsproxyserver.solver.remote.application.CircuitBreakerService; +import com.mageddo.dnsproxyserver.solver.remote.application.RemoteResultSupplier; import com.mageddo.dnsproxyserver.solver.remote.application.ResolverStatsFactory; +import com.mageddo.dnsproxyserver.solver.remote.application.ResultSupplier; import com.mageddo.net.NetExecutorWatchdog; import com.mageddo.utils.Executors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.time.StopWatch; -import org.xbill.DNS.Flags; import org.xbill.DNS.Message; import javax.inject.Inject; import javax.inject.Singleton; -import java.io.IOException; import java.util.List; import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Supplier; import java.util.stream.Stream; -import static com.mageddo.dns.utils.Messages.simplePrint; - @Slf4j @Singleton @RequiredArgsConstructor(onConstructor = @__({@Inject})) public class SolverRemote implements Solver, AutoCloseable { - static final String QUERY_TIMED_OUT_MSG = "Query timed out"; - public static final int PING_TIMEOUT_IN_MS = 1_500; - private final CircuitBreakerService circuitBreakerService; private final ResolverStatsFactory resolverStatsFactory; private final NetExecutorWatchdog netWatchdog = new NetExecutorWatchdog(); @@ -93,83 +82,11 @@ Request buildRequest(Message query, int resolverIndex, StopWatch stopWatch, Reso Result safeQueryResult(Request req) { req.splitStopWatch(); - return this.queryUsingCircuitBreaker(req, () -> this.queryResult(req)); - } - - Result queryUsingCircuitBreaker(Request req, Supplier sup) { - return this.circuitBreakerService.safeHandle(req.getResolverAddress(), sup); - } - - Result queryResult(Request req) { - final var resFuture = this.sendQueryAsyncToResolver(req); - if (this.isPingWhileGettingQueryResponseActive()) { - this.pingWhileGettingQueryResponse(req, resFuture); - } - return this.transformToResult(resFuture, req); - } - - CompletableFuture sendQueryAsyncToResolver(Request req) { - return req.sendQueryAsyncToResolver(this.executor); + return this.queryUsingCircuitBreaker(new RemoteResultSupplier(req, this.executor, this.netWatchdog)); } - void pingWhileGettingQueryResponse(Request req, CompletableFuture resFuture) { - this.netWatchdog.watch(req.getResolverAddr(), resFuture, PING_TIMEOUT_IN_MS); - } - - boolean isPingWhileGettingQueryResponseActive() { - return Boolean.getBoolean("mg.solverRemote.pingWhileGettingQueryResponse"); - } - - Result transformToResult(CompletableFuture resFuture, Request request) { - final var res = this.findFutureRes(resFuture, request); - if (res == null) { - return Result.empty(); - } - - if (Messages.isSuccess(res)) { - log.trace( - "status=found, i={}, time={}, req={}, res={}, server={}", - request.getResolverIndex(), request.getTime(), simplePrint(request.getQuery()), - simplePrint(res), request.getResolverAddress() - ); - return Result.fromSuccessResponse(Response.success(res)); - } else { - log.trace( - "status=notFound, i={}, time={}, req={}, res={}, server={}", - request.getResolverIndex(), request.getTime(), simplePrint(request.getQuery()), - simplePrint(res), request.getResolverAddress() - ); - return Result.fromErrorMessage(res); - } - } - - Message findFutureRes(CompletableFuture resFuture, Request request) { - try { - return Messages.setFlag(resFuture.get(), Flags.RA); - } catch (InterruptedException | ExecutionException e) { - this.checkCircuitError(e, request); - return null; - } - } - - void checkCircuitError(Exception e, Request request) { - if (e.getCause() instanceof IOException) { - final var time = request.getElapsedTimeInMs(); - if (e.getMessage().contains(QUERY_TIMED_OUT_MSG)) { - log.info( - "status=timedOut, i={}, time={}, req={}, msg={} class={}", - request.getResolverIndex(), time, simplePrint(request.getQuery()), e.getMessage(), ClassUtils.getSimpleName(e) - ); - throw new CircuitCheckException(e); - } - log.warn( - "status=failed, i={}, time={}, req={}, server={}, errClass={}, msg={}", - request.getResolverIndex(), time, simplePrint(request.getQuery()), request.getResolverAddress(), - ClassUtils.getSimpleName(e), e.getMessage(), e - ); - } else { - throw new RuntimeException(e.getMessage(), e); - } + Result queryUsingCircuitBreaker(ResultSupplier sup) { + return this.circuitBreakerService.safeHandle(sup); } @Override diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/Request.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/Request.java index c3dd52ce6..d84393bf0 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/Request.java +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/Request.java @@ -1,11 +1,13 @@ package com.mageddo.dnsproxyserver.solver.remote; +import com.mageddo.dns.utils.Messages; import com.mageddo.dnsproxyserver.solver.Resolver; import com.mageddo.net.IpAddr; import com.mageddo.net.IpAddrs; import lombok.Builder; import lombok.NonNull; import lombok.Value; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.StopWatch; import org.xbill.DNS.Message; @@ -13,6 +15,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +@Slf4j @Value @Builder public class Request { @@ -42,6 +45,7 @@ public void splitStopWatch() { } public CompletableFuture sendQueryAsyncToResolver(Executor executor) { + log.trace("status=querying, server={}, req={}", this.resolver.getAddress(), Messages.simplePrint(this.query)); return this.resolver.sendAsync(this.query, executor).toCompletableFuture(); } diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/CircuitBreakerService.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/CircuitBreakerService.java index 375c2d42d..7c9f84a3c 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/CircuitBreakerService.java +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/CircuitBreakerService.java @@ -24,19 +24,19 @@ public class CircuitBreakerService { private String status; - public Result safeHandle(InetSocketAddress resolverAddress, Supplier sup) { + public Result safeHandle(ResultSupplier sup) { try { - return this.handle(resolverAddress, sup); + return this.handle(sup); } catch (CircuitCheckException | CircuitIsOpenException e) { final var clazz = ClassUtils.getSimpleName(e); - log.debug("status=circuitEvent, server={}, type={}", resolverAddress, clazz); - this.status = String.format("%s for %s", clazz, resolverAddress); + log.debug("status=circuitEvent, server={}, type={}", sup.getRemoteAddress(), clazz); + this.status = String.format("%s for %s", clazz, sup.getRemoteAddress()); return Result.empty(); } } - private Result handle(InetSocketAddress resolverAddress, Supplier sup) { - return this.circuitBreakerFactory.check(resolverAddress, sup); + private Result handle(ResultSupplier sup) { + return this.circuitBreakerFactory.check(sup); } public String getStatus() { diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplier.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplier.java new file mode 100644 index 000000000..48d48da6a --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplier.java @@ -0,0 +1,63 @@ +package com.mageddo.dnsproxyserver.solver.remote.application; + +import com.mageddo.dnsproxyserver.solver.remote.Request; +import com.mageddo.dnsproxyserver.solver.remote.Result; +import com.mageddo.dnsproxyserver.solver.remote.application.mapper.ResultMapper; +import com.mageddo.net.IpAddr; +import com.mageddo.net.NetExecutorWatchdog; +import lombok.extern.slf4j.Slf4j; +import org.xbill.DNS.Message; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +@Slf4j +public class RemoteResultSupplier implements ResultSupplier { + + public static final int PING_TIMEOUT_IN_MS = 1_500; + + private final Request req; + private final Executor executor; + private final NetExecutorWatchdog netWatchdog; + + public RemoteResultSupplier(Request req, Executor executor, NetExecutorWatchdog netWatchdog) { + this.req = req; + this.executor = executor; + this.netWatchdog = netWatchdog; + } + + @Override + public Result get() { + return this.queryResult(this.req); + } + + Result queryResult(Request req) { + final var resFuture = this.sendQueryAsyncToResolver(req); + if (this.isPingWhileGettingQueryResponseActive()) { + this.pingWhileGettingQueryResponse(req, resFuture); + } + return ResultMapper.from(resFuture, req); + } + + CompletableFuture sendQueryAsyncToResolver(Request req) { + return req.sendQueryAsyncToResolver(this.executor); + } + + boolean isPingWhileGettingQueryResponseActive() { + return Boolean.getBoolean("mg.solverRemote.pingWhileGettingQueryResponse"); + } + + void pingWhileGettingQueryResponse(Request req, CompletableFuture resFuture) { + this.netWatchdog.watch(req.getResolverAddr(), resFuture, PING_TIMEOUT_IN_MS); + } + + @Override + public String toString() { + return String.format("server=%s", this.req.getResolverAddr()); + } + + @Override + public IpAddr getRemoteAddress() { + return this.req.getResolverAddr(); + } +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/ResultSupplier.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/ResultSupplier.java new file mode 100644 index 000000000..5b069b92c --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/ResultSupplier.java @@ -0,0 +1,10 @@ +package com.mageddo.dnsproxyserver.solver.remote.application; + +import com.mageddo.dnsproxyserver.solver.remote.Result; +import com.mageddo.net.IpAddr; + +import java.util.function.Supplier; + +public interface ResultSupplier extends Supplier { + IpAddr getRemoteAddress(); +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactory.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactory.java index 9805d8792..2ceadb596 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactory.java +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactory.java @@ -7,9 +7,12 @@ import com.mageddo.dnsproxyserver.solver.remote.CircuitStatus; import com.mageddo.dnsproxyserver.solver.remote.Result; import com.mageddo.dnsproxyserver.solver.remote.application.FailsafeCircuitBreakerFactory; +import com.mageddo.dnsproxyserver.solver.remote.application.ResultSupplier; import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegate; import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegateNonResilient; import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegateStaticThresholdFailsafe; +import com.mageddo.dnsproxyserver.solver.remote.mapper.ResolverMapper; +import com.mageddo.net.IpAddr; import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -21,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; @Slf4j @Singleton @@ -34,17 +36,20 @@ public class CircuitBreakerFactory { private final FailsafeCircuitBreakerFactory failsafeCircuitBreakerFactory; private final com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.canaryratethreshold.CircuitBreakerFactory canaryThresholdFactory; - public Result check(InetSocketAddress remoteAddress, Supplier sup) { - final var circuitBreaker = this.findCircuitBreaker(remoteAddress); + public Result check(ResultSupplier sup) { + final var circuitBreaker = this.findCircuitBreaker(sup.getRemoteAddress()); return circuitBreaker.execute(sup); } - public CircuitBreakerDelegate findCircuitBreaker(InetSocketAddress address) { - final var strategy = this.findCircuitBreakerHotLoad(address); - return this.circuitBreakerMap.computeIfAbsent(address, addr -> strategy); + public CircuitBreakerDelegate findCircuitBreaker(IpAddr serverAddress) { + final var strategy = this.findCircuitBreakerHotLoad(serverAddress); + return this.circuitBreakerMap.computeIfAbsent( + ResolverMapper.toInetSocketAddress(serverAddress), + addr -> strategy + ); } - CircuitBreakerDelegate findCircuitBreakerHotLoad(InetSocketAddress address) { + CircuitBreakerDelegate findCircuitBreakerHotLoad(IpAddr address) { final var config = this.findCircuitBreakerConfig(); return switch (config.name()) { case STATIC_THRESHOLD -> this.buildStaticThresholdFailSafeCircuitBreaker(address, config); @@ -54,10 +59,10 @@ CircuitBreakerDelegate findCircuitBreakerHotLoad(InetSocketAddress address) { } private CircuitBreakerDelegateStaticThresholdFailsafe buildStaticThresholdFailSafeCircuitBreaker( - InetSocketAddress address, CircuitBreakerStrategyConfig config + IpAddr address, CircuitBreakerStrategyConfig config ) { return new CircuitBreakerDelegateStaticThresholdFailsafe(this.failsafeCircuitBreakerFactory.build( - address, + ResolverMapper.toInetSocketAddress(address), (StaticThresholdCircuitBreakerStrategyConfig) config )); } diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerPingCheckerService.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerPingCheckerService.java index b2c2ec9ab..9bf3b1ada 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerPingCheckerService.java +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerPingCheckerService.java @@ -1,7 +1,7 @@ package com.mageddo.dnsproxyserver.solver.remote.application.failsafe; import com.mageddo.commons.circuitbreaker.CircuitCheckException; -import com.mageddo.dnsproxyserver.solver.SolverRemote; +import com.mageddo.dnsproxyserver.solver.remote.application.RemoteResultSupplier; import com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application.CircuitBreakerDelegate; import com.mageddo.net.Networks; import dev.failsafe.CircuitBreakerOpenException; @@ -46,6 +46,6 @@ void check(InetSocketAddress server, CircuitBreakerDelegate circuitBreaker) { * @see https://github.com/mageddo/dns-proxy-server/issues/526#issuecomment-2261421618 */ boolean ping(InetSocketAddress server) { - return Networks.ping(server, SolverRemote.PING_TIMEOUT_IN_MS); + return Networks.ping(server, RemoteResultSupplier.PING_TIMEOUT_IN_MS); } } diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapper.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapper.java new file mode 100644 index 000000000..f71d23da2 --- /dev/null +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapper.java @@ -0,0 +1,87 @@ +package com.mageddo.dnsproxyserver.solver.remote.application.mapper; + +import com.mageddo.commons.circuitbreaker.CircuitCheckException; +import com.mageddo.dns.utils.Messages; +import com.mageddo.dnsproxyserver.solver.Response; +import com.mageddo.dnsproxyserver.solver.remote.Request; +import com.mageddo.dnsproxyserver.solver.remote.Result; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ClassUtils; +import org.xbill.DNS.Flags; +import org.xbill.DNS.Message; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static com.mageddo.dns.utils.Messages.simplePrint; + +@Slf4j +public class ResultMapper { + + static final String QUERY_TIMED_OUT_MSG = "Query timed out"; + + public static Result from(CompletableFuture resFuture, Request request){ + return transformToResult(resFuture, request); + } + + private static Result transformToResult(CompletableFuture resFuture, Request request) { + final var res = findFutureRes(resFuture, request); + if (res == null) { + return Result.empty(); + } + + if (Messages.isSuccess(res)) { + log.trace( + "status=found, i={}, time={}, req={}, res={}, server={}", + request.getResolverIndex(), request.getTime(), simplePrint(request.getQuery()), + simplePrint(res), request.getResolverAddress() + ); + return Result.fromSuccessResponse(Response.success(res)); + } else { + log.trace( + "status=notFound, i={}, time={}, req={}, res={}, server={}", + request.getResolverIndex(), request.getTime(), simplePrint(request.getQuery()), + simplePrint(res), request.getResolverAddress() + ); + return Result.fromErrorMessage(res); + } + } + + private static Message findFutureRes(CompletableFuture resFuture, Request request) { + try { + return Messages.setFlag(resFuture.get(), Flags.RA); + } catch (InterruptedException | ExecutionException e) { + checkCircuitError(e, request); + return null; + } + } + + private static void checkCircuitError(Exception e, Request request) { + if (e.getCause() instanceof IOException) { + final var time = request.getElapsedTimeInMs(); + if (e.getMessage().contains(QUERY_TIMED_OUT_MSG)) { + log.info( + "status=timedOut, i={}, time={}, req={}, msg={} class={}", + request.getResolverIndex(), time, simplePrint(request.getQuery()), e.getMessage(), ClassUtils.getSimpleName(e) + ); + throw new CircuitCheckException(buildErrorMsg(e, request), e); + } + log.warn( + "status=failed, i={}, time={}, req={}, server={}, errClass={}, msg={}", + request.getResolverIndex(), time, simplePrint(request.getQuery()), request.getResolverAddress(), + ClassUtils.getSimpleName(e), e.getMessage(), e + ); + } else { + throw new RuntimeException(buildErrorMsg(e, request), e); + } + } + + private static String buildErrorMsg(Exception e, Request request) { + return String.format( + "( req=%s, server=%s, msg=%s )", + simplePrint(request.getQuery()), request.getResolverAddress(), e.getMessage() + ); + } + +} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/circuitbreaker/application/CircuitBreakerExecutorService.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/circuitbreaker/application/CircuitBreakerExecutorService.java deleted file mode 100644 index 1324b0c4d..000000000 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/circuitbreaker/application/CircuitBreakerExecutorService.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mageddo.dnsproxyserver.solver.remote.circuitbreaker.application; - -import com.mageddo.dnsproxyserver.solver.remote.CircuitStatus; -import com.mageddo.dnsproxyserver.solver.remote.Result; -import com.mageddo.dnsproxyserver.solver.remote.application.failsafe.CircuitBreakerFactory; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.net.InetSocketAddress; -import java.util.function.Supplier; - -@Slf4j -@Singleton -@RequiredArgsConstructor(onConstructor = @__({@Inject})) -public class CircuitBreakerExecutorService { - - private final CircuitBreakerFactory factory; - - Result safeExecute(final InetSocketAddress resolverAddress, Supplier sup) { - final var circuitBreaker = this.findCircuitBreaker(resolverAddress); - return circuitBreaker.execute(sup); - } - - CircuitStatus findCircuitStatusFor(InetSocketAddress resolverAddress) { - return this.findCircuitBreaker(resolverAddress).findStatus(); - } - - private CircuitBreakerDelegate findCircuitBreaker(InetSocketAddress resolverAddress) { - return this.factory.findCircuitBreaker(resolverAddress); - } -} diff --git a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/mapper/ResolverMapper.java b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/mapper/ResolverMapper.java index f7e608aa2..b1962e006 100644 --- a/src/main/java/com/mageddo/dnsproxyserver/solver/remote/mapper/ResolverMapper.java +++ b/src/main/java/com/mageddo/dnsproxyserver/solver/remote/mapper/ResolverMapper.java @@ -6,7 +6,6 @@ import com.mageddo.dnsproxyserver.solver.remote.ResolverStats; import com.mageddo.dnsproxyserver.utils.InetAddresses; import com.mageddo.net.IpAddr; -import org.apache.commons.compress.harmony.unpack200.bytecode.BCIRenumberedAttribute; import java.net.InetSocketAddress; import java.time.Duration; @@ -16,7 +15,7 @@ public class ResolverMapper { private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(10); public static Resolver from(IpAddr addr) { - return from(InetAddresses.toSocketAddress(addr.getRawIP(), addr.getPortOrDef(53))); + return from(toInetSocketAddress(addr)); } public static Resolver from(InetSocketAddress addr) { @@ -33,4 +32,8 @@ public static ResolverStats toResolverStats(Resolver resolver, CircuitStatus sta .build() ; } + + public static InetSocketAddress toInetSocketAddress(IpAddr addr) { + return InetAddresses.toSocketAddress(addr.getRawIP(), addr.getPortOrDef(53)); + } } diff --git a/src/main/java/com/mageddo/net/IpAddr.java b/src/main/java/com/mageddo/net/IpAddr.java index 45ee6d4f8..049820bd8 100644 --- a/src/main/java/com/mageddo/net/IpAddr.java +++ b/src/main/java/com/mageddo/net/IpAddr.java @@ -5,11 +5,13 @@ import com.mageddo.dnsproxyserver.json.converter.IPConverter; import com.mageddo.utils.Bytes; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.NonNull; import lombok.Value; @Value @Builder +@EqualsAndHashCode public class IpAddr { @NonNull diff --git a/src/test/java/com/mageddo/dnsproxyserver/solver/SolverRemoteTest.java b/src/test/java/com/mageddo/dnsproxyserver/solver/SolverRemoteTest.java index bbaa85170..55d607358 100644 --- a/src/test/java/com/mageddo/dnsproxyserver/solver/SolverRemoteTest.java +++ b/src/test/java/com/mageddo/dnsproxyserver/solver/SolverRemoteTest.java @@ -1,27 +1,9 @@ package com.mageddo.dnsproxyserver.solver; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import org.xbill.DNS.Flags; -import testing.templates.MessageTemplates; -import testing.templates.solver.remote.ResolverTemplates; - -import java.net.SocketTimeoutException; -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -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.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class SolverRemoteTest { @@ -30,148 +12,5 @@ class SolverRemoteTest { @InjectMocks SolverRemote solverRemote; - @Test - void mustCacheSolvedQueryFor5Minutes() { - // arrange - final var query = MessageTemplates.acmeAQuery(); - final var answer = MessageTemplates.buildAAnswer(query); - - doReturn(ResolverTemplates.googleDnsAsList()) - .when(this.solverRemote) - .findResolversToUse() - ; - - doReturn(CompletableFuture.completedFuture(answer)) - .when(this.solverRemote) - .sendQueryAsyncToResolver(any()); - - this.excludeCircuitBreakerStrategyAndCallQueryMethodDirectly(); - - // act - final var res = this.solverRemote.handle(query); - - // assert - assertEquals(Response.DEFAULT_SUCCESS_TTL, res.getDpsTtl()); - } - - @Test - void mustCacheNxDomainQueryFor1Hour() { - // arrange - final var query = MessageTemplates.acmeAQuery(); - final var answer = MessageTemplates.buildNXAnswer(query); - - doReturn(ResolverTemplates.googleDnsAsList()) - .when(this.solverRemote) - .findResolversToUse() - ; - - doReturn(CompletableFuture.completedFuture(answer)) - .when(this.solverRemote) - .sendQueryAsyncToResolver(any()); - - this.excludeCircuitBreakerStrategyAndCallQueryMethodDirectly(); - - // act - final var res = this.solverRemote.handle(query); - - // assert - assertEquals(Response.DEFAULT_NXDOMAIN_TTL, res.getDpsTtl()); - } - - @Test - void mustReturnNullWhenGetTimeout() { - - // arrange - doReturn(ResolverTemplates.googleDnsAsList()) - .when(this.solverRemote) - .findResolversToUse() - ; - - doReturn(CompletableFuture.failedFuture(new SocketTimeoutException("Deu ruim"))) - .when(this.solverRemote) - .sendQueryAsyncToResolver(any()); - - this.excludeCircuitBreakerStrategyAndCallQueryMethodDirectly(); - - final var query = MessageTemplates.acmeAQuery(); - - // act - final var res = this.solverRemote.handle(query); - - // assert - assertNull(res); - } - - @Test - void mustReturnRaEvenWhenRemoteServerDoesntReturnsRA() { - // arrange - final var query = MessageTemplates.acmeAQuery(); - final var res = MessageTemplates.buildAAnswer(query); - res.getHeader().unsetFlag(Flags.RA); - - doReturn(ResolverTemplates.googleDnsAsList()) - .when(this.solverRemote) - .findResolversToUse() - ; - - doReturn(CompletableFuture.completedFuture(res)) - .when(this.solverRemote) - .sendQueryAsyncToResolver(any()); - - this.excludeCircuitBreakerStrategyAndCallQueryMethodDirectly(); - - // act - final var result = this.solverRemote.handle(query); - - // assert - assertTrue(Responses.hasFlag(result, Flags.RA)); - assertEquals(Response.DEFAULT_SUCCESS_TTL, result.getDpsTtl()); - } - - @Test - void mustPingRemoteServerWhileQueryingWhenFeatureIsActive(){ - - // arrange - final var query = MessageTemplates.acmeAQuery(); - final var answer = MessageTemplates.buildAAnswer(query); - - doReturn(true).when(this.solverRemote).isPingWhileGettingQueryResponseActive(); - - doReturn(ResolverTemplates.googleDnsAsList()) - .when(this.solverRemote) - .findResolversToUse() - ; - - doReturn(CompletableFuture.completedFuture(answer)) - .when(this.solverRemote) - .sendQueryAsyncToResolver(any()); - - this.excludeCircuitBreakerStrategyAndCallQueryMethodDirectly(); - - // act - final var res = this.solverRemote.handle(query); - - // assert - assertNotNull(res); - verify(this.solverRemote).pingWhileGettingQueryResponse(any(), any()); - - } - - @Test - void pingRemoteServerWhileQueryingDisabledByDefault(){ - - // act - final var active = this.solverRemote.isPingWhileGettingQueryResponseActive(); - - // assert - assertFalse(active); - - } - void excludeCircuitBreakerStrategyAndCallQueryMethodDirectly() { - doAnswer(iom -> Supplier.class.cast(iom.getArgument(1)).get()) - .when(this.solverRemote) - .queryUsingCircuitBreaker(any(), any()) - ; - } } diff --git a/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplierTest.java b/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplierTest.java new file mode 100644 index 000000000..9145fd952 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/RemoteResultSupplierTest.java @@ -0,0 +1,68 @@ +package com.mageddo.dnsproxyserver.solver.remote.application; + +import com.mageddo.net.NetExecutorWatchdog; +import com.mageddo.utils.Executors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import testing.templates.MessageTemplates; +import testing.templates.solver.remote.RequestTemplates; + +import java.util.concurrent.CompletableFuture; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class RemoteResultSupplierTest { + + RemoteResultSupplier supplier; + + @BeforeEach + void beforeEach() { + final var req = RequestTemplates.buildDefault(); + final var netWatchdog = new NetExecutorWatchdog(); + final var executor = Executors.newThreadExecutor(); + this.supplier = Mockito.spy(new RemoteResultSupplier(req, executor, netWatchdog)); + } + + @Test + void mustPingRemoteServerWhileQueryingWhenFeatureIsActive() { + + // arrange + final var query = MessageTemplates.acmeAQuery(); + final var answer = MessageTemplates.buildAAnswer(query); + + doReturn(true) + .when(this.supplier) + .isPingWhileGettingQueryResponseActive(); + + doReturn(CompletableFuture.completedFuture(answer)) + .when(this.supplier) + .sendQueryAsyncToResolver(any()); + + // act + final var res = this.supplier.get(); + + // assert + assertNotNull(res); + verify(this.supplier).pingWhileGettingQueryResponse(any(), any()); + + } + + @Test + void pingRemoteServerWhileQueryingDisabledByDefault(){ + + // act + final var active = this.supplier.isPingWhileGettingQueryResponseActive(); + + // assert + assertFalse(active); + + } +} diff --git a/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactoryTest.java b/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactoryTest.java index 37bb2d5a0..504c4000d 100644 --- a/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactoryTest.java +++ b/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/failsafe/CircuitBreakerFactoryTest.java @@ -42,8 +42,8 @@ void mustCreateANewCircuitBreakerInstanceWhenDifferentKeyIsUsed(){ .build(any(), any()); // act - final var a = this.factory.findCircuitBreaker(InetSocketAddressTemplates._8_8_8_8()); - final var b = this.factory.findCircuitBreaker(InetSocketAddressTemplates._1_1_1_1()); + final var a = this.factory.findCircuitBreaker(InetSocketAddressTemplates._8_8_8_8_addr()); + final var b = this.factory.findCircuitBreaker(InetSocketAddressTemplates._1_1_1_1_addr()); // assert assertNotEquals(a, b); @@ -53,7 +53,7 @@ void mustCreateANewCircuitBreakerInstanceWhenDifferentKeyIsUsed(){ @Test void mustReuseCircuitBreakerInstanceWhenSameKeyIsUsed(){ // arrange - final var addr = InetSocketAddressTemplates._8_8_8_8(); + final var addr = InetSocketAddressTemplates._8_8_8_8_addr(); doReturn(CircuitBreakerConfigTemplates.buildDefault()) .when(this.factory) @@ -82,7 +82,7 @@ void mustCheckAllExistentCircuitsAndCountSuccessWhenSafeCheckReturnsTrue() { ; doReturn(true).when(this.factory).circuitBreakerSafeCheck(any()); - final var addr = InetSocketAddressTemplates._8_8_8_8(); + final var addr = InetSocketAddressTemplates._8_8_8_8_addr(); this.factory.findCircuitBreaker(addr); // act @@ -103,7 +103,7 @@ void mustCheckAndCountErrorWhenSafeCheckReturnsFalse() { ; doReturn(false).when(this.factory).circuitBreakerSafeCheck(any()); - final var addr = InetSocketAddressTemplates._8_8_8_8(); + final var addr = InetSocketAddressTemplates._8_8_8_8_addr(); this.factory.findCircuitBreaker(addr); // act @@ -118,7 +118,7 @@ void mustCheckAndCountErrorWhenSafeCheckReturnsFalse() { void mustBuildNonResilientCircuitBreaker(){ // arrange - final var addr = InetSocketAddressTemplates._8_8_8_8(); + final var addr = InetSocketAddressTemplates._8_8_8_8_addr(); doReturn(CircuitBreakerConfigTemplates.buildNonResilientConfig()) .when(this.factory) .findCircuitBreakerConfig(); diff --git a/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapperTest.java b/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapperTest.java new file mode 100644 index 000000000..579f254c8 --- /dev/null +++ b/src/test/java/com/mageddo/dnsproxyserver/solver/remote/application/mapper/ResultMapperTest.java @@ -0,0 +1,88 @@ +package com.mageddo.dnsproxyserver.solver.remote.application.mapper; + +import com.mageddo.dnsproxyserver.solver.Response; +import com.mageddo.dnsproxyserver.solver.Responses; +import org.junit.jupiter.api.Test; +import org.xbill.DNS.Flags; +import org.xbill.DNS.Message; +import testing.templates.MessageTemplates; +import testing.templates.solver.remote.RequestTemplates; + +import java.net.SocketTimeoutException; +import java.util.concurrent.CompletableFuture; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ResultMapperTest { + + @Test + void mustCacheSolvedQueryFor5Minutes() { + // arrange + final var query = MessageTemplates.acmeAQuery(); + final var answer = MessageTemplates.buildAAnswer(query); + + final var resFuture = CompletableFuture.completedFuture(answer); + final var randomReq = RequestTemplates.buildDefault(); + + // act + final var result = ResultMapper.from(resFuture, randomReq); + + // assert + final var successResponse = result.getSuccessResponse(); + assertNotNull(successResponse); + assertEquals(Response.DEFAULT_SUCCESS_TTL, successResponse.getDpsTtl()); + } + + @Test + void mustCacheNxDomainQueryFor1Hour() { + // arrange + final var query = MessageTemplates.acmeAQuery(); + final var answer = MessageTemplates.buildNXAnswer(query); + + final var resFuture = CompletableFuture.completedFuture(answer); + final var randomReq = RequestTemplates.buildDefault(); + + // act + final var result = ResultMapper.from(resFuture, randomReq); + + // assert + final var errorResponse = result.getErrorResponse(); + assertNotNull(errorResponse); + assertEquals(Response.DEFAULT_NXDOMAIN_TTL, errorResponse.getDpsTtl()); + + } + + @Test + void mustReturnNullWhenGetTimeout() { + + // arrange + final CompletableFuture failedFuture = CompletableFuture.failedFuture(new SocketTimeoutException("Deu ruim")); + final var randomReq = RequestTemplates.buildDefault(); + + // act + final var res = ResultMapper.from(failedFuture, randomReq); + + // assert + assertNotNull(res); + assertTrue(res.isEmpty()); + } + + @Test + void mustReturnRaEvenWhenRemoteServerDoesntReturnsRA() { + // arrange + final var query = MessageTemplates.acmeAQuery(); + final var res = MessageTemplates.buildAAnswerWithoutRA(query); + final var future = CompletableFuture.completedFuture(res); + + // act + final var result = ResultMapper.from(future, RequestTemplates.buildDefault()) + .getSuccessResponse(); + + // assert + assertTrue(Responses.hasFlag(result, Flags.RA)); + assertEquals(Response.DEFAULT_SUCCESS_TTL, result.getDpsTtl()); + } + +} diff --git a/src/test/java/testing/templates/InetSocketAddressTemplates.java b/src/test/java/testing/templates/InetSocketAddressTemplates.java index 0e4834f7f..60306865b 100644 --- a/src/test/java/testing/templates/InetSocketAddressTemplates.java +++ b/src/test/java/testing/templates/InetSocketAddressTemplates.java @@ -1,6 +1,8 @@ package testing.templates; import com.mageddo.dnsproxyserver.utils.Ips; +import com.mageddo.net.IpAddr; +import com.mageddo.net.IpAddrs; import java.net.InetSocketAddress; @@ -9,10 +11,17 @@ public static InetSocketAddress _8_8_8_8(){ return new InetSocketAddress(Ips.toAddress("8.8.8.8"), 53); } + public static IpAddr _8_8_8_8_addr(){ + return IpAddrs.from(_8_8_8_8()); + } + public static InetSocketAddress _8_8_4_4() { return new InetSocketAddress(Ips.toAddress("8.8.4.4"), 53); } + public static IpAddr _1_1_1_1_addr() { + return IpAddrs.from(_1_1_1_1()); + } public static InetSocketAddress _1_1_1_1() { return new InetSocketAddress(Ips.toAddress("1.1.1.1"), 53); } diff --git a/src/test/java/testing/templates/MessageTemplates.java b/src/test/java/testing/templates/MessageTemplates.java index ec4ff3d39..f44344882 100644 --- a/src/test/java/testing/templates/MessageTemplates.java +++ b/src/test/java/testing/templates/MessageTemplates.java @@ -1,6 +1,7 @@ package testing.templates; import com.mageddo.dns.utils.Messages; +import org.xbill.DNS.Flags; import org.xbill.DNS.Message; public class MessageTemplates { @@ -36,4 +37,10 @@ public static Message acmeSoaQuery() { public static Message randomHostnameAQuery() { return Messages.aQuestion(System.nanoTime() + ".com"); } + + public static Message buildAAnswerWithoutRA(Message query) { + final var answer = buildAAnswer(query); + answer.getHeader().unsetFlag(Flags.RA); + return answer; + } } diff --git a/src/test/java/testing/templates/solver/remote/RequestTemplates.java b/src/test/java/testing/templates/solver/remote/RequestTemplates.java index 14b8be6a3..68ad1831a 100644 --- a/src/test/java/testing/templates/solver/remote/RequestTemplates.java +++ b/src/test/java/testing/templates/solver/remote/RequestTemplates.java @@ -8,12 +8,14 @@ public class RequestTemplates { public static Request buildDefault() { + final var stopWatch = StopWatch.createStarted(); + stopWatch.split(); return Request .builder() .query(MessageTemplates.acmeAQuery()) .resolver(ResolverMapper.from(InetSocketAddressTemplates._8_8_8_8())) .resolverIndex(0) - .stopWatch(StopWatch.createStarted()) + .stopWatch(stopWatch) .build(); } }