diff options
author | Haakon Dybdahl <dybdahl@yahoo-inc.com> | 2017-03-06 09:22:16 +0100 |
---|---|---|
committer | Haakon Dybdahl <dybdahl@yahoo-inc.com> | 2017-03-06 09:22:16 +0100 |
commit | 3fcafa4cc9a2f29c417cfbda9bed0c8390ba2ab7 (patch) | |
tree | 8a297abd0b7deb4f52098033535a12f11f290cd8 /vespa-http-client/src | |
parent | 351903b75e5cf8feaa6606c139ee56e1caeccd23 (diff) |
Extend APIs with condition-not-met.
Diffstat (limited to 'vespa-http-client/src')
10 files changed, 95 insertions, 25 deletions
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java index 3e809cfe0ad..4488f8603c2 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java @@ -52,6 +52,12 @@ public abstract class Result { */ abstract public boolean isTransient(); + /** + * Returns true if a error was caused by condition-not-met in the document operation. + * @return true if condition is not met. + */ + abstract public boolean isConditionNotMet(); + abstract public List<Detail> getDetails(); /** @@ -71,14 +77,16 @@ public abstract class Result { private final Endpoint endpoint; private final boolean success; private final boolean _transient; + private final boolean isConditionNotMet; private final Exception exception; private final String traceMessage; private final long timeStampMillis = System.currentTimeMillis(); - public Detail(Endpoint endpoint, boolean success, boolean _transient, String traceMessage, Exception e) { + public Detail(Endpoint endpoint, boolean success, boolean _transient, boolean isConditionNotMet, String traceMessage, Exception e) { this.endpoint = endpoint; this.success = success; this._transient = _transient; + this.isConditionNotMet = isConditionNotMet; this.exception = e; this.traceMessage = traceMessage; } @@ -87,6 +95,7 @@ public abstract class Result { this.endpoint = endpoint; this.success = true; this._transient = true; + this.isConditionNotMet = false; this.exception = null; this.traceMessage = null; } @@ -120,6 +129,14 @@ public abstract class Result { } /** + * Returns true if a condition in the document operation was not met. + * @return if condition not met in operation. + */ + public boolean isConditionNotMet() { + return isConditionNotMet; + } + + /** * Returns any exception related to this Detail, if unsuccessful. Might be null. * * @return any exception related to this Detail, if unsuccessful. Might be null. @@ -143,6 +160,7 @@ public abstract class Result { b.append("success=").append(success).append(" "); if (!success) { b.append("transient=").append(_transient).append(" "); + b.append("conditionNotMet=").append(isConditionNotMet).append(" "); } if (exception != null) { b.append("exception='").append(Exceptions.toMessageString(exception)).append("' "); diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/OperationStatus.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/OperationStatus.java index 16315a1ca95..d0355b0e269 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/OperationStatus.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/OperationStatus.java @@ -15,16 +15,19 @@ import java.util.Iterator; */ @Beta public final class OperationStatus { + public static final String IS_CONDITION_NOT_MET = "IS-CONDITION-NOT-MET"; public final String message; public final String operationId; public final ErrorCode errorCode; public final String traceMessage; + public final boolean isConditionNotMet; private static final char EOL = '\n'; private static final char SEPARATOR = ' '; private static final Splitter spaceSep = Splitter.on(SEPARATOR); - public OperationStatus(String message, String operationId, ErrorCode errorCode, String traceMessage) { + public OperationStatus(String message, String operationId, ErrorCode errorCode, boolean isConditionNotMet, String traceMessage) { + this.isConditionNotMet = isConditionNotMet; this.message = message; this.operationId = operationId; this.errorCode = errorCode; @@ -56,19 +59,25 @@ public final class OperationStatus { .toString(); errorCode = ErrorCode.valueOf(Encoder.decode(input.next(), new StringBuilder()).toString()); + message = Encoder.decode(input.next(), new StringBuilder()).toString(); // We are backwards compatible, meaning it is ok not to supply the last argument. + boolean isConditionNotMet = false; + if (message.startsWith(IS_CONDITION_NOT_MET)) { + message = message.replaceFirst(IS_CONDITION_NOT_MET, ""); + isConditionNotMet = true; + } if (input.hasNext()) { traceMessage = Encoder.decode(input.next(), new StringBuilder()).toString(); } - return new OperationStatus(message, operationId, errorCode, traceMessage); + return new OperationStatus(message, operationId, errorCode, isConditionNotMet, traceMessage); } public String render() { StringBuilder s = new StringBuilder(); Encoder.encode(operationId, s).append(SEPARATOR); Encoder.encode(errorCode.toString(), s).append(SEPARATOR); - Encoder.encode(message, s).append(SEPARATOR); + Encoder.encode(isConditionNotMet ? IS_CONDITION_NOT_MET + message : message, s).append(SEPARATOR); Encoder.encode(traceMessage, s).append(EOL); return s.toString(); } diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/api/ResultImpl.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/api/ResultImpl.java index 00196f793c5..3e3f6897ba8 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/api/ResultImpl.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/api/ResultImpl.java @@ -24,6 +24,7 @@ final public class ResultImpl extends Result { private final Document document; private final boolean success; private final boolean _transient; + private final boolean isConditionNotMet; private final List<Detail> details; private final String localTrace; @@ -32,12 +33,15 @@ final public class ResultImpl extends Result { this.details = Collections.unmodifiableList(new ArrayList<>(values)); boolean totalSuccess = true; boolean totalTransient = true; + boolean isConditionNotMet = true; for (Detail d : details) { if (!d.isSuccess()) {totalSuccess = false; } if (!d.isTransient()) {totalTransient = false; } + if (!d.isConditionNotMet()) { isConditionNotMet = false; } } this.success = totalSuccess; this._transient = totalTransient; + this.isConditionNotMet = isConditionNotMet; this.localTrace = localTrace == null ? null : localTrace.toString(); } @@ -67,6 +71,10 @@ final public class ResultImpl extends Result { } @Override + public boolean isConditionNotMet() { return isConditionNotMet; } + + + @Override public List<Detail> getDetails() { return details; } @Override diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/DryRunGatewayConnection.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/DryRunGatewayConnection.java index f634a2e22f7..53336b6899c 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/DryRunGatewayConnection.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/DryRunGatewayConnection.java @@ -31,7 +31,7 @@ public class DryRunGatewayConnection implements GatewayConnection { public InputStream writeOperations(List<Document> docs) throws ServerResponseException, IOException { StringBuilder result = new StringBuilder(); for (Document doc : docs) { - OperationStatus operationStatus = new OperationStatus("ok", doc.getOperationId(), ErrorCode.OK, ""); + OperationStatus operationStatus = new OperationStatus("ok", doc.getOperationId(), ErrorCode.OK, false, ""); result.append(operationStatus.render()); } return new ByteArrayInputStream(result.toString().getBytes(StandardCharsets.UTF_8)); diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/EndpointResultQueue.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/EndpointResultQueue.java index 37395f87fd8..948d2e7f865 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/EndpointResultQueue.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/EndpointResultQueue.java @@ -104,7 +104,7 @@ class EndpointResultQueue { } private synchronized void failedOperationId(String operationId, Exception exception) { - EndpointResult endpointResult = EndPointResultFactory.createError(endpoint, operationId, exception); + EndpointResult endpointResult = EndPointResultFactory.createError(endpoint, operationId, false, exception); operationProcessor.resultReceived(endpointResult, clusterId); } diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/IOThread.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/IOThread.java index d769c5bdf0c..9697256fbde 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/IOThread.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/IOThread.java @@ -404,7 +404,7 @@ class IOThread implements Runnable, AutoCloseable { for (Document document : documentQueue.removeAllDocuments()) { EndpointResult endpointResult= - EndPointResultFactory.createError(endpoint, document.getOperationId(), exception); + EndPointResultFactory.createError(endpoint, document.getOperationId(), false, exception); resultQueue.failOperation(endpointResult, clusterId); } } diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/EndPointResultFactory.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/EndPointResultFactory.java index a91e170fbd5..b357efc82d4 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/EndPointResultFactory.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/EndPointResultFactory.java @@ -41,13 +41,13 @@ public final class EndPointResultFactory { } public static EndpointResult createError( - Endpoint endpoint, String operationId, Exception exception) { - return new EndpointResult(operationId, new Result.Detail(endpoint, false, false, null, exception)); + Endpoint endpoint, String operationId, boolean isConditionNotMetError, Exception exception) { + return new EndpointResult(operationId, new Result.Detail(endpoint, false, false, isConditionNotMetError, null, exception)); } public static EndpointResult createTransientError( Endpoint endpoint, String operationId, Exception exception) { - return new EndpointResult(operationId, new Result.Detail(endpoint, false, true, null, exception)); + return new EndpointResult(operationId, new Result.Detail(endpoint, false, true, false, null, exception)); } private static EndpointResult parseResult(String line, Endpoint endpoint) { @@ -71,6 +71,7 @@ public final class EndPointResultFactory { new Result.Detail(endpoint, reply.errorCode.isSuccess(), reply.errorCode.isTransient(), + reply.isConditionNotMet, reply.traceMessage, exception)); } catch (Throwable t) { diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/OperationProcessor.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/OperationProcessor.java index b736a727c8f..199c5992e8b 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/OperationProcessor.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/OperationProcessor.java @@ -247,6 +247,7 @@ public class OperationProcessor { EndPointResultFactory.createError( eio.getEndpoint(), document.getOperationId(), + false, eio), clusterConnection.getClusterId()); } diff --git a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/V3HttpAPITest.java b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/V3HttpAPITest.java index a095cb184a0..68e86c86929 100644 --- a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/V3HttpAPITest.java +++ b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/V3HttpAPITest.java @@ -57,7 +57,7 @@ public class V3HttpAPITest extends TestOnCiBuildingSystemOnly { TestUtils.writeDocuments(session, Collections.<TestDocument>singletonList(documents.get(0))); } - private void testServerWithMock(V3MockParsingRequestHandler serverMock, boolean failFast) throws Exception { + private void testServerWithMock(V3MockParsingRequestHandler serverMock, boolean failFast, boolean conditionNotMet) throws Exception { try (Server server = new Server(serverMock, 0); Session session = SessionFactory.create( new SessionParams.Builder() @@ -84,7 +84,9 @@ public class V3HttpAPITest extends TestOnCiBuildingSystemOnly { TestDocument document = documents.get(0); Result r = results.remove(document.getDocumentId()); assertThat(r, not(nullValue())); + assertThat(r.isConditionNotMet(), is(conditionNotMet)); assertThat(r.getDetails().toString(), r.isSuccess(), is(false)); + assertThat(r.getDetails().toString(), r.isConditionNotMet(), is(conditionNotMet)); assertThat(results.isEmpty(), is(true)); } } @@ -109,33 +111,33 @@ public class V3HttpAPITest extends TestOnCiBuildingSystemOnly { @Test public void requireThatBadResponseCodeFails() throws Exception { - testServerWithMock(new V3MockParsingRequestHandler(401/*Unauthorized*/), true); - testServerWithMock(new V3MockParsingRequestHandler(403/*Forbidden*/), true); - testServerWithMock(new V3MockParsingRequestHandler(407/*Proxy Authentication Required*/), true); + testServerWithMock(new V3MockParsingRequestHandler(401/*Unauthorized*/), true, false); + testServerWithMock(new V3MockParsingRequestHandler(403/*Forbidden*/), true, false); + testServerWithMock(new V3MockParsingRequestHandler(407/*Proxy Authentication Required*/), true, false); } @Test public void requireThatUnexpectedVersionIsHandledProperly() throws Exception { testServerWithMock(new V3MockParsingRequestHandler( - 200, V3MockParsingRequestHandler.Scenario.RETURN_UNEXPECTED_VERSION), true); + 200, V3MockParsingRequestHandler.Scenario.RETURN_UNEXPECTED_VERSION), true, false); } @Test public void requireThatNonAcceptedVersionIsHandledProperly() throws Exception { testServerWithMock(new V3MockParsingRequestHandler( - 200, V3MockParsingRequestHandler.Scenario.DONT_ACCEPT_VERSION), true); + 200, V3MockParsingRequestHandler.Scenario.DONT_ACCEPT_VERSION), true, false); } @Test public void requireThatNon200OkIsHandledProperly() throws Exception { testServerWithMock(new V3MockParsingRequestHandler( - 200, V3MockParsingRequestHandler.Scenario.INTERNAL_SERVER_ERROR), true); + 200, V3MockParsingRequestHandler.Scenario.INTERNAL_SERVER_ERROR), true, false); } @Test public void requireThatMbusErrorIsHandledProperly() throws Exception { testServerWithMock(new V3MockParsingRequestHandler( - 200, V3MockParsingRequestHandler.Scenario.MBUS_RETURNED_ERROR), false); + 200, V3MockParsingRequestHandler.Scenario.MBUS_RETURNED_ERROR), false, false); } @Test @@ -181,12 +183,17 @@ public class V3HttpAPITest extends TestOnCiBuildingSystemOnly { @Test public void requireThatCouldNotFeedErrorIsHandledProperly() throws Exception { testServerWithMock(new V3MockParsingRequestHandler( - 200, V3MockParsingRequestHandler.Scenario.COULD_NOT_FEED), false); + 200, V3MockParsingRequestHandler.Scenario.COULD_NOT_FEED), false, false); } @Test public void requireThatImmediateDisconnectIsHandledProperly() throws Exception { testServerWithMock(new V3MockParsingRequestHandler( - 200, V3MockParsingRequestHandler.Scenario.DISCONNECT_IMMEDIATELY), true); + 200, V3MockParsingRequestHandler.Scenario.DISCONNECT_IMMEDIATELY), true, false); + } + @Test + public void testConditionNotMet() throws Exception { + testServerWithMock(new V3MockParsingRequestHandler( + 200, V3MockParsingRequestHandler.Scenario.CONDITON_NOT_MET), false, true); } } diff --git a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/handlers/V3MockParsingRequestHandler.java b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/handlers/V3MockParsingRequestHandler.java index c43feef088b..2b5b5ff10e9 100644 --- a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/handlers/V3MockParsingRequestHandler.java +++ b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/handlers/V3MockParsingRequestHandler.java @@ -42,7 +42,7 @@ public class V3MockParsingRequestHandler extends AbstractHandler { DISCONNECT_IMMEDIATELY, DONT_ACCEPT_VERSION, RETURN_UNEXPECTED_VERSION, INTERNAL_SERVER_ERROR, COULD_NOT_FEED, MBUS_RETURNED_ERROR, NEVER_RETURN_ANY_RESULTS, DELAYED_RESPONSE, BAD_REQUEST, SERVER_ERROR_TWICE_THEN_OK, - EXPECT_HIGHEST_PRIORITY_AND_TRACELEVEL_123 + EXPECT_HIGHEST_PRIORITY_AND_TRACELEVEL_123, CONDITON_NOT_MET } public V3MockParsingRequestHandler() { @@ -120,6 +120,9 @@ public class V3MockParsingRequestHandler extends AbstractHandler { checkIfSessionThenHighPriorityAndTraceLevel123(request); allOk(baseRequest, request, response); break; + case CONDITON_NOT_MET: + conditionNotMetRequest(baseRequest, request, response); + break; default: throw new IllegalArgumentException("Test scenario " + scenario + " not supported."); } @@ -132,6 +135,24 @@ public class V3MockParsingRequestHandler extends AbstractHandler { } } + private void conditionNotMetRequest(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { + String sessionId = getSessionId(request); + setHeaders(response, sessionId); + response.setStatus(responseCode); + baseRequest.setHandled(true); + PrintWriter responseWriter = response.getWriter(); + String operationId; + while ((operationId = readOperationId(request.getInputStream())) != null) { + long lengthToSkip = readByteLength(request.getInputStream()); + while (lengthToSkip > 0) { + long skipped = request.getInputStream().skip(lengthToSkip); + lengthToSkip -= skipped; + } + respondConditionNotMet(responseWriter, operationId); + } + closeChannel(responseWriter); + + } private void badRequest(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { if (badRequestScenarioShouldReturnBadRequest.get()) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); @@ -353,24 +374,29 @@ public class V3MockParsingRequestHandler extends AbstractHandler { private void respondFailed(PrintWriter responseWriter, String docId) { final OperationStatus operationStatus = - new OperationStatus("mbus returned boom", docId, ErrorCode.ERROR, "trace"); + new OperationStatus("mbus returned boom", docId, ErrorCode.ERROR, false, "trace"); writeResponse(responseWriter, operationStatus); } private void respondTransientFailed(PrintWriter responseWriter, String docId) { final OperationStatus operationStatus = new OperationStatus( - "Could not put", docId, ErrorCode.TRANSIENT_ERROR, ""); + "Could not put", docId, ErrorCode.TRANSIENT_ERROR, false, ""); writeResponse(responseWriter, operationStatus); } private void respondFailedWithTransitiveErrorSeenFromClient(PrintWriter responseWriter, String docId) { final OperationStatus operationStatus = - new OperationStatus("NETWORK_ERROR", docId, ErrorCode.ERROR, "trace"); + new OperationStatus("NETWORK_ERROR", docId, ErrorCode.ERROR, false, "trace"); writeResponse(responseWriter, operationStatus); } + private void respondConditionNotMet(PrintWriter responseWriter, String docId) { + final OperationStatus operationStatus = + new OperationStatus("this is a test", docId, ErrorCode.ERROR, true, "trace"); + writeResponse(responseWriter, operationStatus); + } private void respondOK(PrintWriter responseWriter, String docId) { - final OperationStatus operationStatus = new OperationStatus("Doc fed", docId, ErrorCode.OK, "Trace message"); + final OperationStatus operationStatus = new OperationStatus("Doc fed", docId, ErrorCode.OK, false, "Trace message"); writeResponse(responseWriter, operationStatus); } |