@Override public void close() { commitLock.lock(); try { if (closed) { return; } if (hasUncommitedData()) { LOG.warning("Closing storage with uncommited data, this data will be discarded."); } headVol.putData(0, headVolBackup, 0, headVolBackup.length); if (!readonly) { replaySoft(); wal.destroyWalFiles(); } wal.close(); vol.close(); vol = null; headVol.close(); headVol = null; headVolBackup = null; uncommittedStackPages.clear(); if (caches != null) { for (Cache c : caches) { c.close(); } Arrays.fill(caches, null); } if (fileLockHeartbeat != null) { fileLockHeartbeat.unlock(); fileLockHeartbeat = null; } closed = true; } finally { commitLock.unlock(); } }
@Override public void commit() { commitLock.lock(); try { // flush write caches into write ahead log flushWriteCache(); // move uncommited data to committed for (int segment = 0; segment < locks.length; segment++) { locks[segment].writeLock().lock(); try { // dump index vals into WAL long[] table = uncommittedIndexTable[segment].table; for (int i = 0; i < table.length; ) { long offset = table[i++]; long val = table[i++]; if (offset == 0) continue; wal.walPutLong(offset, val); } moveAndClear(uncommittedIndexTable[segment], committedIndexTable[segment]); moveAndClear(uncommittedDataLongs[segment], committedDataLongs[segment]); } finally { locks[segment].writeLock().unlock(); } } structuralLock.lock(); try { // flush modified Long Stack pages into WAL long[] set = uncommittedStackPages.set; longStackPagesLoop: for (int i = 0; i < set.length; i++) { long offset = set[i]; if (offset == 0) continue longStackPagesLoop; byte[] val = (byte[]) uncommittedStackPages.values[i]; if (val == LONG_STACK_PAGE_TOMBSTONE) committedPageLongStack.put(offset, -1); else { if (CC.ASSERT) assertLongStackPage(offset, val); long walPointer = wal.walPutByteArray(offset, val, 0, val.length); committedPageLongStack.put(offset, walPointer); } } uncommittedStackPages.clear(); // update checksum headVol.putInt(HEAD_CHECKSUM, headChecksum(headVol)); // take backup of headVol headVol.getData(0, headVolBackup, 0, headVolBackup.length); wal.walPutByteArray(0, headVolBackup, 0, headVolBackup.length); wal.commit(); wal.seal(); replaySoft(); realVol.sync(); wal.destroyWalFiles(); } finally { structuralLock.unlock(); } } finally { commitLock.unlock(); } }
@Override public void initOpen() { // TODO disable readonly feature for this store realVol = vol; if (readonly && !Volume.isEmptyFile(fileName + ".wal.0")) throw new DBException.WrongConfig( "There is dirty WAL file, but storage is read-only. Can not replay file"); wal.open( new WriteAheadLog.WALReplay() { @Override public void beforeReplayStart() {} @Override public void writeLong(long offset, long value) { if (CC.ASSERT && offset % 8 != 0) throw new AssertionError(); realVol.ensureAvailable(Fun.roundUp(offset + 8, StoreDirect.PAGE_SIZE)); realVol.putLong(offset, value); } @Override public void writeRecord(long recid, long walId, Volume vol, long volOffset, int length) { throw new DBException.DataCorruption(); } @Override public void writeByteArray( long offset, long walId, Volume vol, long volOffset, int length) { if (CC.ASSERT && offset % 8 != 0) throw new AssertionError(); realVol.ensureAvailable(Fun.roundUp(offset + length, StoreDirect.PAGE_SIZE)); vol.transferInto(volOffset, realVol, offset, length); } @Override public void beforeDestroyWAL() {} @Override public void commit() {} @Override public void rollback() { throw new DBException.DataCorruption(); } @Override public void writeTombstone(long recid) { throw new DBException.DataCorruption(); } @Override public void writePreallocate(long recid) { throw new DBException.DataCorruption(); } }); realVol.sync(); wal.destroyWalFiles(); initOpenPost(); // TODO reenable this assertion // if(CC.PARANOID) // storeCheck(); }