com.jivesoftware.os.amza.service.AmzaTestCluster.java Source code

Java tutorial

Introduction

Here is the source code for com.jivesoftware.os.amza.service.AmzaTestCluster.java

Source

/*
 * Copyright 2013 Jive Software, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.jivesoftware.os.amza.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.jivesoftware.os.amza.api.AmzaInterner;
import com.jivesoftware.os.amza.api.partition.Consistency;
import com.jivesoftware.os.amza.api.partition.Durability;
import com.jivesoftware.os.amza.api.partition.PartitionName;
import com.jivesoftware.os.amza.api.partition.PartitionProperties;
import com.jivesoftware.os.amza.api.partition.VersionedPartitionName;
import com.jivesoftware.os.amza.api.ring.RingHost;
import com.jivesoftware.os.amza.api.ring.RingMember;
import com.jivesoftware.os.amza.api.ring.TimestampedRingHost;
import com.jivesoftware.os.amza.api.scan.RowStream;
import com.jivesoftware.os.amza.api.scan.RowsChanged;
import com.jivesoftware.os.amza.api.stream.RowType;
import com.jivesoftware.os.amza.api.stream.TxKeyValueStream;
import com.jivesoftware.os.amza.api.take.TakeCursors;
import com.jivesoftware.os.amza.berkeleydb.BerkeleyDBWALIndexProvider;
import com.jivesoftware.os.amza.lab.pointers.LABPointerIndexConfig;
import com.jivesoftware.os.amza.lab.pointers.LABPointerIndexWALIndexProvider;
import com.jivesoftware.os.amza.service.AmzaServiceInitializer.AmzaServiceConfig;
import com.jivesoftware.os.amza.service.EmbeddedClientProvider.CheckOnline;
import com.jivesoftware.os.amza.service.Partition.ScanRange;
import com.jivesoftware.os.amza.service.replication.TakeFailureListener;
import com.jivesoftware.os.amza.service.ring.AmzaRingReader;
import com.jivesoftware.os.amza.service.ring.RingTopology;
import com.jivesoftware.os.amza.service.stats.AmzaStats;
import com.jivesoftware.os.amza.service.storage.PartitionCreator;
import com.jivesoftware.os.amza.service.storage.PartitionPropertyMarshaller;
import com.jivesoftware.os.amza.service.storage.binary.BinaryHighwaterRowMarshaller;
import com.jivesoftware.os.amza.service.storage.binary.BinaryPrimaryRowMarshaller;
import com.jivesoftware.os.amza.service.take.AvailableRowsTaker;
import com.jivesoftware.os.amza.service.take.RowsTaker;
import com.jivesoftware.os.amza.service.take.StreamingTakesConsumer;
import com.jivesoftware.os.amza.service.take.StreamingTakesConsumer.StreamingTakeConsumed;
import com.jivesoftware.os.aquarium.AquariumStats;
import com.jivesoftware.os.jive.utils.ordered.id.ConstantWriterIdProvider;
import com.jivesoftware.os.jive.utils.ordered.id.JiveEpochTimestampProvider;
import com.jivesoftware.os.jive.utils.ordered.id.OrderIdProviderImpl;
import com.jivesoftware.os.jive.utils.ordered.id.SnowflakeIdPacker;
import com.jivesoftware.os.jive.utils.ordered.id.TimestampedOrderIdProvider;
import com.jivesoftware.os.mlogger.core.CountersAndTimers;
import com.jivesoftware.os.mlogger.core.MetricLogger;
import com.jivesoftware.os.mlogger.core.MetricLoggerFactory;
import com.jivesoftware.os.routing.bird.health.api.HealthTimer;
import com.jivesoftware.os.routing.bird.health.api.NoOpHealthChecker;
import com.jivesoftware.os.routing.bird.health.checkers.SickThreads;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.mutable.MutableBoolean;
import org.apache.commons.lang.mutable.MutableInt;
import org.merlin.config.BindInterfaceToConfiguration;
import org.xerial.snappy.SnappyInputStream;
import org.xerial.snappy.SnappyOutputStream;

public class AmzaTestCluster {

    private final MetricLogger LOG = MetricLoggerFactory.getLogger();

    private final File workingDirctory;
    private final ConcurrentSkipListMap<RingMember, AmzaNode> cluster = new ConcurrentSkipListMap<>();
    private int oddsOfAConnectionFailureWhenAdding = 0; // 0 never - 100 always
    private int oddsOfAConnectionFailureWhenTaking = 0; // 0 never - 100 always
    private AmzaService lastAmzaService = null;

    public AmzaTestCluster(File workingDirctory, int oddsOfAConnectionFailureWhenAdding,
            int oddsOfAConnectionFailureWhenTaking) {
        this.workingDirctory = workingDirctory;
        this.oddsOfAConnectionFailureWhenAdding = oddsOfAConnectionFailureWhenAdding;
        this.oddsOfAConnectionFailureWhenTaking = oddsOfAConnectionFailureWhenTaking;
    }

    public Collection<AmzaNode> getAllNodes() {
        return cluster.values();
    }

    public AmzaNode get(RingMember ringMember) {
        return cluster.get(ringMember);
    }

    public void remove(RingMember ringMember) {
        cluster.remove(ringMember);
    }

    public AmzaNode newNode(final RingMember localRingMember, final RingHost localRingHost) throws Exception {

        AmzaNode service = cluster.get(localRingMember);
        if (service != null) {
            return service;
        }

        AmzaServiceConfig config = new AmzaServiceConfig();
        config.workingDirectories = new String[] {
                workingDirctory.getAbsolutePath() + "/" + localRingHost.getHost() + "-" + localRingHost.getPort() };
        config.aquariumLivelinessFeedEveryMillis = 500;
        config.maxUpdatesBeforeDeltaStripeCompaction = 10;
        config.deltaStripeCompactionIntervalInMillis = 1000;
        config.flushHighwatersAfterNUpdates = 10;

        config.initialBufferSegmentSize = 1_024;
        config.maxBufferSegmentSize = 10 * 1_024;

        config.updatesBetweenLeaps = 10;
        config.useMemMap = true;

        SnowflakeIdPacker idPacker = new SnowflakeIdPacker();
        OrderIdProviderImpl orderIdProvider = new OrderIdProviderImpl(
                new ConstantWriterIdProvider(localRingHost.getPort()), idPacker, new JiveEpochTimestampProvider());

        AvailableRowsTaker availableRowsTaker = (localRingMember1, localTimestampedRingHost, remoteRingMember,
                remoteRingHost, system, takeSessionId, takeSharedKey, timeoutMillis, updatedPartitionsStream,
                pingStream) -> {

            AmzaNode amzaNode = cluster.get(remoteRingMember);
            if (amzaNode == null) {
                throw new IllegalStateException("Service doesn't exist for " + remoteRingMember);
            } else {
                amzaNode.takePartitionUpdates(localRingMember1, localTimestampedRingHost, system, takeSessionId,
                        takeSharedKey, timeoutMillis, (versionedPartitionName, txId) -> {
                            if (versionedPartitionName != null) {
                                updatedPartitionsStream.available(versionedPartitionName, txId);
                            }
                        }, () -> {
                            LOG.debug("Special delivery! Special delivery!");
                            return null;
                        }, () -> {
                            LOG.debug("Ping pong!");
                            return null;
                        });
            }
        };

        RowsTaker updateTaker = new RowsTaker() {

            @Override
            public RowsTaker.StreamingRowsResult rowsStream(RingMember localRingMember, RingMember remoteRingMember,
                    RingHost remoteRingHost, VersionedPartitionName remoteVersionedPartitionName,
                    long takeSessionId, long takeSharedKey, long remoteTxId, long localLeadershipToken, long limit,
                    RowStream rowStream) {

                AmzaNode amzaNode = cluster.get(remoteRingMember);
                if (amzaNode == null) {
                    throw new IllegalStateException("Service doesn't exist for " + localRingMember);
                } else {
                    StreamingTakesConsumer.StreamingTakeConsumed consumed = amzaNode.rowsStream(localRingMember,
                            remoteVersionedPartitionName, takeSessionId, takeSharedKey, remoteTxId,
                            localLeadershipToken, limit, rowStream);
                    HashMap<RingMember, Long> otherHighwaterMarks = consumed.isOnline ? new HashMap<>() : null;
                    return new StreamingRowsResult(null, null, consumed.leadershipToken, consumed.partitionVersion,
                            otherHighwaterMarks);
                }
            }

            @Override
            public boolean rowsTaken(RingMember localRingMember, RingMember remoteRingMember,
                    RingHost remoteRingHost, long takeSessionId, long takeSharedKey,
                    VersionedPartitionName remoteVersionedPartitionName, long remoteTxId,
                    long localLeadershipToken) {
                AmzaNode amzaNode = cluster.get(remoteRingMember);
                if (amzaNode == null) {
                    throw new IllegalStateException("Service doesn't exists for " + localRingMember);
                } else {
                    try {
                        amzaNode.remoteMemberTookToTxId(localRingMember, takeSessionId, takeSharedKey,
                                remoteVersionedPartitionName, remoteTxId, localLeadershipToken);
                        return true;
                    } catch (Exception x) {
                        throw new RuntimeException("Issue while applying acks.", x);
                    }
                }
            }

            @Override
            public boolean pong(RingMember localRingMember, RingMember remoteRingMember, RingHost remoteRingHost,
                    long takeSessionId, long takeSharedKey) {
                AmzaNode amzaNode = cluster.get(remoteRingMember);
                if (amzaNode == null) {
                    throw new IllegalStateException("Service doesn't exists for " + localRingMember);
                } else {
                    try {
                        amzaNode.remoteMemberPong(localRingMember, takeSessionId, takeSharedKey);
                        return true;
                    } catch (Exception x) {
                        throw new RuntimeException("Issue while replying to pings.", x);
                    }
                }
            }

            @Override
            public boolean invalidate(RingMember localRingMember, RingMember remoteRingMember,
                    RingHost remoteRingHost, long takeSessionId, long takeSharedKey,
                    VersionedPartitionName remoteVersionedPartitionName) {
                return true;
            }
        };

        final ObjectMapper mapper = new ObjectMapper();
        PartitionPropertyMarshaller partitionPropertyMarshaller = new PartitionPropertyMarshaller() {

            @Override
            public PartitionProperties fromBytes(byte[] bytes) {
                try {
                    return mapper.readValue(bytes, PartitionProperties.class);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }

            @Override
            public byte[] toBytes(PartitionProperties partitionProperties) {
                try {
                    return mapper.writeValueAsBytes(partitionProperties);
                } catch (JsonProcessingException ex) {
                    throw new RuntimeException(ex);
                }
            }
        };

        AmzaInterner amzaInterner = new AmzaInterner();
        AmzaStats amzaSystemStats = new AmzaStats();
        AmzaStats amzaStats = new AmzaStats();
        SickThreads sickThreads = new SickThreads();
        SickPartitions sickPartitions = new SickPartitions();
        Optional<TakeFailureListener> absent = Optional.<TakeFailureListener>absent();

        BinaryPrimaryRowMarshaller primaryRowMarshaller = new BinaryPrimaryRowMarshaller(); // hehe you cant change this :)
        BinaryHighwaterRowMarshaller highwaterRowMarshaller = new BinaryHighwaterRowMarshaller(amzaInterner);
        AquariumStats aquariumStats = new AquariumStats();

        AmzaService amzaService = new AmzaServiceInitializer().initialize(config, amzaInterner, aquariumStats,
                amzaSystemStats, amzaStats,
                new HealthTimer(CountersAndTimers.getOrCreate("test"), "test", new NoOpHealthChecker<>("test")),
                cluster::size, sickThreads, sickPartitions, primaryRowMarshaller, highwaterRowMarshaller,
                localRingMember, localRingHost, Collections.emptySet(), orderIdProvider, idPacker,
                partitionPropertyMarshaller, (workingIndexDirectories, indexProviderRegistry,
                        ephemeralRowIOProvider, persistentRowIOProvider, partitionStripeFunction) -> {

                    indexProviderRegistry
                            .register(
                                    new BerkeleyDBWALIndexProvider(BerkeleyDBWALIndexProvider.INDEX_CLASS_NAME,
                                            partitionStripeFunction, workingIndexDirectories),
                                    persistentRowIOProvider);

                    LABPointerIndexConfig labConfig = BindInterfaceToConfiguration
                            .bindDefault(LABPointerIndexConfig.class);

                    indexProviderRegistry.register(new LABPointerIndexWALIndexProvider(amzaInterner, labConfig,
                            Executors.newCachedThreadPool(), Executors.newCachedThreadPool(),
                            Executors.newCachedThreadPool(), Executors.newCachedThreadPool(),
                            LABPointerIndexWALIndexProvider.INDEX_CLASS_NAME, partitionStripeFunction,
                            workingIndexDirectories), persistentRowIOProvider);

                }, availableRowsTaker, () -> updateTaker, () -> updateTaker, absent, (changes) -> {
                }, (threadCount, name) -> {
                    return Executors.newCachedThreadPool();
                });

        amzaService.start(localRingMember, localRingHost);

        try {
            //amzaService.getRingWriter().addRingMember(AmzaRingReader.SYSTEM_RING, localRingMember); // ?? Hacky
            TimestampedRingHost timestampedRingHost = amzaService.getRingReader().getRingHost();
            amzaService.getRingWriter().addRingMember("test".getBytes(), localRingMember); // ?? Hacky
            if (lastAmzaService != null) {
                TimestampedRingHost lastTimestampedRingHost = lastAmzaService.getRingReader().getRingHost();
                amzaService.getRingWriter().register(lastAmzaService.getRingReader().getRingMember(),
                        lastTimestampedRingHost.ringHost, lastTimestampedRingHost.timestampId, false);
                amzaService.getRingWriter().addRingMember("test".getBytes(),
                        lastAmzaService.getRingReader().getRingMember()); // ?? Hacky

                lastAmzaService.getRingWriter().register(localRingMember, localRingHost,
                        timestampedRingHost.timestampId, false);
                lastAmzaService.getRingWriter().addRingMember("test".getBytes(), localRingMember); // ?? Hacky
            }
            lastAmzaService = amzaService;
        } catch (Exception x) {
            x.printStackTrace();
            System.out.println("FAILED CONNECTING RING");
            System.exit(1);
        }

        service = new AmzaNode(amzaInterner, localRingMember, localRingHost, amzaService, orderIdProvider,
                sickThreads, sickPartitions);

        cluster.put(localRingMember, service);

        System.out.println("Added serviceHost:" + localRingMember + " to the cluster.");
        return service;
    }

    public class AmzaNode {

        private final Random random = new Random();
        private final AmzaInterner amzaInterner;
        final RingMember ringMember;
        final RingHost ringHost;
        private final AmzaService amzaService;
        private final TimestampedOrderIdProvider orderIdProvider;
        final SickThreads sickThreads;
        final SickPartitions sickPartitions;
        private boolean off = false;
        private int flapped = 0;
        private final ExecutorService asIfOverTheWire = Executors.newSingleThreadExecutor();
        private final EmbeddedClientProvider clientProvider;

        public AmzaNode(AmzaInterner amzaInterner, RingMember ringMember, RingHost ringHost,
                AmzaService amzaService, TimestampedOrderIdProvider orderIdProvider, SickThreads sickThreads,
                SickPartitions sickPartitions) {

            this.amzaInterner = amzaInterner;
            this.ringMember = ringMember;
            this.ringHost = ringHost;
            this.amzaService = amzaService;
            this.clientProvider = new EmbeddedClientProvider(amzaService);
            this.orderIdProvider = orderIdProvider;
            this.sickThreads = sickThreads;
            this.sickPartitions = sickPartitions;
        }

        @Override
        public String toString() {
            return ringMember.toString();
        }

        public boolean isOff() {
            return off;
        }

        public void setOff(boolean off) {
            this.off = off;
            flapped++;
        }

        public void stop() throws Exception {
            amzaService.stop();
            asIfOverTheWire.shutdownNow();
        }

        public void create(Consistency consistency, PartitionName partitionName, String indexClassName,
                int maxValueSizeInIndex, RowType rowType) throws Exception {
            // TODO test other consistencies and durabilities and .... Hehe
            PartitionProperties properties = new PartitionProperties(Durability.fsync_never, 0, 0, 0, 0, 0, 0, 0, 0,
                    false, consistency, true, true, false, rowType, indexClassName, maxValueSizeInIndex, null, -1,
                    -1);
            amzaService.createPartitionIfAbsent(partitionName, properties);
            amzaService.awaitOnline(partitionName, Integer.MAX_VALUE); //TODO lololol
        }

        public void update(Consistency consistency, PartitionName partitionName, byte[] p, byte[] k, byte[] v,
                boolean tombstone) throws Exception {
            if (off) {
                throw new RuntimeException("Service is off:" + ringMember);
            }

            clientProvider.getClient(partitionName, CheckOnline.always).commit(consistency, p,
                    commitKeyValueStream -> {
                        long timestamp = orderIdProvider.nextId();
                        if (tombstone) {
                            return commitKeyValueStream.commit(k, null, timestamp, true);
                        } else {
                            return commitKeyValueStream.commit(k, v, timestamp, false);
                        }
                    }, 10, TimeUnit.SECONDS);

        }

        public byte[] get(Consistency consistency, PartitionName partitionName, byte[] prefix, byte[] key)
                throws Exception {
            if (off) {
                throw new RuntimeException("Service is off:" + ringMember);
            }

            List<byte[]> got = new ArrayList<>();
            clientProvider.getClient(partitionName, CheckOnline.always).get(consistency, prefix,
                    stream -> stream.stream(key), (_prefix, _key, value, timestamp, version) -> {
                        got.add(value);
                        return true;
                    });
            return got.get(0);
        }

        public TakeCursors takeFromTransactionId(PartitionName partitionName, long transactionId,
                TxKeyValueStream stream) throws Exception {
            if (off) {
                throw new RuntimeException("Service is off:" + ringMember);
            }
            return clientProvider.getClient(partitionName, CheckOnline.always).takeFromTransactionId(transactionId,
                    stream);
        }

        public void watch(PartitionName partitionName) throws Exception {
            amzaService.watch(partitionName, (RowsChanged changes) -> {
                /*if (changes.getApply().size() > 0) {
                 System.out.println("Service:" + localRingMember
                 + " Partition:" + partitionName.getName()
                 + " Changed:" + changes.getApply().size());
                 }*/
            });
        }

        void remoteMemberTookToTxId(RingMember remoteRingMember, long takeSessionId, long takeSharedKey,
                VersionedPartitionName remoteVersionedPartitionName, long localTxId, long leadershipToken)
                throws Exception {
            amzaService.rowsTaken(remoteRingMember, takeSessionId, takeSharedKey, remoteVersionedPartitionName,
                    localTxId, leadershipToken);
        }

        void remoteMemberPong(RingMember remoteRingMember, long takeSessionId, long takeSharedKey)
                throws Exception {
            amzaService.pong(remoteRingMember, takeSessionId, takeSharedKey);
        }

        StreamingTakeConsumed rowsStream(RingMember remoteRingMember,
                VersionedPartitionName localVersionedPartitionName, long takeSessionId, long takeSharedKey,
                long localTxId, long leadershipToken, long limit, RowStream rowStream) {
            if (off) {
                throw new RuntimeException("Service is off:" + ringMember);
            }
            if (random.nextInt(100) > (100 - oddsOfAConnectionFailureWhenTaking)) {
                throw new RuntimeException("Random take failure:" + ringMember);
            }

            try {
                ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
                Future<Object> submit = asIfOverTheWire.submit(() -> {
                    DataOutputStream dos = new DataOutputStream(
                            new BufferedOutputStream(new SnappyOutputStream(bytesOut), 8192));
                    amzaService.rowsStream(dos, remoteRingMember, localVersionedPartitionName, takeSessionId,
                            takeSharedKey, localTxId, leadershipToken, limit);
                    dos.flush();
                    return null;
                });
                submit.get();

                StreamingTakesConsumer streamingTakesConsumer = new StreamingTakesConsumer(amzaInterner);
                // this is some sick joke
                DataInputStream in = new DataInputStream(new SnappyInputStream(
                        new BufferedInputStream(new ByteArrayInputStream(bytesOut.toByteArray()), 8192)));
                return streamingTakesConsumer.consume(in, rowStream);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public void printService() throws Exception {
            if (off) {
                System.out.println(ringHost.getHost() + ":" + ringHost.getPort() + " is OFF flapped:" + flapped);
            }
        }

        public void printRings() {
            try {
                RingTopology systemRing = amzaService.getRingReader().getRing(AmzaRingReader.SYSTEM_RING, 10_000);
                System.out.println("RING:" + " me:" + amzaService.getRingReader().getRingMember() + " ring:"
                        + Lists.transform(systemRing.entries, input -> input.ringMember));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public boolean isEmpty() throws Exception {
            Set<PartitionName> allAPartitions = Sets.newHashSet(amzaService.getAllPartitionNames());
            if (allAPartitions.isEmpty()) {
                return true;
            }

            for (PartitionName partitionName : allAPartitions) {
                if (!partitionName.isSystemPartition()) {
                    Partition partition = amzaService.getPartition(partitionName);
                    int[] count = { 0 };
                    partition.scan(Collections.singletonList(ScanRange.ROW_SCAN), true, true,
                            (prefix, key, value, timestamp, tombstoned, version) -> {
                                if (!tombstoned) {
                                    count[0]++;
                                }
                                return true;
                            });
                    if (count[0] > 0) {
                        return false;
                    }
                }
            }
            return true;
        }

        private final Set<PartitionName> IGNORED_PARTITION_NAMES = ImmutableSet.of(
                PartitionCreator.HIGHWATER_MARK_INDEX.getPartitionName(),
                PartitionCreator.AQUARIUM_LIVELINESS_INDEX.getPartitionName());

        public boolean compare(Consistency consistency, AmzaNode service) throws Exception {
            if (off || service.off) {
                return true;
            }

            Set<PartitionName> allAPartitions = Sets.newHashSet();
            Iterables.addAll(allAPartitions, amzaService.getSystemPartitionNames());
            Iterables.addAll(allAPartitions, amzaService.getMemberPartitionNames());

            Set<PartitionName> allBPartitions = Sets.newHashSet(service.amzaService.getAllPartitionNames());
            Iterables.addAll(allBPartitions, service.amzaService.getSystemPartitionNames());
            Iterables.addAll(allBPartitions, service.amzaService.getMemberPartitionNames());

            if (allAPartitions.size() != allBPartitions.size()) {
                System.out.println(allAPartitions + " -vs- " + allBPartitions);
                return false;
            }

            Set<PartitionName> partitionNames = new HashSet<>();
            partitionNames.addAll(allAPartitions);
            partitionNames.addAll(allBPartitions);

            RingTopology aRing = amzaService.getRingReader().getRing(AmzaRingReader.SYSTEM_RING, -1);
            RingTopology bRing = service.amzaService.getRingReader().getRing(AmzaRingReader.SYSTEM_RING, -1);

            if (!aRing.entries.equals(bRing.entries)) {
                System.out.println(aRing + "-vs-" + bRing);
                return false;
            }

            for (PartitionName partitionName : partitionNames) {
                if (IGNORED_PARTITION_NAMES.contains(partitionName)) {
                    continue;
                }

                Partition a = amzaService.getPartition(partitionName);
                Partition b = service.amzaService.getPartition(partitionName);
                if (a == null || b == null) {
                    System.out.println(partitionName + " " + amzaService.getRingReader().getRingMember() + " " + a
                            + " -- vs --" + service.amzaService.getRingReader().getRingMember() + " " + b);
                    return false;
                }
                if (!compare(consistency, partitionName, a, b)) {
                    return false;
                }
            }
            return true;
        }

        private void takePartitionUpdates(RingMember ringMember, TimestampedRingHost timestampedRingHost,
                boolean system, long sessionId, long sharedKey, long timeoutMillis,
                AvailableRowsTaker.AvailableStream updatedPartitionsStream, Callable<Void> deliverCallback,
                Callable<Void> pingCallback) throws Exception {

            if (off) {
                throw new RuntimeException("Service is off:" + ringMember);
            }
            if (random.nextInt(100) > (100 - oddsOfAConnectionFailureWhenTaking)) {
                throw new RuntimeException("Random take failure:" + ringMember);
            }

            amzaService.availableRowsStream(system, ringMember, timestampedRingHost, sessionId, sharedKey,
                    timeoutMillis, updatedPartitionsStream, deliverCallback, pingCallback);
        }

        //  Use for testing
        private boolean compare(Consistency consistency, PartitionName partitionName, Partition a, Partition b)
                throws Exception {
            final MutableInt compared = new MutableInt(0);
            final MutableBoolean passed = new MutableBoolean(true);
            try {
                a.scan(Collections.singletonList(ScanRange.ROW_SCAN), true, true,
                        (prefix, key, aValue, aTimestamp, aTombstoned, aVersion) -> {
                            try {
                                compared.increment();
                                long[] btimestamp = { -1L };
                                byte[][] bvalue = new byte[1][];
                                boolean[] btombstoned = new boolean[1];
                                long[] bversion = new long[1];
                                b.get(consistency, prefix, true, stream -> stream.stream(key),
                                        (_prefix, _key, value, timestamp, tombstoned, version) -> {
                                            btimestamp[0] = timestamp;
                                            bvalue[0] = value;
                                            btombstoned[0] = tombstoned;
                                            bversion[0] = version;
                                            return true;
                                        });

                                long bTimestamp = btimestamp[0];
                                byte[] bValue = bvalue[0];
                                boolean bTombstoned = btombstoned[0];
                                long bVersion = bversion[0];
                                String comparing = new String(partitionName.getRingName()) + ":"
                                        + new String(partitionName.getName()) + " to "
                                        + new String(partitionName.getRingName()) + ":"
                                        + new String(partitionName.getName()) + "\n";

                                if (bTimestamp == -1) {
                                    System.out.println("INCONSISTENCY: " + comparing + " timestamp:'" + bTimestamp
                                            + "' != -1" + " \n" + Arrays.toString(aValue) + " vs null");
                                    passed.setValue(false);
                                    return false;
                                }
                                if (aTimestamp != bTimestamp) {
                                    System.out.println("INCONSISTENCY: " + comparing + " timestamp:'" + aTimestamp
                                            + "' != '" + bTimestamp + "' \n" + Arrays.toString(aValue) + " vs "
                                            + Arrays.toString(bValue));
                                    passed.setValue(false);
                                    System.out.println("----------------------------------");
                                    return false;
                                }
                                if (aTombstoned != bTombstoned) {
                                    System.out.println("INCONSISTENCY: " + comparing + " tombstoned:'" + aTombstoned
                                            + "' != '" + bTombstoned + "' \n" + Arrays.toString(aValue) + " vs "
                                            + Arrays.toString(bValue));
                                    passed.setValue(false);
                                    System.out.println("----------------------------------");
                                    return false;
                                }
                                if (aVersion != bVersion) {
                                    System.out.println("INCONSISTENCY: " + comparing + " version:'" + aVersion
                                            + "' != '" + bVersion + "' \n" + Arrays.toString(aValue) + " vs "
                                            + Arrays.toString(bValue));
                                    passed.setValue(false);
                                    System.out.println("----------------------------------");
                                    return false;
                                }
                                if (aValue == null && bValue != null) {
                                    System.out.println("INCONSISTENCY: " + comparing + " null" + " != '"
                                            + Arrays.toString(bValue) + "' \n" + "null" + " vs "
                                            + Arrays.toString(bValue));
                                    passed.setValue(false);
                                    return false;
                                }
                                if (aValue != null && !Arrays.equals(aValue, bValue)) {
                                    System.out.println("INCONSISTENCY: " + comparing + " value:'"
                                            + Arrays.toString(aValue) + "' != '" + Arrays.toString(bValue) + "' \n"
                                            + Arrays.toString(aValue) + " vs " + Arrays.toString(bValue));
                                    passed.setValue(false);
                                    return false;
                                }
                                return true;
                            } catch (Exception x) {
                                throw new RuntimeException("Failed while comparing", x);
                            }
                        });
            } catch (Exception e) {
                System.out.println("EXCEPTION: " + e.getMessage());
                e.printStackTrace();
                passed.setValue(false);
            }

            System.out.println("partition:" + new String(partitionName.getName()) + " vs:"
                    + new String(partitionName.getName()) + " compared:" + compared + " keys");
            return passed.booleanValue();
        }

        public AmzaService.AmzaPartitionRoute getPartitionRoute(PartitionName partitionName) throws Exception {
            return amzaService.getPartitionRoute(partitionName, 0);
        }

        public void compactAllTombstones() throws Exception {
            LOG.info("Manual compact all tombstones requests.");
            amzaService.compactAllTombstones();
        }

        public void mergeAllDeltas(boolean force) {
            amzaService.mergeAllDeltas(true);
        }

        public void expunge() throws Exception {
            amzaService.expunge();
        }
    }
}