diff --git a/api/nakadi-event-bus-api.yaml b/api/nakadi-event-bus-api.yaml index 3284d43c4f..59d40fda58 100644 --- a/api/nakadi-event-bus-api.yaml +++ b/api/nakadi-event-bus-api.yaml @@ -1905,7 +1905,7 @@ definitions: format: int64 description: | Number of events between two offsets. Initial offset is exclusive. It's only zero when both provided offsets - are equal. If the `final_cursor` is strictly smaller than the `initial_cursor` it results in client error. + are equal. Cursor: required: diff --git a/src/main/java/org/zalando/nakadi/controller/CursorOperationsController.java b/src/main/java/org/zalando/nakadi/controller/CursorOperationsController.java index 0a30cd5dea..8de88c4d18 100644 --- a/src/main/java/org/zalando/nakadi/controller/CursorOperationsController.java +++ b/src/main/java/org/zalando/nakadi/controller/CursorOperationsController.java @@ -136,13 +136,8 @@ public ResponseEntity invalidCursorOperation(final InvalidCursorOperation e, private String clientErrorMessage(final InvalidCursorOperation.Reason reason) { switch (reason) { - case INVERTED_TIMELINE_ORDER: return "Inverted timelines. Final cursor must correspond to a newer " + - "timeline than initial cursor."; - case TIMELINE_NOT_FOUND: return "Timeline not found. It might happen in case the cursor refers to a " + "timeline that has already expired."; - case INVERTED_OFFSET_ORDER: return "Inverted offsets. Final cursor offsets must be newer than initial " + - "cursor offsets"; case PARTITION_NOT_FOUND: return "Partition not found."; case CURSORS_WITH_DIFFERENT_PARTITION: return "Cursors with different partition. Pairs of cursors should " + "have matching partitions."; diff --git a/src/main/java/org/zalando/nakadi/exceptions/runtime/InvalidCursorOperation.java b/src/main/java/org/zalando/nakadi/exceptions/runtime/InvalidCursorOperation.java index 00bd76ab17..d8c1d90bb3 100644 --- a/src/main/java/org/zalando/nakadi/exceptions/runtime/InvalidCursorOperation.java +++ b/src/main/java/org/zalando/nakadi/exceptions/runtime/InvalidCursorOperation.java @@ -4,9 +4,7 @@ public class InvalidCursorOperation extends MyNakadiRuntimeException1 { private final Reason reason; public enum Reason { - INVERTED_TIMELINE_ORDER, TIMELINE_NOT_FOUND, - INVERTED_OFFSET_ORDER, PARTITION_NOT_FOUND, CURSORS_WITH_DIFFERENT_PARTITION } diff --git a/src/main/java/org/zalando/nakadi/service/CursorOperationsService.java b/src/main/java/org/zalando/nakadi/service/CursorOperationsService.java index 04f6a677a1..6b5981d526 100644 --- a/src/main/java/org/zalando/nakadi/service/CursorOperationsService.java +++ b/src/main/java/org/zalando/nakadi/service/CursorOperationsService.java @@ -24,8 +24,6 @@ import java.util.stream.Collectors; import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.CURSORS_WITH_DIFFERENT_PARTITION; -import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.INVERTED_OFFSET_ORDER; -import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.INVERTED_TIMELINE_ORDER; import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.PARTITION_NOT_FOUND; import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.TIMELINE_NOT_FOUND; @@ -44,7 +42,7 @@ public Long calculateDistance(final NakadiCursor initialCursor, final NakadiCurs if (!initialCursor.getPartition().equals(finalCursor.getPartition())) { throw new InvalidCursorOperation(CURSORS_WITH_DIFFERENT_PARTITION); } else if (initialCursor.getTimeline().getOrder() > finalCursor.getTimeline().getOrder()) { - throw new InvalidCursorOperation(INVERTED_TIMELINE_ORDER); + return - getDistanceDifferentTimelines(finalCursor, initialCursor); } if (initialCursor.getTimeline().getOrder() == finalCursor.getTimeline().getOrder()) { @@ -56,9 +54,7 @@ public Long calculateDistance(final NakadiCursor initialCursor, final NakadiCurs private long getDistanceSameTimeline(final NakadiCursor initialCursor, final NakadiCursor finalCursor) { final long distance = numberOfEventsBeforeCursor(finalCursor) - numberOfEventsBeforeCursor(initialCursor); - if (distance < 0) { - throw new InvalidCursorOperation(INVERTED_OFFSET_ORDER); - } + return distance; } diff --git a/src/main/java/org/zalando/nakadi/service/subscription/SubscriptionService.java b/src/main/java/org/zalando/nakadi/service/subscription/SubscriptionService.java index 9e0b2a265b..dd7ca0ccb9 100644 --- a/src/main/java/org/zalando/nakadi/service/subscription/SubscriptionService.java +++ b/src/main/java/org/zalando/nakadi/service/subscription/SubscriptionService.java @@ -253,7 +253,7 @@ private SubscriptionEventTypeStats loadStats( if (!lastPosition.getEventType().equals(eventType.getName())) { continue; } - Long distance; + final Long distance; if (subscriptionNode.containsPartition(lastPosition.getEventTypePartition())) { final NakadiCursor currentPosition; final SubscriptionCursorWithoutToken offset = @@ -267,12 +267,7 @@ private SubscriptionEventTypeStats loadStats( try { distance = cursorOperationsService.calculateDistance(currentPosition, lastPosition); } catch (final InvalidCursorOperation ex) { - if (ex.getReason() == InvalidCursorOperation.Reason.INVERTED_TIMELINE_ORDER || - ex.getReason() == InvalidCursorOperation.Reason.INVERTED_OFFSET_ORDER) { - distance = 0L; - } else { - throw new InconsistentStateException("Unexpected exception while calculating distance", ex); - } + throw new InconsistentStateException("Unexpected exception while calculating distance", ex); } } else { distance = null; diff --git a/src/test/java/org/zalando/nakadi/service/CursorOperationsServiceTest.java b/src/test/java/org/zalando/nakadi/service/CursorOperationsServiceTest.java index 14d2fb3e8f..a08db92a13 100644 --- a/src/test/java/org/zalando/nakadi/service/CursorOperationsServiceTest.java +++ b/src/test/java/org/zalando/nakadi/service/CursorOperationsServiceTest.java @@ -25,8 +25,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.CURSORS_WITH_DIFFERENT_PARTITION; -import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.INVERTED_OFFSET_ORDER; -import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.INVERTED_TIMELINE_ORDER; import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.PARTITION_NOT_FOUND; import static org.zalando.nakadi.exceptions.runtime.InvalidCursorOperation.Reason.TIMELINE_NOT_FOUND; @@ -46,22 +44,29 @@ public void whenCursorsAreInTheSameTimeline() throws Exception { } @Test - public void whenCursorsOffsetsAreInvertedThenException() throws Exception { + public void whenCursorsOffsetsAreInvertedThenNegativeDistance() throws Exception { final NakadiCursor initialCursor = new NakadiCursor(timeline, "0", "0000000000000002"); final NakadiCursor finalCursor = new NakadiCursor(timeline, "0", "0000000000000001"); - expectException(initialCursor, finalCursor, INVERTED_OFFSET_ORDER); + final Long distance = service.calculateDistance(initialCursor, finalCursor); + + assertThat(distance, equalTo(-1L)); } @Test - public void whenCursorTimelinesAreInvertedThenException() throws Exception { - final Timeline initialTimeline = mockTimeline(1); - final Timeline finalTimeline = mockTimeline(0); + public void whenCursorTimelinesAreInvertedThenNegativeDistance() throws Exception { + final Timeline initialTimeline = mockTimeline(2, 7); + final Timeline intermediaryTimeline = mockTimeline(1, 9L); + final Timeline finalTimeline = mockTimeline(0, 5); final NakadiCursor initialCursor = new NakadiCursor(initialTimeline, "0", "0000000000000001"); - final NakadiCursor finalCursor = new NakadiCursor(finalTimeline, "0", "0000000000000002"); + final NakadiCursor finalCursor = new NakadiCursor(finalTimeline, "0", "0000000000000003"); + + mockActiveTimelines(initialTimeline, intermediaryTimeline, finalTimeline); + + final Long distance = service.calculateDistance(initialCursor, finalCursor); - expectException(initialCursor, finalCursor, INVERTED_TIMELINE_ORDER); + assertThat(distance, equalTo(-14L)); } @Test