package com.jivesoftware.os.amza.service; import com.jivesoftware.os.amza.api.PartitionClient; import com.jivesoftware.os.amza.api.partition.Consistency; import com.jivesoftware.os.amza.api.ring.RingMember; import com.jivesoftware.os.amza.api.stream.ClientUpdates; import com.jivesoftware.os.amza.api.stream.KeyValueStream; import com.jivesoftware.os.amza.api.stream.KeyValueTimestampStream; import com.jivesoftware.os.amza.api.stream.OffsetUnprefixedWALKeys; import com.jivesoftware.os.amza.api.stream.PrefixedKeyRanges; import com.jivesoftware.os.amza.api.stream.TxKeyValueStream; import com.jivesoftware.os.amza.api.stream.TxKeyValueStream.TxResult; import com.jivesoftware.os.amza.api.stream.UnprefixedWALKeys; import com.jivesoftware.os.amza.api.take.Highwaters; import com.jivesoftware.os.amza.api.take.TakeResult; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Optional; /** * */ public class EmbeddedPartitionClient implements PartitionClient { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); private final Partition partition; private final RingMember rootRingMember; public EmbeddedPartitionClient(Partition partition, RingMember rootRingMember) { this.partition = partition; this.rootRingMember = rootRingMember; } @Override public void commit(Consistency consistency, byte[] prefix, ClientUpdates updates, long additionalSolverAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { partition.commit(consistency, prefix, updates, abandonSolutionAfterNMillis); } @Override public long getApproximateCount(Consistency consistency, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { return partition.approximateCount(); } @Override public boolean get(Consistency consistency, byte[] prefix, UnprefixedWALKeys keys, KeyValueTimestampStream valuesStream, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { return partition.get(consistency, prefix, true, keys, (prefix1, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { if (valueTimestamp == -1 || valueTombstoned) { return valuesStream.stream(prefix1, key, null, -1, -1); } else { return valuesStream.stream(prefix1, key, value, valueTimestamp, valueVersion); } }); } @Override public boolean getOffset(Consistency consistency, byte[] prefix, OffsetUnprefixedWALKeys keys, KeyValueTimestampStream valuesStream, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { Deque<int[]> offsetLengths = new ArrayDeque<>(); return partition.get(consistency, prefix, true, keyStream -> { return keys.consume((key, offset, length) -> { offsetLengths.addLast(new int[] { offset, length }); return keyStream.stream(key); }); }, (prefix1, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { int[] offsetLength = offsetLengths.removeFirst(); int offset = offsetLength[0]; int length = offsetLength[1]; if (valueTimestamp == -1 || valueTombstoned) { return valuesStream.stream(prefix1, key, null, -1, -1); } else { if (value == null || offset == 0 && length >= value.length) { // do nothing } else if (offset >= value.length) { value = null; } else { int available = Math.min(length, value.length - offset); byte[] sub = new byte[available]; System.arraycopy(value, offset, sub, 0, available); value = sub; } return valuesStream.stream(prefix1, key, value, valueTimestamp, valueVersion); } }); } @Override public boolean getRaw(Consistency consistency, byte[] prefix, UnprefixedWALKeys keys, KeyValueStream valuesStream, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { return partition.get(consistency, prefix, true, keys, valuesStream); } @Override public boolean scan(Consistency consistency, boolean compressed, PrefixedKeyRanges ranges, KeyValueTimestampStream scan, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { return scanInternal(consistency, compressed, ranges, null, scan, true, additionalSolverAfterNMillis, abandonLeaderSolutionAfterNMillis, abandonSolutionAfterNMillis, solutionLog); } @Override public boolean scanFiltered(Consistency consistency, boolean compressed, PrefixedKeyRanges ranges, KeyValueFilter filter, KeyValueTimestampStream scan, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { return scanInternal(consistency, compressed, ranges, filter, scan, true, additionalSolverAfterNMillis, abandonLeaderSolutionAfterNMillis, abandonSolutionAfterNMillis, solutionLog); } @Override public boolean scanKeys(Consistency consistency, boolean compressed, PrefixedKeyRanges ranges, KeyValueTimestampStream scan, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { return scanInternal(consistency, compressed, ranges, null, scan, false, additionalSolverAfterNMillis, abandonLeaderSolutionAfterNMillis, abandonSolutionAfterNMillis, solutionLog); } private boolean scanInternal(Consistency consistency, boolean compressed, PrefixedKeyRanges ranges, KeyValueFilter filter, KeyValueTimestampStream stream, boolean hydrateValues, long additionalSolverAfterNMillis, long abandonLeaderSolutionAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { if (filter != null) { KeyValueStream filterStream = (prefix, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { // valueTombstoned will be false return stream.stream(prefix, key, value, valueTimestamp, valueVersion); }; return partition.scan(ranges, hydrateValues, true, (prefix, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { return valueTombstoned || filter.filter(prefix, key, value, valueTimestamp, false, valueVersion, filterStream); }); } else { return partition.scan(ranges, hydrateValues, true, (prefix, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { return valueTombstoned || stream.stream(prefix, key, value, valueTimestamp, valueVersion); }); } } @Override public TakeResult takeFromTransactionId(List<RingMember> membersInOrder, Map<RingMember, Long> memberTxIds, int limit, Highwaters highwaters, TxKeyValueStream stream, long additionalSolverAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { if (!membersInOrder.contains(rootRingMember)) { LOG.warn("Took from {} but not in desired members {}", rootRingMember, membersInOrder); return new TakeResult(rootRingMember, -1L, null); } long txId = memberTxIds.getOrDefault(rootRingMember, -1L); int[] count = { 0 }; return partition.takeFromTransactionId(txId, true, highwaters, (rowTxId, prefix, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { TxResult result = stream.stream(rowTxId, prefix, key, value, valueTimestamp, valueTombstoned, valueVersion); count[0]++; return (limit > 0 && count[0] >= limit) ? TxResult.ACCEPT_AND_STOP : result; }); } @Override public TakeResult takePrefixFromTransactionId(List<RingMember> membersInOrder, byte[] prefix, Map<RingMember, Long> memberTxIds, int limit, Highwaters highwaters, TxKeyValueStream stream, long additionalSolverAfterNMillis, long abandonSolutionAfterNMillis, Optional<List<String>> solutionLog) throws Exception { if (!membersInOrder.contains(rootRingMember)) { LOG.warn("Took with prefix from {} but not in desired members {}", rootRingMember, membersInOrder); return new TakeResult(rootRingMember, -1L, null); } long txId = memberTxIds.getOrDefault(rootRingMember, -1L); int[] count = { 0 }; return partition.takePrefixFromTransactionId(prefix, txId, true, highwaters, (rowTxId, prefix1, key, value, valueTimestamp, valueTombstoned, valueVersion) -> { TxResult result = stream.stream(rowTxId, prefix1, key, value, valueTimestamp, valueTombstoned, valueVersion); count[0]++; return (limit > 0 && count[0] >= limit) ? TxResult.ACCEPT_AND_STOP : result; }); } }