summaryrefslogtreecommitdiffstats
path: root/vespa-http-client
diff options
context:
space:
mode:
authorHaakon Dybdahl <dybdahl@yahoo-inc.com>2017-03-06 09:22:16 +0100
committerHaakon Dybdahl <dybdahl@yahoo-inc.com>2017-03-06 09:22:16 +0100
commit3fcafa4cc9a2f29c417cfbda9bed0c8390ba2ab7 (patch)
tree8a297abd0b7deb4f52098033535a12f11f290cd8 /vespa-http-client
parent351903b75e5cf8feaa6606c139ee56e1caeccd23 (diff)
Extend APIs with condition-not-met.
Diffstat (limited to 'vespa-http-client')
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java20
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/OperationStatus.java15
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/api/ResultImpl.java8
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/DryRunGatewayConnection.java2
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/EndpointResultQueue.java2
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/communication/IOThread.java2
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/EndPointResultFactory.java7
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/core/operationProcessor/OperationProcessor.java1
-rw-r--r--vespa-http-client/src/test/java/com/yahoo/vespa/http/client/V3HttpAPITest.java27
-rw-r--r--vespa-http-client/src/test/java/com/yahoo/vespa/http/client/handlers/V3MockParsingRequestHandler.java36
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);
}