/*
* Copyright © 2015 Cask Data, 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 co.cask.cdap.api.dataset.lib;
import co.cask.cdap.api.common.Bytes;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Contains the state necessary to keep track of which partitions are processed and which partitions would need to be
* processed as they are created.
*/
public class PartitionConsumerState {
// useful on initial query of partitions
public static final PartitionConsumerState FROM_BEGINNING =
new PartitionConsumerState(0, Collections.<Long>emptyList());
// Write pointer of a transaction, to be used as the start of the next scan for partitions.
private final long startVersion;
// The list of in progress transactions from the previous query of partitions that are smaller than the startVersion.
// We do not need to include the in-progress transaction Ids that are larger than the startVersion because those will
// be picked up in the next scan anyways, since we will start the scan from the startVersion.
// Keeping track of these in-progress transactions is necessary because these might be creations of partitions that
// fall before the startVersion.
private final List<Long> versionsToCheck;
public PartitionConsumerState(long startVersion, List<Long> versionsToCheck) {
if (versionsToCheck == null) {
throw new IllegalArgumentException("List of versions cannot be null");
}
this.startVersion = startVersion;
this.versionsToCheck = Collections.unmodifiableList(new ArrayList<>(versionsToCheck));
}
public long getStartVersion() {
return startVersion;
}
public List<Long> getVersionsToCheck() {
return versionsToCheck;
}
public static PartitionConsumerState fromBytes(byte[] bytes) {
if (((bytes.length - 1) % Bytes.SIZEOF_LONG) != 0) {
throw new IllegalArgumentException("bytes does not have length divisible by " + Bytes.SIZEOF_LONG);
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
byte serializationFormatVersion = bb.get();
if (serializationFormatVersion != 0) {
throw new IllegalArgumentException("Unsupported serialization format: " + serializationFormatVersion);
}
long startVersion = bb.getLong();
List<Long> versionsToCheck = new ArrayList<>();
while (bb.hasRemaining()) {
versionsToCheck.add(bb.getLong());
}
return new PartitionConsumerState(startVersion, versionsToCheck);
}
public byte[] toBytes() {
int numLongs = 1 + versionsToCheck.size();
// first byte for serialization format version
ByteBuffer bb = ByteBuffer.allocate(1 + Bytes.SIZEOF_LONG * numLongs);
// currently, serialization format is 0
bb.put((byte) 0);
bb.putLong(startVersion);
for (long l : versionsToCheck) {
bb.putLong(l);
}
return bb.array();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PartitionConsumerState that = (PartitionConsumerState) o;
if (startVersion != that.startVersion) {
return false;
}
return versionsToCheck.equals(that.versionsToCheck);
}
@Override
public int hashCode() {
int result = (int) (startVersion ^ (startVersion >>> 32));
result = 31 * result + versionsToCheck.hashCode();
return result;
}
}