/** * Commit all changes and persist them to disk. This method does nothing if there are no unsaved * changes, otherwise it increments the current version and stores the data (for file based * storages). * * <p>At most one storage operation may run at any time. * * @return the new version (incremented if there were changes) */ private synchronized long commitAndSave() { if (closed) { return currentVersion; } if (map.isInMemory()) { throw DataUtils.newIllegalStateException( DataUtils.ERROR_WRITING_FAILED, "This is an in-memory storage"); } if (map.isReadOnly()) { throw DataUtils.newIllegalStateException( DataUtils.ERROR_WRITING_FAILED, "This storage is read-only"); } if (!hasUnsavedChanges()) { return currentVersion; } try { currentStoreVersion = currentVersion; return save(); } catch (IllegalStateException e) { panic(e); return -1; } finally { currentStoreVersion = -1; } }
/** * Get the oldest version to retain in memory, which is the manually set retain version, or the * current store version (whatever is older). * * @return the version */ long getOldestVersionToKeep() { long v = currentVersion; if (map.isInMemory()) { return v - versionsToKeep; } long storeVersion = currentStoreVersion; if (storeVersion > -1) { v = Math.min(v, storeVersion); } return v; }
/** Close the file and the storage. Unsaved changes are written to disk first. */ void close() { if (closed) { return; } if (!map.isInMemory()) { if (hasUnsavedChanges()) { commitAndSave(); } } closeStorage(); }
private void closeStorage() { if (closed) { return; } closed = true; if (map.isInMemory()) { return; } synchronized (this) { for (BTreeChunk c : chunks.values()) { if (c.fileStorage != null) c.fileStorage.close(); } // release memory early - this is important when called // because of out of memory if (cache != null) cache.clear(); chunks.clear(); } }
/** * Rename a map. * * @param map the map * @param newName the new name */ public synchronized void renameMap(BTreeMap<?, ?> map, String newName) { // TODO if (map.isInMemory()) return; checkOpen(); String oldName = map.getName(); if (oldName.equals(newName)) { return; } String fileName = (String) map.config.get("storageName"); if (fileName != null) { fileName = fileName + File.separator + newName; if (!FileUtils.exists(fileName)) FileUtils.createDirectories(fileName); close(); FileUtils.move(btreeStorageName, fileName); // btreeStorageName = fileName; } }
/** * Commit the changes. * * <p>For in-memory storages, this method increments the version. * * <p>For persistent storages, it also writes changes to disk. It does nothing if there are no * unsaved changes, and returns the old version. It is not necessary to call this method when * auto-commit is enabled (the default setting), as in this case it is automatically called from * time to time or when enough changes have accumulated. However, it may still be called to flush * all changes to disk. * * @return the new version */ public synchronized long commit() { if (map.isInMemory()) { return ++currentVersion; } return commitAndSave(); }
/** * Create and open the storage. * * @param map the map to use * @throws IllegalStateException if the file is corrupt, or an exception occurred while opening * @throws IllegalArgumentException if the directory does not exist */ protected BTreeStorage(BTreeMap<Object, Object> map) { this.map = map; btreeStorageName = map.getBTreeStorageName(); Map<String, Object> config = map.config; Object value = config.get("retentionTime"); retentionTime = value == null ? 45000 : (Long) value; value = config.get("versionsToKeep"); versionsToKeep = value == null ? 5 : (Integer) value; reuseSpace = config.containsKey("reuseSpace"); value = config.get("pageSplitSize"); pageSplitSize = value != null ? (Integer) value : (map.isInMemory() ? 4 * 1024 : 16 * 1024); backgroundExceptionHandler = (UncaughtExceptionHandler) config.get("backgroundExceptionHandler"); if (map.isInMemory()) { cache = null; compressionLevel = 0; autoCompactFillRate = 0; autoCommitMemory = 0; createVersion = 0; creationTime = getTimeAbsolute(); return; } value = config.get("cacheSize"); int mb = value == null ? 16 : (Integer) value; if (mb > 0) { CacheLongKeyLIRS.Config cc = new CacheLongKeyLIRS.Config(); cc.maxMemory = mb * 1024L * 1024L; cache = new CacheLongKeyLIRS<BTreePage>(cc); } else { cache = null; } value = config.get("compress"); compressionLevel = value == null ? 0 : (Integer) value; value = config.get("autoCompactFillRate"); autoCompactFillRate = value == null ? 50 : (Integer) value; value = config.get("autoCommitBufferSize"); int kb = value == null ? 1024 : (Integer) value; // 19 KB memory is about 1 KB storage autoCommitMemory = kb * 1024 * 19; lastChunkId = 0; long createVersion = Long.MAX_VALUE; if (!FileUtils.exists(btreeStorageName)) FileUtils.createDirectories(btreeStorageName); String[] files = new File(btreeStorageName).list(); if (files != null && files.length > 0) { for (String f : files) { int id = Integer.parseInt(f.substring(0, f.length() - AOStorage.SUFFIX_AO_FILE_LENGTH)); if (id > lastChunkId) lastChunkId = id; if (id < createVersion) createVersion = id; } } if (createVersion == Long.MAX_VALUE) createVersion = 0; this.createVersion = createVersion; try { if (lastChunkId > 0) readLastChunk(); } catch (IllegalStateException e) { panic(e); } if (lastChunk != null) creationTime = lastChunk.creationTime; else creationTime = getTimeAbsolute(); lastCommitTime = getTimeSinceCreation(); }