GP-4341 Force retained checkout if file is in-use during checkin or add-to-version-control. Deprecated upgrade concept during checkin. Revised manner in which file open for update is updated following a version control operation (perform DBHandle update).

This commit is contained in:
ghidra1 2024-03-20 17:43:49 -04:00
parent 74a5b6f0e1
commit 2dff876f0f
46 changed files with 695 additions and 852 deletions

View file

@ -18,7 +18,6 @@ package db;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import db.buffers.*;
import db.util.ErrorHandler;
@ -439,8 +438,7 @@ public class DBHandle {
* @throws IllegalStateException if transaction is already active or this {@link DBHandle} has
* already been closed.
*/
public Transaction openTransaction(ErrorHandler errorHandler)
throws IllegalStateException {
public Transaction openTransaction(ErrorHandler errorHandler) throws IllegalStateException {
return new Transaction() {
long txId = startTransaction();
@ -542,6 +540,33 @@ public class DBHandle {
return (bufferMgr != null && !bufferMgr.atCheckpoint());
}
/**
* Set the DB source buffer file with a newer local buffer file version.
* Intended for use following a merge or commit operation only where a local checkout has been
* retained.
* @param versionedSourceBufferFile updated local DB source buffer file opened for versioning
* update (NOTE: file itself is read-only). File must be an instance of
* {@link LocalManagedBufferFile}.
* @throws IOException if an IO error occurs
*/
public void setDBVersionedSourceFile(BufferFile versionedSourceBufferFile) throws IOException {
if (!(versionedSourceBufferFile instanceof LocalManagedBufferFile bf) ||
!versionedSourceBufferFile.isReadOnly()) {
throw new IllegalArgumentException(
"Requires local versioned buffer file opened for versioning update");
}
synchronized (this) {
if (isTransactionActive()) {
throw new IOException("transaction is active");
}
bufferMgr.clearRecoveryFiles();
bufferMgr.setDBVersionedSourceFile(bf);
++checkpointNum;
reloadTables();
}
notifyDbRestored();
}
/**
* Terminate current transaction. If commit is false a rollback may occur followed by
* {@link DBListener#dbRestored(DBHandle)} notification to listeners. This method is very
@ -1022,10 +1047,9 @@ public class DBHandle {
public Table[] getTables() {
Table[] t = new Table[tables.size()];
Iterator<Table> it = tables.values().iterator();
int i = 0;
while (it.hasNext()) {
t[i++] = it.next();
for (Table element : tables.values()) {
t[i++] = element;
}
return t;
}
@ -1050,8 +1074,7 @@ public class DBHandle {
* @return new table instance
* @throws IOException if IO error occurs during table creation
*/
public Table createTable(String name, Schema schema, int[] indexedColumns)
throws IOException {
public Table createTable(String name, Schema schema, int[] indexedColumns) throws IOException {
Table table;
synchronized (this) {
if (tables.containsKey(name)) {

View file

@ -223,6 +223,22 @@ public class BufferMgr {
approxCacheSize < MINIMUM_CACHE_SIZE ? MINIMUM_CACHE_SIZE : approxCacheSize;
maxCacheSize = (int) (approxCacheSize / bufferSize);
// Setup baseline - checkpoint 0
startCheckpoint();
baselineCheckpointHead = currentCheckpointHead;
currentCheckpointHead = null;
initializeCache();
addInstance(this);
}
private void initializeCache() throws IOException {
if (cacheFile != null) {
cacheFile.delete();
}
// Setup memory cache list
cacheHead = new BufferNode(HEAD, -1);
cacheTail = new BufferNode(TAIL, -1);
@ -234,11 +250,6 @@ public class BufferMgr {
cacheIndexProvider = new IndexProvider();
// Setup baseline - checkpoint 0
startCheckpoint();
baselineCheckpointHead = currentCheckpointHead;
currentCheckpointHead = null;
// Copy file parameters into cache file
if (sourceFile != null) {
String[] parmNames = sourceFile.getParameterNames();
@ -247,8 +258,6 @@ public class BufferMgr {
}
}
addInstance(this);
if (alwaysPreCache) {
startPreCacheIfNeeded();
}
@ -417,9 +426,7 @@ public class BufferMgr {
// Dispose all buffer nodes - speeds up garbage collection
if (checkpointHeads != null) {
Iterator<BufferNode> iter = checkpointHeads.iterator();
while (iter.hasNext()) {
BufferNode node = iter.next();
for (BufferNode node : checkpointHeads) {
while (node != null) {
BufferNode next = node.nextInCheckpoint;
node.buffer = null;
@ -435,9 +442,7 @@ public class BufferMgr {
checkpointHeads = null;
}
if (redoCheckpointHeads != null) {
Iterator<BufferNode> iter = redoCheckpointHeads.iterator();
while (iter.hasNext()) {
BufferNode node = iter.next();
for (BufferNode node : redoCheckpointHeads) {
while (node != null) {
BufferNode next = node.nextInCheckpoint;
node.buffer = null;
@ -702,24 +707,7 @@ public class BufferMgr {
return; // only pre-cache remote buffer files
}
synchronized (preCacheLock) {
preCacheThread = new Thread(() -> {
try {
preCacheSourceFile();
}
catch (InterruptedIOException e) {
// ignore
}
catch (IOException e) {
Msg.error(BufferMgr.this, "pre-cache failure: " + e.getMessage(), e);
}
finally {
synchronized (preCacheLock) {
preCacheStatus = PreCacheStatus.STOPPED;
preCacheThread = null;
preCacheLock.notifyAll();
}
}
});
preCacheThread = new Thread(() -> preCacheSourceFile());
preCacheThread.setName("Pre-Cache");
preCacheThread.setPriority(Thread.MIN_PRIORITY);
preCacheThread.start();
@ -731,23 +719,38 @@ public class BufferMgr {
* Pre-cache source file into cache file. This is intended to be run in a
* dedicated thread when the source file is remote.
*/
private void preCacheSourceFile() throws IOException {
if (!(sourceFile instanceof BufferFileAdapter)) {
throw new UnsupportedOperationException("unsupported use of preCacheSourceFile");
}
Msg.trace(BufferMgr.this, "Pre-cache started...");
int cacheCount = 0;
BufferFileAdapter sourceAdapter = (BufferFileAdapter) sourceFile;
try (InputBlockStream inputBlockStream = sourceAdapter.getInputBlockStream()) {
BufferFileBlock block;
while (!Thread.interrupted() && (block = inputBlockStream.readBlock()) != null) {
DataBuffer buf = LocalBufferFile.getDataBuffer(block);
if (buf != null && !buf.isEmpty() && preCacheBuffer(buf)) { // skip head block and empty blocks
++cacheCount;
}
private void preCacheSourceFile() {
try {
if (!(sourceFile instanceof BufferFileAdapter)) {
throw new UnsupportedOperationException("unsupported use of preCacheSourceFile");
}
Msg.trace(BufferMgr.this, "Pre-cache started...");
int cacheCount = 0;
BufferFileAdapter sourceAdapter = (BufferFileAdapter) sourceFile;
try (InputBlockStream inputBlockStream = sourceAdapter.getInputBlockStream()) {
BufferFileBlock block;
while (!Thread.interrupted() && (block = inputBlockStream.readBlock()) != null) {
DataBuffer buf = LocalBufferFile.getDataBuffer(block);
if (buf != null && !buf.isEmpty() && preCacheBuffer(buf)) { // skip head block and empty blocks
++cacheCount;
}
}
Msg.trace(BufferMgr.this, "Pre-cache added " + cacheCount + " of " +
sourceFile.getIndexCount() + " buffers to cache");
}
}
catch (InterruptedIOException e) {
// ignore
}
catch (IOException e) {
Msg.error(BufferMgr.this, "pre-cache failure: " + e.getMessage(), e);
}
finally {
synchronized (preCacheLock) {
preCacheStatus = PreCacheStatus.STOPPED;
preCacheThread = null;
preCacheLock.notifyAll();
}
Msg.trace(BufferMgr.this, "Pre-cache added " + cacheCount + " of " +
sourceFile.getIndexCount() + " buffers to cache");
}
}
@ -1744,6 +1747,51 @@ public class BufferMgr {
return false;
}
/**
* Set the source buffer file with a newer local buffer file version.
* Intended for use following a merge or commit operation only where a local checkout has been
* retained.
* @param versionedSourceBufferFile updated local source buffer file opened for versioning
* update (NOTE: file itself is read-only).
* @throws IOException if an IO error occurs
*/
public void setDBVersionedSourceFile(LocalManagedBufferFile versionedSourceBufferFile)
throws IOException {
synchronized (snapshotLock) {
synchronized (this) {
if (!(sourceFile instanceof LocalManagedBufferFile)) {
throw new UnsupportedOperationException(getClass().getSimpleName() +
".setDBSourceFile not allowed: " + sourceFile.getClass());
}
if (bufferSize != sourceFile.getBufferSize()) {
throw new IllegalArgumentException("Buffer size mismatch");
}
if (corruptedState) {
throw new IOException("Corrupted BufferMgr state");
}
if (lockCount != 0) {
throw new IOException("Attempted checkout update while buffers are locked");
}
stopPreCache();
clearCheckpoints();
doSetSourceFile(versionedSourceBufferFile);
// re-initialize cached file data
int cnt = sourceFile.getIndexCount();
indexProvider = new IndexProvider(cnt, sourceFile.getFreeIndexes());
bufferTable = new ObjectArray(cnt + INITIAL_BUFFER_TABLE_SIZE);
initializeCache();
}
}
}
/**
* Save the current set of buffers to a new version of the source buffer file.
* If the buffer manager was not instantiated with a source file an
@ -1825,7 +1873,7 @@ public class BufferMgr {
monitor.setCancelEnabled(oldCancelState & !monitor.isCancelled());
}
setSourceFile(outFile);
doSetSourceFile(outFile);
}
}
}
@ -1881,7 +1929,7 @@ public class BufferMgr {
monitor.setCancelEnabled(true);
}
if (associateWithNewFile) {
setSourceFile(outFile);
doSetSourceFile(outFile);
}
}
}
@ -1965,7 +2013,7 @@ public class BufferMgr {
}
}
private void setSourceFile(BufferFile newFile) {
private void doSetSourceFile(BufferFile newFile) {
// Close buffer file
if (sourceFile != null) {

View file

@ -157,7 +157,6 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
* <code>preSaveThread</code> corresponds to the PreSaveTask which creates the
* preSaveFile when this buffer file is updateable.
*/
//private Thread preSaveThread;
private PreSaveTask preSaveTask;
/**
@ -228,8 +227,8 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
setFreeIndexes(versionFileHandler.getFreeIndexList());
String[] names = versionFileHandler.getOldParameterNames();
clearParameters();
for (int i = 0; i < names.length; i++) {
setParameter(names[i], versionFileHandler.getOldParameter(names[i]));
for (String name : names) {
setParameter(name, versionFileHandler.getOldParameter(name));
}
}