/* * Copyright 2013 LinkedIn, 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 voldemort.utils; import java.util.List; import java.util.concurrent.BlockingQueue; import org.apache.log4j.Logger; import voldemort.VoldemortException; import voldemort.client.protocol.admin.QueryKeyResult; import voldemort.store.routed.NodeValue; import voldemort.utils.ConsistencyFix.BadKey; import voldemort.utils.ConsistencyFix.BadKeyStatus; import voldemort.utils.ConsistencyFix.Status; import voldemort.versioning.ObsoleteVersionException; class ConsistencyFixWorker extends AbstractConsistencyFixer implements Runnable { private static final Logger logger = Logger.getLogger(ConsistencyFixWorker.class); private final ConsistencyFix consistencyFix; private final BlockingQueue<BadKeyStatus> badKeyQOut; /** * Normal use case constructor. * * @param keyInHexFormat * @param consistencyFix * @param badKeyQOut */ ConsistencyFixWorker(BadKey badKey, ConsistencyFix consistencyFix, BlockingQueue<BadKeyStatus> badKeyQOut) { this(badKey, consistencyFix, badKeyQOut, null); } /** * Constructor for "orphaned values" use case. I.e., there are values for * the specific key that exist somewhere and may need to be written to the * nodes which actually host the key. * * @param keyInHexFormat * @param consistencyFix * @param badKeyQOut * @param orphanedValues Set to null if no orphaned values to be included. */ ConsistencyFixWorker(BadKey badKey, ConsistencyFix consistencyFix, BlockingQueue<BadKeyStatus> badKeyQOut, QueryKeyResult orphanedValues) { super(badKey, consistencyFix.getStoreInstance(), consistencyFix.getAdminClient(), orphanedValues); this.consistencyFix = consistencyFix; this.badKeyQOut = badKeyQOut; } private String myName() { return Thread.currentThread().getName() + "-" + ConsistencyFixWorker.class.getName(); } @Override public void run() { logger.trace("About to process key " + badKey + " (" + myName() + ")"); Status status = doConsistencyFix(); logger.trace("Finished processing key " + badKey + " (" + myName() + ")"); consistencyFix.getStats().incrementFixCount(); if(status != Status.SUCCESS) { try { badKeyQOut.put(new BadKeyStatus(badKey, status)); } catch(InterruptedException ie) { logger.warn("Worker thread " + myName() + " interrupted."); } consistencyFix.getStats().incrementFailures(status); } } /** * * @param toReadRepair Effectively the output of this method. Must pass in a * non-null object to be populated by this method. * @return */ @Override public Status doRepairPut(final List<NodeValue<ByteArray, byte[]>> toReadRepair) { if(this.consistencyFix.isDryRun()) { logger.debug("Returning success from ConsistencyFixWorker.doRepairPut because this is a dry run."); return Status.SUCCESS; } boolean allRepairsSuccessful = true; for(NodeValue<ByteArray, byte[]> nodeKeyValue: toReadRepair) { try { consistencyFix.maybePutThrottle(nodeKeyValue.getNodeId()); consistencyFix.getAdminClient().storeOps.putNodeKeyValue(consistencyFix.getStoreName(), nodeKeyValue); consistencyFix.getStats().incrementPutCount(); } catch(ObsoleteVersionException ove) { // Treat OVE as success. consistencyFix.getStats().incrementObsoleteVersionExceptions(); } catch(VoldemortException ve) { allRepairsSuccessful = false; logger.debug("Repair of key " + nodeKeyValue.getKey() + "on node with id " + nodeKeyValue.getNodeId() + " for version " + nodeKeyValue.getVersion() + " failed because of exception : " + ve.getMessage()); } } if(!allRepairsSuccessful) { logger.info("Aborting fixKey because exceptions were encountered when repairing key-values."); return Status.REPAIR_EXCEPTION; } return Status.SUCCESS; } }