package com.jivesoftware.os.amzabot.deployable.bot; import com.jivesoftware.os.amzabot.deployable.AmzaBotService; import com.jivesoftware.os.amzabot.deployable.AmzaBotUtil; import com.jivesoftware.os.amzabot.deployable.AmzaKeyClearingHouse; import com.jivesoftware.os.amzabot.deployable.AmzaKeyClearingHousePool; import com.jivesoftware.os.mlogger.core.AtomicCounter; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.util.AbstractMap.SimpleEntry; import java.util.Map.Entry; import java.util.Random; public class AmzaBotCoalminer implements Runnable { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); private final Random RANDOM = new Random(); private final AmzaBotCoalmineConfig config; private final AmzaBotService service; private final AmzaKeyClearingHousePool amzaKeyClearingHousePool; AmzaBotCoalminer(AmzaBotCoalmineConfig config, AmzaBotService service, AmzaKeyClearingHousePool amzaKeyClearingHousePool) { this.config = config; this.service = service; this.amzaKeyClearingHousePool = amzaKeyClearingHousePool; } public void run() { try { long start = System.currentTimeMillis(); LOG.info("Generating a clearing house of {} canaries.", config.getCoalmineCapacity()); AmzaKeyClearingHouse amzaKeyClearingHouse = amzaKeyClearingHousePool.genAmzaKeyClearingHouse(config.getCoalmineCapacity()); LOG.info("Fill clearing house and partition."); { AtomicCounter seq = new AtomicCounter(); Entry<String, String> canary = amzaKeyClearingHouse.genRandomEntry( String.valueOf(seq.getValue()), config.getCanarySizeThreshold()); while (canary != null) { amzaKeyClearingHouse.set(canary.getKey(), canary.getValue()); service.set(canary.getKey(), canary.getValue()); LOG.debug("Mined canary {}:{}", canary.getKey(), AmzaBotUtil.truncVal(canary.getValue())); if (config.getHesitationMs() > 0) { Thread.sleep(RANDOM.nextInt(config.getHesitationMs())); } seq.inc(); canary = amzaKeyClearingHouse.genRandomEntry( String.valueOf(seq.getValue()), config.getCanarySizeThreshold()); } } amzaKeyClearingHouse.verifyKeyMap(service.getAll()); LOG.info("Drain clearing house and corresponding partition entries"); { Entry<String, Integer> canary = amzaKeyClearingHouse.popRandomEntry(); while (canary != null) { String value = service.delete(canary.getKey()); if (value == null) { amzaKeyClearingHouse.quarantineEntry(canary, null); LOG.error("Canary not found {}", canary.getKey()); } else if (value.hashCode() != canary.getValue()) { amzaKeyClearingHouse.quarantineEntry(canary, value.hashCode()); LOG.error("Canary value differs {}:{}:{}", canary.getKey(), canary.getValue(), AmzaBotUtil.truncVal(value)); } if (config.getHesitationMs() > 0) { Thread.sleep(RANDOM.nextInt(config.getHesitationMs())); } canary = amzaKeyClearingHouse.popRandomEntry(); } } LOG.info("Verify the partition is empty"); { for (Entry<String, String> canary : service.getAll().entrySet()) { amzaKeyClearingHouse.quarantineEntry( new SimpleEntry<>(canary.getKey(), canary.getValue().hashCode()), -1); LOG.error("Extra canary found {}:{}", canary.getKey(), AmzaBotUtil.truncVal(canary.getValue())); } } if (amzaKeyClearingHouse.getQuarantinedKeyMap().size() == 0) { LOG.info("No quarantined keys generated. Removing clean clearing house."); amzaKeyClearingHousePool.removeAmzaKeyClearingHouse(amzaKeyClearingHouse); } LOG.info("Coalmine test completed in {}ms", System.currentTimeMillis() - start); } catch (Exception e) { LOG.error("Error occurred mining coal", e); } } }