/**
* Copyright 2011 LiveRamp
*
* 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.liveramp.hank.storage.incremental;
import com.liveramp.hank.coordinator.Domain;
import com.liveramp.hank.coordinator.DomainVersion;
import com.liveramp.hank.coordinator.DomainVersions;
import com.liveramp.hank.storage.PartitionRemoteFileOps;
import java.io.IOException;
import java.util.*;
public abstract class IncrementalUpdatePlanner {
protected final Domain domain;
public IncrementalUpdatePlanner(Domain domain) {
this.domain = domain;
}
public IncrementalUpdatePlan computeUpdatePlan(DomainVersion updatingToVersion) throws IOException {
return computeUpdatePlan(null, new HashSet<DomainVersion>(), updatingToVersion);
}
/**
* Return the list of versions needed to update to the specific version given that
* the specified current version and cached bases are available.
*
* @param currentVersion
* @param cachedBases
* @param updatingToVersion
* @return
* @throws java.io.IOException
*/
protected IncrementalUpdatePlan computeUpdatePlan(DomainVersion currentVersion,
Set<DomainVersion> cachedBases,
DomainVersion updatingToVersion) throws IOException {
LinkedList<DomainVersion> updatePlanVersions = new LinkedList<DomainVersion>();
// Backtrack versions (ignoring defunct versions) until we find:
// - a base (no parent)
// - or the current version (which is by definition a base or a rebased delta)
// - or a version that is a base and that is cached
DomainVersion parentVersion = updatingToVersion;
while (parentVersion != null) {
// Ignore completely defunct versions
if (!parentVersion.isDefunct()) {
// If a version along the path is still open, abort
if (!DomainVersions.isClosed(parentVersion)) {
throw new IOException("Detected a domain version that is still open"
+ " along the path from current version to version to update to: "
+ " domain: " + domain
+ " open version: " + parentVersion
+ " current version: " + currentVersion
+ " updating to version: " + updatingToVersion);
}
// If backtrack to current version, use it and stop backtracking
if (currentVersion != null && parentVersion.equals(currentVersion)) {
// If we only need the current version, we don't need any plan
if (updatePlanVersions.isEmpty()) {
return null;
} else {
updatePlanVersions.add(parentVersion);
break;
}
}
// If backtrack to cached base version, use it and stop backtracking
if (cachedBases.contains(parentVersion)) {
updatePlanVersions.add(parentVersion);
break;
}
// Add backtracked version to versions needed
updatePlanVersions.add(parentVersion);
}
// Move to parent version
parentVersion = getParentDomainVersion(parentVersion);
}
if (updatePlanVersions.isEmpty()) {
return null;
}
// The base is the last version that was added (a base, the current version or a cached base)
DomainVersion base = updatePlanVersions.removeLast();
// Check that the base we are going to update from is not invalid
if (!(getParentDomainVersion(base) == null ||
cachedBases.contains(base) ||
(currentVersion != null && base.equals(currentVersion)))) {
throw new IOException("Failed to find a valid base from which to update: "
+ " domain: " + domain
+ " not a valid base: " + base
+ " current version: " + currentVersion
+ " updating to version: " + updatingToVersion);
}
// Reverse list of deltas as we have added versions going backwards
Collections.reverse(updatePlanVersions);
return new IncrementalUpdatePlan(base, updatePlanVersions);
}
public DomainVersion getParentDomainVersion(DomainVersion domainVersion) throws IOException {
return IncrementalDomainVersionProperties.getParentDomainVersion(domain, domainVersion);
}
public abstract List<String> getRemotePartitionFilePaths(IncrementalUpdatePlan updatePlan,
PartitionRemoteFileOps partitionRemoteFileOps) throws IOException;
}