@Override public String toString() { return String.format("Segment[%d/%s]", getSegmentId(), getMask()); }
/** * Checks whether the given {@code sagaId} matches with the given {@code segment}. * <p> * For any complete set of segments, exactly one segment matches with any value. * <p> * * @param segment The segment to validate the identifier for * @param sagaId The identifier to test * @return {@code true} if the identifier matches the segment, otherwise {@code false} * * @implSpec This implementation uses the {@link Segment#matches(Object)} to match against the Saga identifier */ protected boolean matchesSegment(Segment segment, String sagaId) { return segment.matches(sagaId); }
/** * Calculates the Segment that represents the merger of this segment with the given {@code other} segment. * * @param other the segment to merge this one with * @return The Segment representing the merged segments */ public Segment mergedWith(Segment other) { Assert.isTrue(this.isMergeableWith(other), () -> "Given Segment cannot be merged with this segment."); return new Segment(Math.min(this.segmentId, other.segmentId), this.mask >>> 1); }
/** * Indicates whether this segment can be merged with the given {@code other} segment. * <p> * Two segments can be merged when their mask is identical, and the only difference in SegmentID is in the first * 1-bit of their mask. * * @param other the Segment to verify mergeability for * @return {@code true} if the segments can be merged, otherwise {@code false} */ public boolean isMergeableWith(Segment other) { return this.mask == other.mask && mergeableSegmentId() == other.getSegmentId(); }
private static boolean computeSegments(Segment segment, List<Integer> segments, Set<Segment> applicableSegments) { final Segment[] splitSegment = segment.split(); // As the first segmentId mask, keeps the original segmentId, we only check the 2nd segmentId mask being a know. if (segments.contains(splitSegment[1].getSegmentId())) { for (Segment segmentSplit : splitSegment) { if (!computeSegments(segmentSplit, segments, applicableSegments)) { applicableSegments.add(segmentSplit); } } } else { applicableSegments.add(segment); } return true; }
private void checkSegmentCaughtUp(Segment segment, BlockingStream<TrackedEventMessage<?>> eventStream) { if (!eventStream.hasNextAvailable()) { activeSegments.computeIfPresent(segment.getSegmentId(), (k, v) -> v.caughtUp()); } }
Segment[] segments = Segment.computeSegments(tokenStoreCurrentSegments); Segment segment = segments[i]; if (!activeSegments.containsKey(segment.getSegmentId()) && canClaimSegment(segment.getSegmentId())) { try { transactionManager.executeInTransaction(() -> { TrackingToken token = tokenStore.fetchToken(processorName, segment.getSegmentId()); activeSegments.putIfAbsent(segment.getSegmentId(), new TrackerStatus(segment, token)); }); } catch (UnableToClaimTokenException ucte) { segment.getSegmentId()); activeSegments.remove(segment.getSegmentId()); continue; } catch (Exception e) { activeSegments.remove(segment.getSegmentId()); if (AxonNonTransientException.isCauseOf(e)) { logger.error( "An unrecoverable error has occurred wile attempting to claim a token " + "for segment: {}. Shutting down processor [{}].", segment.getSegmentId(), getName(), "An error occurred while attempting to claim a token for segment: {}. " + "Will retry later...", segment.getSegmentId(),
.setCaughtUp(e.getValue().isCaughtUp()) .setReplaying(e.getValue().isReplaying()) .setOnePartOf(e.getValue().getSegment().getMask()+1)
/** * Compute the {@link Segment}'s from a given list of segmentId's. * * @param segments The segment id's for which to compute Segments. * @return an array of computed {@link Segment} */ public static Segment[] computeSegments(int... segments) { if (segments == null || segments.length == 0) { return EMPTY_SEGMENTS; } final Set<Segment> resolvedSegments = new HashSet<>(); computeSegments(ROOT_SEGMENT, stream(segments).boxed().collect(toList()), resolvedSegments); // As we split and compute segment masks branching by first entry, the resolved segment mask is not guaranteed // to be added to the collection in natural order. return resolvedSegments.stream().sorted().collect(toList()).toArray(new Segment[resolvedSegments.size()]); }
/** * Split a given {@link Segment} n-times in round robin fashion. * <br/> * * @param segment The {@link Segment} to split. * @param numberOfTimes The number of times to split it. * @return a collection of {@link Segment}'s. */ public static List<Segment> splitBalanced(Segment segment, int numberOfTimes) { final SortedSet<Segment> toBeSplit = new TreeSet<>(Comparator.comparing(Segment::getMask) .thenComparing(Segment::getSegmentId)); toBeSplit.add(segment); for (int i = 0; i < numberOfTimes; i++) { final Segment workingSegment = toBeSplit.first(); toBeSplit.remove(workingSegment); toBeSplit.addAll(Arrays.asList(workingSegment.split())); } ArrayList<Segment> result = new ArrayList<>(toBeSplit); result.sort(Comparator.comparing(Segment::getSegmentId)); return result; }
/** * Returns an array with two {@link Segment segments with a corresponding mask}.<br/><br/> * The first entry contains the original {@code segmentId}, with the newly calculated mask. (Simple left shift, adding a '1' as LSB). * The 2nd entry is a new {@code segmentId} with the same derived mask. * <p> * Callers must ensure that either the two returned Segments are used, or the instance from which they are derived, * but not both. * * @return an array of two {@link Segment}'s. */ public Segment[] split() { if ((mask << 1) < 0) { throw new IllegalArgumentException("Unable to split the given segmentId, as the mask exceeds the max mask size."); } Segment[] segments = new Segment[2]; int newMask = ((mask << 1) + 1); final int newSegment = segmentId + (mask == 0 ? 1 : newMask ^ mask); segments[0] = new Segment(segmentId, newMask); segments[1] = new Segment(newSegment, newMask); return segments; }
private static boolean computeSegments(Segment segment, List<Integer> segments, Set<Segment> applicableSegments) { final Segment[] splitSegment = segment.split(); // As the first segmentId mask, keeps the original segmentId, we only check the 2nd segmentId mask being a know. if (segments.contains(splitSegment[1].getSegmentId())) { for (Segment segmentSplit : splitSegment) { if (!computeSegments(segmentSplit, segments, applicableSegments)) { applicableSegments.add(segmentSplit); } } } else { applicableSegments.add(segment); } return true; }
@Override public void run() { try { processingLoop(segment); } catch (Throwable e) { logger.error("Processing loop ended due to uncaught exception. Pausing processor in Error State.", e); state.set(State.PAUSED_ERROR); } finally { activeSegments.remove(segment.getSegmentId()); if (availableThreads.getAndIncrement() == 0 && getState().isRunning()) { logger.info("No Worker Launcher active. Using current thread to assign segments."); new WorkerLauncher().run(); } } }
/** * Indicates whether this segment can be merged with the given {@code other} segment. * <p> * Two segments can be merged when their mask is identical, and the only difference in SegmentID is in the first * 1-bit of their mask. * * @param other the Segment to verify mergeability for * @return {@code true} if the segments can be merged, otherwise {@code false} */ public boolean isMergeableWith(Segment other) { return this.mask == other.mask && mergeableSegmentId() == other.getSegmentId(); }
Segment[] segments = Segment.computeSegments(tokenStoreCurrentSegments); Segment segment = segments[i]; if (!activeSegments.containsKey(segment.getSegmentId()) && canClaimSegment(segment.getSegmentId())) { try { transactionManager.executeInTransaction(() -> { TrackingToken token = tokenStore.fetchToken(processorName, segment.getSegmentId()); activeSegments.putIfAbsent(segment.getSegmentId(), new TrackerStatus(segment, token)); }); } catch (UnableToClaimTokenException ucte) { logger.debug("Unable to claim the token for segment: {}. It is owned by another process", segment.getSegmentId()); activeSegments.remove(segment.getSegmentId()); continue; } catch (Exception e) { activeSegments.remove(segment.getSegmentId()); if (AxonNonTransientException.isCauseOf(e)) { logger.error("An unrecoverable error has occurred wile attempting to claim a token for segment: {}. Shutting down processor [{}].", segment.getSegmentId(), getName(), e); state.set(State.PAUSED_ERROR); break; logger.info("An error occurred while attempting to claim a token for segment: {}. Will retry later...", segment.getSegmentId(), e); continue;
.setCaughtUp(e.getValue().isCaughtUp()) .setReplaying(e.getValue().isReplaying()) .setOnePartOf(e.getValue().getSegment().getMask()+1)
/** * Compute the {@link Segment}'s from a given list of segmentId's. * * @param segments The segment id's for which to compute Segments. * @return an array of computed {@link Segment} */ public static Segment[] computeSegments(int... segments) { if (segments == null || segments.length == 0) { return EMPTY_SEGMENTS; } final Set<Segment> resolvedSegments = new HashSet<>(); computeSegments(ROOT_SEGMENT, stream(segments).boxed().collect(toList()), resolvedSegments); // As we split and compute segment masks branching by first entry, the resolved segment mask is not guaranteed // to be added to the collection in natural order. return resolvedSegments.stream().sorted().collect(toList()).toArray(new Segment[resolvedSegments.size()]); }
/** * Split a given {@link Segment} n-times in round robin fashion. * <br/> * * @param segment The {@link Segment} to split. * @param numberOfTimes The number of times to split it. * @return a collection of {@link Segment}'s. */ public static List<Segment> splitBalanced(Segment segment, int numberOfTimes) { final SortedSet<Segment> toBeSplit = new TreeSet<>(Comparator.comparing(Segment::getMask) .thenComparing(Segment::getSegmentId)); toBeSplit.add(segment); for (int i = 0; i < numberOfTimes; i++) { final Segment workingSegment = toBeSplit.first(); toBeSplit.remove(workingSegment); toBeSplit.addAll(Arrays.asList(workingSegment.split())); } ArrayList<Segment> result = new ArrayList<>(toBeSplit); result.sort(Comparator.comparing(Segment::getSegmentId)); return result; }
/** * Returns an array with two {@link Segment segments with a corresponding mask}.<br/><br/> * The first entry contains the original {@code segmentId}, with the newly calculated mask. (Simple left shift, adding a '1' as LSB). * The 2nd entry is a new {@code segmentId} with the same derived mask. * <p> * Callers must ensure that either the two returned Segments are used, or the instance from which they are derived, * but not both. * * @return an array of two {@link Segment}'s. */ public Segment[] split() { if ((mask << 1) < 0) { throw new IllegalArgumentException("Unable to split the given segmentId, as the mask exceeds the max mask size."); } Segment[] segments = new Segment[2]; int newMask = ((mask << 1) + 1); final int newSegment = segmentId + (mask == 0 ? 1 : newMask ^ mask); segments[0] = new Segment(segmentId, newMask); segments[1] = new Segment(newSegment, newMask); return segments; }
/** * Indicates whether the given {@code value} matches this segment. A value matches when the hashCode of a value, * after applying this segments mask, equals to this segment ID. * * @param value The value to verify against. * @return {@code true} if the given value matches this segment, otherwise {@code false} */ public boolean matches(Object value) { return mask == 0 || matches(Objects.hashCode(value)); }