package org.rakam.kume.service.ringmap; import org.rakam.kume.Member; import org.rakam.kume.transport.OperationContext; import org.rakam.kume.transport.Request; import org.rakam.kume.transport.serialization.Serializer; import org.rakam.kume.util.ConsistentHashRing; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import static java.util.Map.Entry; class ChangeRingRequest<K, V> implements Request<RingMap, Map<K, V>> { private final long queryStartToken; private final long queryEndToken; private ConsistentHashRing oldRing; public ChangeRingRequest(long queryStartToken, long queryEndToken, ConsistentHashRing oldRing) { this.queryStartToken = queryStartToken; this.queryEndToken = queryEndToken; this.oldRing = oldRing; } public ChangeRingRequest(long queryStartToken, long queryEndToken) { this.queryStartToken = queryStartToken; this.queryEndToken = queryEndToken; } @Override public void run(RingMap service, OperationContext ctx) { // synchronized (service) { Map<K, V> moveEntries = new HashMap<>(); ConsistentHashRing serviceRing = service.getRing(); int startBucket = serviceRing.findBucketIdFromToken(queryStartToken); int endBucket = serviceRing.findBucketIdFromToken(queryEndToken); int loopEnd = endBucket - startBucket < 0 ? endBucket + serviceRing.getBucketCount() : endBucket; for (int bckIdz = startBucket; bckIdz <= loopEnd; bckIdz++) { int bckId = bckIdz % serviceRing.getBucketCount(); Map<K, V> partition = service.getBucket(bckId); if (partition != null) { long endToken = serviceRing.getBucket(bckId + 1).token-1; long startToken = serviceRing.getBucket(bckId).token; boolean partitionBetweenToken = ConsistentHashRing.isTokenBetween(startToken, queryStartToken, queryStartToken); partitionBetweenToken &= ConsistentHashRing.isTokenBetween(endToken, queryStartToken, queryStartToken); if (partitionBetweenToken) { moveEntries.putAll(partition); } else { partition.forEach((key, value) -> { if (ConsistentHashRing.isTokenBetween(ConsistentHashRing.hash(key), queryStartToken, queryEndToken)) { moveEntries.put(key, value); } }); } } else { // it seems partition table is changed (most probably updated before this request is processed) // so the old items must be in dataWaitingForMigration. Iterator<Entry<ConsistentHashRing.TokenRange, Map>> it = service.dataWaitingForMigration.entrySet().iterator(); while (it.hasNext()) { Entry<ConsistentHashRing.TokenRange, Map> next = it.next(); ConsistentHashRing.TokenRange token = next.getKey(); Map map = next.getValue(); boolean startTokenIn = ConsistentHashRing.isTokenBetween(token.start, queryStartToken, queryEndToken); boolean endTokenIn = ConsistentHashRing.isTokenBetween(token.end, queryStartToken, queryEndToken); if (startTokenIn && endTokenIn) { moveEntries.putAll(map); it.remove(); } else if (startTokenIn || endTokenIn) { Iterator<Entry<K, V>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Entry<K, V> n = iterator.next(); long entryToken = ConsistentHashRing.hash(Serializer.toByteArray(n.getKey())); if (ConsistentHashRing.isTokenBetween(entryToken, queryStartToken, queryEndToken)) { moveEntries.put(n.getKey(), n.getValue()); iterator.remove(); } } } } } } // if(moveEntries.size() == 0) { // // boolean i1 = Arrays.binarySearch(service.bucketIds, startBucket) >= 0; // boolean i2 = Arrays.binarySearch( service.bucketIds, endBucket) >= 0; // // if(!i1 && !i2) { // System.out.println("new i don't have that range"); // } // // int closestSb = oldRing.findBucketIdFromToken(queryStartToken); // int closestEb = oldRing.findBucketIdFromToken(queryEndToken); // int[] bucketForRing = service.createBucketForRing(oldRing); // // boolean i3 = Arrays.binarySearch(bucketForRing, closestSb) >= 0; // boolean i4 = Arrays.binarySearch(bucketForRing, closestEb) >= 0; // // if(!i3 && !i4) { // System.out.println("old i don't have that range"); // } // } Member sender = ctx.getSender(); if (sender == null || !sender.equals(service.getContext().getCluster().getLocalMember())) RingMap.LOGGER.debug("moving {} entries [{}, {}] to {}", moveEntries.size(), queryStartToken, queryEndToken, sender); else RingMap.LOGGER.debug("moving {} entries [{}, {}] to local", moveEntries.size(), queryStartToken, queryEndToken); ctx.reply(moveEntries); } // } }