/** * 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.curly; import com.liveramp.hank.compression.cueball.CueballCompressionCodec; import com.liveramp.hank.coordinator.Domain; import com.liveramp.hank.coordinator.DomainVersion; import com.liveramp.hank.partition_server.PartitionUpdateTaskStatistics; import com.liveramp.hank.storage.PartitionRemoteFileOps; import com.liveramp.hank.storage.cueball.CueballPartitionUpdater; import com.liveramp.hank.storage.cueball.ICueballMerger; import com.liveramp.hank.storage.incremental.IncrementalDomainVersionProperties; import com.liveramp.hank.storage.incremental.IncrementalUpdatePlan; import com.liveramp.hank.util.FormatUtils; import com.liveramp.hank.util.HankTimer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class CurlyFastPartitionUpdater extends AbstractCurlyPartitionUpdater { private static final Logger LOG = LoggerFactory.getLogger(CurlyFastPartitionUpdater.class); private final int keyHashSize; private final int offsetNumBytes; private final int valueSize; private final int hashIndexBits; private final CueballCompressionCodec compressionCodec; private final ICurlyMerger curlyMerger; private final ICueballMerger cueballMerger; public CurlyFastPartitionUpdater(Domain domain, PartitionRemoteFileOps partitionRemoteFileOps, ICurlyMerger curlyMerger, ICueballMerger cueballMerger, int keyHashSize, int offsetNumBytes, int offsetInBlockNumBytes, int hashIndexBits, CueballCompressionCodec compressionCodec, String localPartitionRoot) throws IOException { super(domain, partitionRemoteFileOps, localPartitionRoot); this.keyHashSize = keyHashSize; this.offsetNumBytes = offsetNumBytes; if (offsetInBlockNumBytes > 0) { this.valueSize = offsetNumBytes + offsetInBlockNumBytes; } else { this.valueSize = offsetNumBytes; } this.hashIndexBits = hashIndexBits; this.compressionCodec = compressionCodec; this.curlyMerger = curlyMerger; this.cueballMerger = cueballMerger; } @Override protected boolean shouldFetchCurlyVersion(DomainVersion version) throws IOException { // If this is the base, we also need to fetch the Curly file. Otherwise, // the Curly fetching is done on the fly during the update. return IncrementalDomainVersionProperties.isBase(version); } @Override protected void runUpdateCore(DomainVersion currentVersion, DomainVersion updatingToVersion, IncrementalUpdatePlan updatePlan, String updateWorkRoot, PartitionUpdateTaskStatistics statistics) throws IOException { // Run Curly update // Determine new base path CurlyFilePath newCurlyBasePath = new CurlyFilePath(updateWorkRoot + "/" + Curly.getName(updatingToVersion.getVersionNumber(), true)); // Determine base file from version CurlyFilePath curlyBase = getCurlyFilePathForVersion(updatePlan.getBase(), currentVersion, true); // Check that base file is available CueballPartitionUpdater.checkRequiredFileExists(curlyBase.getPath()); // Move the Curly base to the final destination, overwriting it File newCurlyBaseFile = new File(newCurlyBasePath.getPath()); if (newCurlyBaseFile.exists()) { if (!newCurlyBaseFile.delete()) { throw new IOException("Failed to overwrite Curly base " + newCurlyBaseFile.getAbsolutePath()); } } if (!new File(curlyBase.getPath()).renameTo(newCurlyBaseFile)) { throw new IOException("Failed to move Curly base " + curlyBase.getPath() + " to " + newCurlyBasePath); } // Determine delta files from versions List<String> curlyDeltaRemoteFiles = new ArrayList<String>(); for (DomainVersion curlyDeltaVersion : updatePlan.getDeltasOrdered()) { curlyDeltaRemoteFiles.add(Curly.getName(curlyDeltaVersion)); } // Merge the Curly delta files into the base HankTimer timer = new HankTimer(); long[] offsetAdjustments = curlyMerger.merge(newCurlyBasePath, curlyDeltaRemoteFiles, partitionRemoteFileOps); long curlyTimeMs = timer.getDurationMs(); // Run Cueball update timer.restart(); CueballPartitionUpdater.runUpdateCore( currentVersion, updatingToVersion, updatePlan, updateWorkRoot, localPartitionRoot, localPartitionRootCache, cueballMerger, keyHashSize, valueSize, hashIndexBits, compressionCodec, new OffsetTransformer(offsetNumBytes, offsetAdjustments), statistics); long cueballTimeMs = timer.getDurationMs(); statistics.getDurationsMs().put("Curly merge", curlyTimeMs); LOG.info("Update in " + updateWorkRoot + " to " + updatingToVersion + ": merged Curly deltas in " + FormatUtils.formatSecondsDuration(curlyTimeMs / 1000) + ", ran Cueball update in " + FormatUtils.formatSecondsDuration(cueballTimeMs / 1000)); } }