package com.jivesoftware.os.amza.sync.deployable; import com.google.common.base.Joiner; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.PeekingIterator; import com.jivesoftware.os.amza.api.PartitionClient; import com.jivesoftware.os.amza.api.PartitionClientProvider; import com.jivesoftware.os.amza.api.RingPartitionProperties; import com.jivesoftware.os.amza.api.partition.Consistency; import com.jivesoftware.os.amza.api.partition.PartitionName; import com.jivesoftware.os.amza.api.partition.PartitionProperties; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; /** * */ public class AmzaSyncReceiver implements AmzaSyncClient { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); private final PartitionClientProvider partitionClientProvider; private final boolean useSolutionLog; private final Map<PartitionName, Consistency> consistencyCache = Maps.newConcurrentMap(); private final long additionalSolverAfterNMillis = 10_000; //TODO expose to conf? private final long abandonSolutionAfterNMillis = 60_000; //TODO expose to conf? public AmzaSyncReceiver(PartitionClientProvider partitionClientProvider, boolean useSolutionLog) { this.partitionClientProvider = partitionClientProvider; this.useSolutionLog = useSolutionLog; } @Override public void commitRows(PartitionName partitionName, List<Row> rows) throws Exception { LOG.info("Received from partition:{} rows:{}", partitionName, rows.size()); PartitionClient client = partitionClientProvider.getPartition(partitionName); Consistency consistency = consistencyCache.computeIfAbsent(partitionName, partitionName1 -> { try { RingPartitionProperties properties = partitionClientProvider.getProperties(partitionName1); return properties != null ? properties.partitionProperties.consistency : null; } catch (Exception e) { LOG.error("Failed to get properties for partition:{}", partitionName1); return null; } }); if (consistency == null) { throw new RuntimeException("Missing consistency for partition: " + partitionName); } PeekingIterator<Row> iter = Iterators.peekingIterator(rows.iterator()); Optional<List<String>> solutionLog = useSolutionLog ? Optional.of(Collections.synchronizedList(Lists.newArrayList())) : Optional.empty(); int[] batch = { 0 }; try { while (iter.hasNext()) { batch[0]++; solutionLog.ifPresent(log -> log.add("Batch " + batch[0])); byte[] prefix = iter.peek().prefix; client.commit(consistency, prefix, commitKeyValueStream -> { while (iter.hasNext()) { byte[] peek = iter.peek().prefix; if ((prefix == null && peek == null) || (prefix != null && peek != null && Arrays.equals(prefix, peek))) { Row row = iter.next(); commitKeyValueStream.commit(row.key, row.value, row.valueTimestamp, row.valueTombstoned); } else { break; } } return true; }, additionalSolverAfterNMillis, abandonSolutionAfterNMillis, solutionLog); } } catch (Exception e) { if (solutionLog.isPresent()) { LOG.error("Commit failure for {}, solution log:\n - {}", partitionName, Joiner.on("\n - ").join(solutionLog.get())); } throw e; } } @Override public void ensurePartition(PartitionName partitionName, PartitionProperties partitionProperties, int ringSize) throws Exception { partitionClientProvider.getPartition(partitionName, ringSize, partitionProperties); } }