private void TXN_BEGIN(string dbName, string tableName = null, object recordKey = null, bool block = false) { logger.Debug("TXN_BEGIN"); // PURPOSE: // Create a lock // Open/prepare shared datastore for all the future modify functions // verify block transaction status if (block && !blockTxn) { blockTxn = block; } // validate input // db might be null // make sure db is specified when locking a table if (tableName != null && dbName is null) { throw new Exception("DB must be specified when opening a table-level transaction."); } // make sure db:table is specified when locking a record if (recordKey != null && (tableName is null || dbName is null)) { throw new Exception("DB and Table must be specified when opening a record-level transaction."); } // write the lock ConnectionLock newLock = new ConnectionLock(ConnectionSource, ConnectionRequester, ConnectionId, dbName, tableName, recordKey); if (activeConnLock != null) { logger.Debug("Active lock exists: {0}", activeConnLock); ConnectionLock txnLock = DetermineHigherLock(activeConnLock, newLock); if (txnLock != activeConnLock) { activeConnLock = AcquireLocks(newLock); } logger.Debug("Using lock: {0}", activeConnLock); } else { activeConnLock = AcquireLocks(newLock); } // if no active datastore (means that this is the first BEGIN in a txn chain, // then set the active datastore if (activeDStore is null) { activeDStore = new DataStore(ActiveDataStoreType); } }
private void TXN_END(bool block = false) { logger.Debug("TXN_END"); // close the block if block is true (called by END) // if block is false, the TXN_END is being called from commands if (block) { blockTxn = false; } // if there is no active block txn, close and commit // otherwise skip writing changes until END block is requested if (!blockTxn) { // close only if there is an active dstore if (activeDStore != null) { try { // now close and commit the datastore activeDStore.Commit(); activeDStore.Dispose(); activeDStore = null; } catch (Exception commitEx) { throw new Exception( string.Format("Exception occured while trying to commit changes. | {0}", commitEx.Message)); } finally { // drop all new locks created by this connection ReleaseLocks(); activeConnLock = null; } } } }
private ConnectionLock DetermineHigherLock(ConnectionLock exstLock, ConnectionLock newLock) { ConnectionLock txnLock = newLock; // is existing lock restricting access? if (newLock.IsRestrictedBy(exstLock)) { // does restricting lock belong to this connection? // if yes and it is a higher-level lock, use that if (exstLock.BelongsTo(this) && exstLock.LockType <= newLock.LockType) { logger.Debug("Existing lock {0} is higher than new {1}", exstLock, newLock); txnLock = exstLock; } // does the lock not belong to this connection in which case reject // or is trying to acquire a higher level lock that active lock else { logger.Debug("New lock {0} is restricted by {1}", newLock, exstLock); throw new AccessRestrictedException(newLock, exstLock); } } // if not restricting, does this connection already have a txn block lock of equal level // reject again since the connection need to close its block lock first and then // work on another entry else { if (exstLock.BelongsTo(this)) { logger.Debug("New lock {0} not on the same path as existing {1}", newLock, exstLock); throw new AccessRestrictedByExistingLockException(newLock, exstLock); } } return(txnLock); }
private ConnectionLock AcquireLocks(ConnectionLock newLock) { logger.Debug("RECORD LOCK"); ConnectionLock txnLock = newLock; // open datastore for exclusive access using (var dstore = new DataStore(ActiveDataStoreType, exclusive: true)) { // get all existing locks // check access, find the highest-level compatible lock logger.Debug("Reading existing locks"); foreach (ConnectionLock activeLock in ReadActiveLocks(dstore)) { logger.Debug("Lock Found: {0}", activeLock); txnLock = DetermineHigherLock(activeLock, newLock); } logger.Debug("Using lock: {0}", txnLock); // at this point, the are no locks that restrict access // but we might be re-using an active compatible lock // so only write the lock if is not existing compatible if (txnLock == newLock) { try { // register the new lock: if target is not locked, lock it DataFunctions.InsertRecord( dstore, connDbName, connDbLocksTableName, newLock.LockId, new Dictionary <string, object>() { { connDbLockSourceField, newLock.LockSource }, { connDbLockRequesterField, newLock.LockRequester }, { connDbLockConnUUIDField, newLock.LockConnId }, { connDbLockDBField, newLock.LockTargetDB }, { connDbLockTableField, newLock.LockTargetTable }, { connDbLockRecordKeyField, newLock.LockTargetRecordKey }, }); } catch (Exception lockEx) { // finally statement closes the dstore and release the exclusivity if throwing an exception throw new Exception( string.Format("Error creating lock: {0} | {1}", newLock, lockEx.Message) ); } finally { try { // write changes dstore.Commit(); } catch (Exception commitEx) { // finally statement closes the dstore and release the exclusivity if throwing an exception throw new Exception( string.Format("Exception occured when commiting new locks. | {0}", commitEx.Message) ); } } } } return(txnLock); }
public void END(ConnectionLock cLock = null) { // user ends a transaction block TXN_END(block: true); }
public bool IsRestrictedBy(ConnectionLock otherLock) { // this method checks two-way locking conflicts // db:table conflicts with db:table:record both ways. // because if a connection has a lock on db:table:record, another connection should not // be able to lock its parent db:table // also if a connection has a lock on db:table, another connection should not // be able to lock its child db:table:record // checking for master datastore locks first // if any lock is a datastore lock, all other locks are in conflict if (IsDataStoreLock || otherLock.IsDataStoreLock) { logger.Debug("At least one lock is a datastore lock."); return(true); } // checking db:table:record locks if (LockType == otherLock.LockType) { logger.Debug("Lock types are equal = {0}", LockType); // if lock types are equal // check if we have equal lock targets switch (LockType) { case ConnectionLockType.DB: if (otherLock.LockTargetDB == LockTargetDB) { return(true); } break; case ConnectionLockType.Table: if (otherLock.LockTargetDB == LockTargetDB && otherLock.LockTargetTable == LockTargetTable) { return(true); } break; case ConnectionLockType.Record: if (otherLock.LockTargetDB == LockTargetDB && otherLock.LockTargetTable == LockTargetTable && otherLock.LockTargetRecordKey.Equals(LockTargetRecordKey)) { return(true); } break; } } else if (LockType < otherLock.LockType) { logger.Debug("This lock is higher-level than other."); // if I have a higher-degree lock, test if I'm holding a lock on other lock targets if (LockType == ConnectionLockType.DB) { // Am I trying to lock other locks target db? if (otherLock.LockTargetDB == LockTargetDB) { logger.Debug("This lock is locking other locks DB."); return(true); } } else if (LockType == ConnectionLockType.Table) { // Am I trying to lock other locks target db:table? if (otherLock.LockTargetDB == LockTargetDB && otherLock.LockTargetTable == LockTargetTable) { logger.Debug("This lock is locking other locks DB:Table."); return(true); } } // there is nothing lower than the record lock and I'm higher-degree than other lock // so I'm definitely not record otherwise we would have been equal } else { logger.Debug("This lock is lower-level than other."); // if no condition above is met, then LockType > otherLock.LockType // if I have a lower-degree lock, test if other lock is trying to lock my targets if (otherLock.LockType == ConnectionLockType.DB) { // Is other lock trying to lock the db containing my table? if (otherLock.LockTargetDB == LockTargetDB) { logger.Debug("Other lock is locking this locks DB."); return(true); } } else if (otherLock.LockType == ConnectionLockType.Table) { // Is other lock trying to lock the db containing my table? if (otherLock.LockTargetDB == LockTargetDB && otherLock.LockTargetTable == LockTargetTable) { logger.Debug("Other lock is locking this locks DB:Table."); return(true); } } // there is nothing lower than the record lock and otherLock is higher-degree than me // so otherLock is definitely not record otherwise we would have been equal } return(false); }
public bool IsIdenticalTo(ConnectionLock connLock) { return(connLock.LockId == LockId && connLock.LockConnectionSignature == LockConnectionSignature && connLock.LockHierarchy == LockHierarchy); }
private void AcquireLocks(ConnectionLock newLock) { logger.Debug("RECORD LOCK"); // open datastore for exclusive access using (var dstore = new DataStore(ActiveDataStoreType, exclusive: true)) { // get all existing locks // check access, find the highest-level compatible lock ConnectionLock txnLock = newLock; foreach (ConnectionLock activeLock in ReadActiveLocks(dstore)) { logger.Debug("Lock Found: {0}", activeLock); // is existing lock restricting access? if (newLock.IsRestrictedBy(activeLock)) { // does restricting lock belong to this connection? // if yes and it is a higher-level lock, use that if (activeLock.BelongsTo(this) && activeLock.LockType <= newLock.LockType) { logger.Debug("Existing lock {0} is higher than new {1}", activeLock, newLock); txnLock = activeLock; } // does the lock not belong to this connection in which case reject // or is trying to acquire a higher level lock that active lock else { logger.Debug("New lock {0} is restricted by {1}", newLock, activeLock); throw new AccessRestrictedException(newLock, activeLock); } } // if not restricting, does this connection already have a txn block lock of equal level // reject again since the connection need to close its block lock first and then // work on another entry else { if (activeLock.BelongsTo(this)) { logger.Debug("New lock {0} not on the same path as existing {1}", newLock, activeLock); throw new AccessRestrictedByExistingLockException(newLock, activeLock); } } } logger.Debug("Using lock: {0}", txnLock); // at this point, the are no locks that restrict access // but we might be re-using an active compatible lock // so only write the lock if is not existing compatible if (txnLock == newLock) { try { // register the new lock: if target is not locked, lock it DataFunctions.InsertRecord( dstore, connDbName, connDbLocksTableName, newLock.LockId, new Dictionary <string, object>() { { connDbLockSourceField, newLock.LockSource }, { connDbLockRequesterField, newLock.LockRequester }, { connDbLockConnUUIDField, newLock.LockConnId }, { connDbLockDBField, newLock.LockTargetDB }, { connDbLockTableField, newLock.LockTargetTable }, { connDbLockRecordKeyField, newLock.LockTargetRecordKey }, }); } catch (Exception lockEx) { // finally statement closes the dstore and release the exclusivity if throwing an exception throw new Exception( string.Format("Error creating lock: {0} | {1}", newLock, lockEx.Message) ); } finally { try { // write changes dstore.Commit(); } catch (Exception commitEx) { // finally statement closes the dstore and release the exclusivity if throwing an exception throw new Exception( string.Format("Exception occured when commiting new locks. | {0}", commitEx.Message) ); } } } } }
public AccessRestrictedByExistingLockException(ConnectionLock newLock, ConnectionLock restrictingLock) { NewLock = newLock; RestrictingLock = restrictingLock; }