@Override protected void startInternal() throws Exception { Path storeRoot = createStorageDir(); Options options = new Options(); options.createIfMissing(false); options.logger(new LeveldbLogger()); LOG.info("Using state database at " + storeRoot + " for recovery"); File dbfile = new File(storeRoot.toString()); try { db = JniDBFactory.factory.open(dbfile, options); } catch (NativeDB.DBException e) { if (e.isNotFound() || e.getMessage().contains(" does not exist ")) { LOG.info("Creating state database at " + dbfile); options.createIfMissing(true); try { db = JniDBFactory.factory.open(dbfile, options); // store version storeVersion(); } catch (DBException dbErr) { throw new IOException(dbErr.getMessage(), dbErr); } } else { throw e; } } }
public DBase getDb(String name) throws IOException { if (!name.matches("[a-zA-Z]{1,32}")) { // $NON-NLS-1$ throw new IllegalArgumentException("Invalid database name."); } Options options = new Options(); options.createIfMissing(true); return new DBase(factory.open(new File(this.baseDir, "sc_" + name + ".db"), options), name); }
// Allows tests to have more control over when directories are cleaned up. @VisibleForTesting ExternalShuffleBlockResolver( TransportConf conf, File registeredExecutorFile, Executor directoryCleaner) throws IOException { this.conf = conf; this.registeredExecutorFile = registeredExecutorFile; if (registeredExecutorFile != null) { Options options = new Options(); options.createIfMissing(false); options.logger(new LevelDBLogger()); DB tmpDb; try { tmpDb = JniDBFactory.factory.open(registeredExecutorFile, options); } catch (NativeDB.DBException e) { if (e.isNotFound() || e.getMessage().contains(" does not exist ")) { logger.info("Creating state database at " + registeredExecutorFile); options.createIfMissing(true); try { tmpDb = JniDBFactory.factory.open(registeredExecutorFile, options); } catch (NativeDB.DBException dbExc) { throw new IOException("Unable to create state store", dbExc); } } else { // the leveldb file seems to be corrupt somehow. Lets just blow it away and create a new // one, so we can keep processing new apps logger.error( "error opening leveldb file {}. Creating new file, will not be able to " + "recover state for existing applications", registeredExecutorFile, e); if (registeredExecutorFile.isDirectory()) { for (File f : registeredExecutorFile.listFiles()) { if (!f.delete()) { logger.warn("error deleting {}", f.getPath()); } } } if (!registeredExecutorFile.delete()) { logger.warn("error deleting {}", registeredExecutorFile.getPath()); } options.createIfMissing(true); try { tmpDb = JniDBFactory.factory.open(registeredExecutorFile, options); } catch (NativeDB.DBException dbExc) { throw new IOException("Unable to create state store", dbExc); } } } // if there is a version mismatch, we throw an exception, which means the service is unusable checkVersion(tmpDb); executors = reloadRegisteredExecutors(tmpDb); db = tmpDb; } else { db = null; executors = Maps.newConcurrentMap(); } this.directoryCleaner = directoryCleaner; }
public LevelDBPersistence(String root, Map options, boolean readOnly, boolean allowCreate) throws IOException { this.readOnly = readOnly; new File(root).mkdirs(); dboptions = new Options(); dboptions.createIfMissing(allowCreate); dboptions.paranoidChecks(true); // 20mb TODO: set this using the options map dboptions.cacheSize(20 * 1024 * 1024); dboptions.compressionType(CompressionType.SNAPPY); db = factory.open(new File(root), dboptions); }
private long recoverLogFile(long fileNumber, VersionEdit edit) throws IOException { Preconditions.checkState(mutex.isHeldByCurrentThread()); File file = new File(databaseDir, Filename.logFileName(fileNumber)); FileChannel channel = new FileInputStream(file).getChannel(); try { LogMonitor logMonitor = LogMonitors.logMonitor(); LogReader logReader = new LogReader(channel, logMonitor, true, 0); // Log(options_.info_log, "Recovering log #%llu", (unsigned long long) log_number); // Read all the records and add to a memtable long maxSequence = 0; MemTable memTable = null; for (Slice record = logReader.readRecord(); record != null; record = logReader.readRecord()) { SliceInput sliceInput = record.input(); // read header if (sliceInput.available() < 12) { logMonitor.corruption(sliceInput.available(), "log record too small"); continue; } long sequenceBegin = sliceInput.readLong(); int updateSize = sliceInput.readInt(); // read entries WriteBatchImpl writeBatch = readWriteBatch(sliceInput, updateSize); // apply entries to memTable if (memTable == null) { memTable = new MemTable(internalKeyComparator); } writeBatch.forEach(new InsertIntoHandler(memTable, sequenceBegin)); // update the maxSequence long lastSequence = sequenceBegin + updateSize - 1; if (lastSequence > maxSequence) { maxSequence = lastSequence; } // flush mem table if necessary if (memTable.approximateMemoryUsage() > options.writeBufferSize()) { writeLevel0Table(memTable, edit, null); memTable = null; } } // flush mem table if (memTable != null && !memTable.isEmpty()) { writeLevel0Table(memTable, edit, null); } return maxSequence; } finally { channel.close(); } }
private void makeRoomForWrite(boolean force) { Preconditions.checkState(mutex.isHeldByCurrentThread()); boolean allowDelay = !force; while (true) { // todo background processing system need work // if (!bg_error_.ok()) { // // Yield previous error // s = bg_error_; // break; // } else if (allowDelay && versions.numberOfFilesInLevel(0) > L0_SLOWDOWN_WRITES_TRIGGER) { // We are getting close to hitting a hard limit on the number of // L0 files. Rather than delaying a single write by several // seconds when we hit the hard limit, start delaying each // individual write by 1ms to reduce latency variance. Also, // this delay hands over some CPU to the compaction thread in // case it is sharing the same core as the writer. try { mutex.unlock(); Thread.sleep(1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } finally { mutex.lock(); } // Do not delay a single write more than once allowDelay = false; } else if (!force && memTable.approximateMemoryUsage() <= options.writeBufferSize()) { // There is room in current memtable break; } else if (immutableMemTable != null) { // We have filled up the current memtable, but the previous // one is still being compacted, so we wait. backgroundCondition.awaitUninterruptibly(); } else if (versions.numberOfFilesInLevel(0) >= L0_STOP_WRITES_TRIGGER) { // There are too many level-0 files. // Log(options_.info_log, "waiting...\n"); backgroundCondition.awaitUninterruptibly(); } else { // Attempt to switch to a new memtable and trigger compaction of old Preconditions.checkState(versions.getPrevLogNumber() == 0); // close the existing log try { log.close(); } catch (IOException e) { throw new RuntimeException("Unable to close log file " + log.getFile(), e); } // open a new log long logNumber = versions.getNextFileNumber(); try { this.log = Logs.createLogWriter( new File(databaseDir, Filename.logFileName(logNumber)), logNumber); } catch (IOException e) { throw new RuntimeException( "Unable to open new log file " + new File(databaseDir, Filename.logFileName(logNumber)).getAbsoluteFile(), e); } // create a new mem table immutableMemTable = memTable; memTable = new MemTable(internalKeyComparator); // Do not force another compaction there is space available force = false; maybeScheduleCompaction(); } } }
public DbImpl(Options options, File databaseDir) throws IOException { Preconditions.checkNotNull(options, "options is null"); Preconditions.checkNotNull(databaseDir, "databaseDir is null"); this.options = options; if (this.options.compressionType() == CompressionType.ZLIB && !Zlib.available()) { // There's little hope to continue. this.options.compressionType(CompressionType.NONE); } if (this.options.compressionType() == CompressionType.SNAPPY && !Snappy.available()) { // Disable snappy if it's not available. this.options.compressionType(CompressionType.NONE); } this.databaseDir = databaseDir; // use custom comparator if set DBComparator comparator = options.comparator(); UserComparator userComparator; if (comparator != null) { userComparator = new CustomUserComparator(comparator); } else { userComparator = new BytewiseComparator(); } internalKeyComparator = new InternalKeyComparator(userComparator); memTable = new MemTable(internalKeyComparator); immutableMemTable = null; ThreadFactory compactionThreadFactory = new ThreadFactoryBuilder() .setNameFormat("leveldb-compaction-%s") .setUncaughtExceptionHandler( new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { // todo need a real UncaughtExceptionHandler System.out.printf("%s%n", t); e.printStackTrace(); } }) .build(); compactionExecutor = Executors.newSingleThreadExecutor(compactionThreadFactory); // Reserve ten files or so for other uses and give the rest to TableCache. int tableCacheSize = options.maxOpenFiles() - 10; tableCache = new TableCache( databaseDir, tableCacheSize, new InternalUserComparator(internalKeyComparator), options.verifyChecksums()); // create the version set // create the database dir if it does not already exist databaseDir.mkdirs(); Preconditions.checkArgument( databaseDir.exists(), "Database directory '%s' does not exist and could not be created", databaseDir); Preconditions.checkArgument( databaseDir.isDirectory(), "Database directory '%s' is not a directory", databaseDir); mutex.lock(); try { // lock the database dir dbLock = new DbLock(new File(databaseDir, Filename.lockFileName())); // verify the "current" file File currentFile = new File(databaseDir, Filename.currentFileName()); if (!currentFile.canRead()) { Preconditions.checkArgument( options.createIfMissing(), "Database '%s' does not exist and the create if missing option is disabled", databaseDir); } else { Preconditions.checkArgument( !options.errorIfExists(), "Database '%s' exists and the error if exists option is enabled", databaseDir); } versions = new VersionSet(databaseDir, tableCache, internalKeyComparator); // load (and recover) current version versions.recover(); // Recover from all newer log files than the ones named in the // descriptor (new log files may have been added by the previous // incarnation without registering them in the descriptor). // // Note that PrevLogNumber() is no longer used, but we pay // attention to it in case we are recovering a database // produced by an older version of leveldb. long minLogNumber = versions.getLogNumber(); long previousLogNumber = versions.getPrevLogNumber(); List<File> filenames = Filename.listFiles(databaseDir); List<Long> logs = Lists.newArrayList(); for (File filename : filenames) { FileInfo fileInfo = Filename.parseFileName(filename); if (fileInfo != null && fileInfo.getFileType() == FileType.LOG && ((fileInfo.getFileNumber() >= minLogNumber) || (fileInfo.getFileNumber() == previousLogNumber))) { logs.add(fileInfo.getFileNumber()); } } // Recover in the order in which the logs were generated VersionEdit edit = new VersionEdit(); Collections.sort(logs); for (Long fileNumber : logs) { long maxSequence = recoverLogFile(fileNumber, edit); if (versions.getLastSequence() < maxSequence) { versions.setLastSequence(maxSequence); } } // open transaction log long logFileNumber = versions.getNextFileNumber(); this.log = Logs.createLogWriter( new File(databaseDir, Filename.logFileName(logFileNumber)), logFileNumber); edit.setLogNumber(log.getFileNumber()); // apply recovered edits versions.logAndApply(edit); // cleanup unused files deleteObsoleteFiles(); // schedule compactions maybeScheduleCompaction(); } finally { mutex.unlock(); } }