package com.jivesoftware.os.amza.lab.pointers; import com.jivesoftware.os.amza.api.AmzaInterner; import com.jivesoftware.os.amza.api.AmzaVersionConstants; import com.jivesoftware.os.amza.api.partition.VersionedPartitionName; import com.jivesoftware.os.amza.api.wal.WALIndexProvider; import com.jivesoftware.os.amza.lab.pointers.LABPointerIndexWALIndexName.Type; import com.jivesoftware.os.jive.utils.collections.bah.LRUConcurrentBAHLinkedHash; import com.jivesoftware.os.lab.LABEnvironment; import com.jivesoftware.os.lab.LABStats; import com.jivesoftware.os.lab.LabHeapPressure; import com.jivesoftware.os.lab.guts.Leaps; import com.jivesoftware.os.lab.guts.StripingBolBufferLocks; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.io.File; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicLong; /** * */ public class LABPointerIndexWALIndexProvider implements WALIndexProvider<LABPointerIndexWALIndex> { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); public static final String INDEX_CLASS_NAME = "lab"; private final AmzaInterner amzaInterner; private final String name; private final LABEnvironment[] environments; private final LABPointerIndexConfig config; private final LRUConcurrentBAHLinkedHash<Leaps> leapCache; public LABPointerIndexWALIndexProvider(AmzaInterner amzaInterner, LABPointerIndexConfig config, ExecutorService heapThreadPool, ExecutorService schedulerThreadPool, ExecutorService compactorThreadPool, ExecutorService destroyThreadPool, String name, int numberOfStripes, File[] baseDirs) throws Exception { this.amzaInterner = amzaInterner; this.config = config; this.name = name; this.environments = new LABEnvironment[numberOfStripes]; LABStats labStats = new LABStats(); // grr LabHeapPressure labHeapPressure = new LabHeapPressure(labStats, heapThreadPool, config.getHeapPressureName(), config.getGlobalMaxHeapPressureInBytes(), config.getGlobalBlockOnHeapPressureInBytes(), new AtomicLong(), LabHeapPressure.FreeHeapStrategy.mostBytesFirst); this.leapCache = LABEnvironment.buildLeapsCache((int) config.getLeapCacheMaxCapacity(), config.getConcurrency()); for (int i = 0; i < environments.length; i++) { File active = new File( new File( new File(baseDirs[i % baseDirs.length], AmzaVersionConstants.LATEST_VERSION), INDEX_CLASS_NAME), String.valueOf(i)); if (!active.exists() && !active.mkdirs()) { throw new RuntimeException("Failed while trying to mkdirs for " + active); } this.environments[i] = new LABEnvironment(labStats, schedulerThreadPool, compactorThreadPool, destroyThreadPool, null, active, labHeapPressure, config.getMinMergeDebt(), config.getMaxMergeDebt(), leapCache, new StripingBolBufferLocks(1024), false, true); File[] files = active.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { File dest = convertBase64toPartitionVersion(file); if (dest != null && !file.renameTo(dest)) { throw new IOException("Failed rename of " + file + " to " + dest); } } } } files = active.listFiles(); if (files != null) { for (File file : files) { String[] split = file.getName().split("-"); if (split.length == 3) { try { long partitionVersion = Long.parseLong(split[2]); long h = hash(partitionVersion); File parent = new File(active, String.valueOf(h % 1024)); if (!parent.mkdirs() && !parent.exists()) { throw new IOException("Failed to mkdirs for " + parent); } File dest = new File(parent, file.getName()); if (!file.renameTo(dest)) { throw new IOException("Failed to move " + file + " to " + dest); } LOG.info("We hash repaired {} to {}", file, dest); } catch (NumberFormatException e) { LOG.info("Skipped repair for " + file); } } } } } } @Override public String getName() { return name; } @Override public void start() { leapCache.start(name, config.getLeapCacheCleanupIntervalInMillis(), new LRUConcurrentBAHLinkedHash.CleanerExceptionCallback() { @Override public boolean exception(Throwable thrwbl) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } }); } @Override public void stop() { leapCache.stop(); } @Override public LABPointerIndexWALIndex createIndex(VersionedPartitionName versionedPartitionName, int maxValueSizeInIndex, int stripe) throws Exception { int modulo = (int) (hash(versionedPartitionName.getPartitionVersion()) % 1024); LABPointerIndexWALIndexName indexName = new LABPointerIndexWALIndexName(modulo, Type.active, String.valueOf(versionedPartitionName.getPartitionVersion())); //TODO config flush interval return new LABPointerIndexWALIndex(name, maxValueSizeInIndex, versionedPartitionName, environments, stripe, indexName, config); } @Override public void deleteIndex(VersionedPartitionName versionedPartitionName, int stripe) throws Exception { int modulo = (int) (hash(versionedPartitionName.getPartitionVersion()) % 1024); LABPointerIndexWALIndexName name = new LABPointerIndexWALIndexName(modulo, LABPointerIndexWALIndexName.Type.active, String.valueOf(versionedPartitionName.getPartitionVersion())); LABEnvironment env = environments[stripe]; for (LABPointerIndexWALIndexName n : name.all()) { env.remove(n.getPrimaryName(), true); LOG.info("Removed database: {}", n.getPrimaryName()); env.remove(n.getPrefixName(), true); LOG.info("Removed database: {}", n.getPrefixName()); } } @Override public void flush(Iterable<LABPointerIndexWALIndex> indexes, boolean fsync) throws Exception { for (LABPointerIndexWALIndex index : indexes) { index.flush(fsync); // So call me maybe? } } private final static long randMult = 0x5DEECE66DL; private final static long randAdd = 0xBL; private final static long randMask = (1L << 48) - 1; private static long hash(long partitionVersion) { long x = (partitionVersion * randMult + randAdd) & randMask; long h = Math.abs(x >>> (16)); if (h >= 0) { return h; } else { return Long.MAX_VALUE; } } private File convertBase64toPartitionVersion(File file) throws Exception { String filename = file.getName(); int firstHyphen = filename.indexOf('-'); int secondHyphen = filename.indexOf('-', firstHyphen + 1); if (firstHyphen != -1 && secondHyphen != -1) { String base = filename.substring(0, secondHyphen); String partition = filename.substring(secondHyphen + 1); try { long partitionVersion = Long.parseLong(partition); LOG.info("Did not repair partition version {}", partitionVersion); } catch (NumberFormatException e) { VersionedPartitionName vpn = amzaInterner.internVersionedPartitionNameBase64(partition); LOG.info("We will repair partition {}", vpn); return new File(file.getParent(), base + "-" + String.valueOf(vpn.getPartitionVersion())); } } return null; } }