コード例 #1
0
ファイル: Scheme.cs プロジェクト: lanicon/DBreeze
        /// <summary>
        /// Checks if in configuration was supplied alternative path for table location.
        /// Returns true if intersection was found.
        /// Alternative Path equals to String.Empty - locate in Memory
        /// </summary>
        /// <param name="userTableName"></param>
        /// <param name="alternativePath"></param>
        /// <returns></returns>
        internal bool CheckAlternativeTableLocationsIntersections(string userTableName, out string alternativePath)
        {
            alternativePath = String.Empty;

            foreach (var pattern in Engine.Configuration.AlternativeTablesLocations)
            {
                //pattern.Key
                if (DbUserTables.PatternsIntersect(pattern.Key, userTableName))
                {
                    alternativePath = pattern.Value;
                    return(true);
                }
            }

            return(false);
        }
コード例 #2
0
        /// <summary>
        /// Access synchronizer.
        /// All calls of the WRITE LOCATOR come over this function.
        /// </summary>
        /// <param name="transactionThreadId"></param>
        /// <param name="tablesNames"></param>
        /// <param name="calledBySynchronizer"></param>
        public void RegisterWriteTablesForTransaction(int transactionThreadId, List <string> tablesNames, bool calledBySynchronizer)
        {
            //in every transaction unit we got a list of reserved for WRITING tables

            //if we have in tablesNames one of the tables which is in this list we have to stop the thread with mre
            bool            toWaitTillTransactionIsFinished = false;
            bool            breakOuterLoop  = false;
            TransactionUnit transactionUnit = null;
            bool            deadlock        = false;

            //When SyncTables is called
            //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> SYNC IN Thread: " + transactionThreadId);

            while (true)    //loop till thread will get full access to write tables
            {
                toWaitTillTransactionIsFinished = false;
                breakOuterLoop = false;
                deadlock       = false;

                //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> " + "Thread: " + transactionThreadId + "WHILE ");

                //only tables required for writing or read-commited will have to go over this fast bottleneck
                lock (_sync_dl)
                {
                    _sync_transactions.EnterReadLock();
                    try
                    {
                        this._transactions.TryGetValue(transactionThreadId, out transactionUnit);

                        if (transactionUnit == null)
                        {
                            return; //transaction doesn't exist anymore, gracefully goes out
                        }
                        if (!calledBySynchronizer)
                        {
                            //Here we are in case if Registrator is called by WriteTableCall, so we check intersections
                            //Between reserved Write tables and current table using patterns intersections technique.
                            //If they intersect we let the thread to proceed
                            if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tablesNames))
                            {
                                return;
                            }
                        }


                        //iterating over all open transactions except self, finding out if desired tables are locked by other threads.
                        foreach (var tu in this._transactions.Where(r => r.Value.TransactionThreadId != transactionThreadId))
                        {
                            foreach (string tableName in tu.Value.GetTransactionWriteTablesNames())
                            {
                                //if (tablesNames.Contains(tableName))
                                if (DbUserTables.TableNamesContains(tablesNames, tableName))
                                {
                                    //
                                    //++++++++++++++ here we can register all tables which are waiting for write lock release
                                    transactionUnit.AddTransactionWriteTablesAwaitingReservation(tablesNames);

                                    //++++++++++++++ if thread, who has locked this table has another table in a "waiting for reservation" blocked by this thread - it's a deadlock
                                    //if (transactionUnit.GetTransactionWriteTablesNames().Intersect(tu.Value.GetTransactionWriteTablesAwaitingReservation()).Count() > 0)
                                    if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tu.Value.GetTransactionWriteTablesAwaitingReservation()))
                                    {
                                        //we got deadlock, we will stop this transaction with an exception
                                        deadlock = true;
                                    }

                                    //other thread has reserved table for the transaction we have to wait
                                    toWaitTillTransactionIsFinished = true;
                                    breakOuterLoop = true;

                                    if (!deadlock)
                                    {
                                        //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> " + "Thread: " + transactionThreadId + " GATE IS CLOSED ");

                                        ThreadsGator.CloseGate();  //closing gate only if no deadlock situation
                                        //mreWriteTransactionLock.Reset();   //setting to signalled only in non-deadlock case
                                    }

                                    break;
                                }
                            }

                            if (breakOuterLoop)
                            {
                                break;
                            }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        this.UnregisterTransaction(transactionThreadId);

                        throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_TABLE_WRITE_REGISTRATION_FAILED, ex);
                    }
                    finally
                    {
                        _sync_transactions.ExitReadLock();
                    }

                    //if(true) this thread owns all table for modification lock
                    if (!toWaitTillTransactionIsFinished)
                    {
                        //+++++++++++ Here we can clear all table names in the waiting reservation queue
                        transactionUnit.ClearTransactionWriteTablesAwaitingReservation(tablesNames);

                        //we have to reserve for our transaction all tables
                        foreach (var tbn in tablesNames)
                        {
                            transactionUnit.AddTransactionWriteTable(tbn, null);
                        }

                        //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> SYNC OUT Thread: " + transactionThreadId + "   Sync stop: " + transactionUnit.udtSyncStop.ToString("dd.MM.yyyy HH:mm:ss"));

                        return;
                    }
                }//end of lock


                if (deadlock)
                {
                    this.UnregisterTransaction(transactionThreadId);

                    throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_IN_DEADLOCK);
                }

                if (toWaitTillTransactionIsFinished)
                {
                    //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> " + "Thread: " + transactionThreadId + " GATE IS PUT ");

                    //blocking thread which requires busy tables for writing, till they are released
                    //ThreadsGator.PutGateHere(20000);    //every 20 second (or by Gate open we give a chance to re-try, for safety reasons of hanged threads, if programmer didn't dispose DBreeze process after the programm end)

                    //#if ASYNC
                    //                    await ThreadsGator.PutGateHere().ConfigureAwait(false);
                    //#else
                    //                    ThreadsGator.PutGateHere();
                    //#endif

                    ThreadsGator.PutGateHere();

                    //mreWriteTransactionLock.WaitOne();
                }
            }//eo while
        }
コード例 #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="lockType"></param>
        /// <param name="tables"></param>
        /// <returns>false if thread grants access, false if thread is in a queue</returns>
        public bool AddSession(eTransactionTablesLockTypes lockType, string[] tables)
        {
            lock (lock_disposed)
            {
                if (disposed)
                {
                    return(true);
                }
            }

            internSession iSession = null;
            bool          ret      = true;

            _sync.EnterWriteLock();
            try
            {
                foreach (var ses in _acceptedSessions)
                {
                    if (DbUserTables.TableNamesIntersect(ses.Value.tables.ToList(), tables.ToList()))
                    {
                        if (ses.Value.lockType == eTransactionTablesLockTypes.EXCLUSIVE || lockType == eTransactionTablesLockTypes.EXCLUSIVE)
                        {
                            //Lock
                            ret = false;
                            break;
                        }
                    }
                }

                if (!ret)
                {
                    internSession xSes = null;
                    foreach (var ses in _waitingSessionSequence)
                    {
                        if (ses == Environment.CurrentManagedThreadId)
                        {
                            break;
                        }

                        _waitingSessions.TryGetValue(ses, out xSes);

                        if (DbUserTables.TableNamesIntersect(xSes.tables.ToList(), tables.ToList()))
                        {
                            if (xSes.lockType == eTransactionTablesLockTypes.EXCLUSIVE || lockType == eTransactionTablesLockTypes.EXCLUSIVE)
                            {
                                //Lock
                                ret = false;
                                break;
                            }
                        }
                    }
                }

                if (_waitingSessions.TryGetValue(Environment.CurrentManagedThreadId, out iSession))
                {
                    //This session was in the waiting list once
                    if (ret)
                    {
                        //We have to take away session from waiting list
                        iSession.gator.Dispose();
                        iSession.gator = null;
                        _waitingSessions.Remove(Environment.CurrentManagedThreadId);
                        _waitingSessionSequence.Remove(Environment.CurrentManagedThreadId);
                    }
                    else
                    {
                        iSession.gator.CloseGate();
                    }
                }
                else
                {
                    //Creating new session
                    iSession = new internSession()
                    {
                        lockType = lockType,
                        tables   = tables
                    };

                    if (!ret)
                    {
                        iSession.gator = new DbThreadsGator(false);
                        _waitingSessions.Add(Environment.CurrentManagedThreadId, iSession);
                        _waitingSessionSequence.Add(Environment.CurrentManagedThreadId);
                    }
                }

                if (ret)
                {
                    //Adding into accepted sessions
                    _acceptedSessions.Add(Environment.CurrentManagedThreadId, iSession);
                }
            }
            finally
            {
                _sync.ExitWriteLock();
            }

            if (!ret)
            {
                //putting gate
                iSession.gator.PutGateHere();
            }

            return(ret);
        }
コード例 #4
0
ファイル: Scheme.cs プロジェクト: lanicon/DBreeze
        /// <summary>
        /// Returns table for READ, WRITE FUNC
        /// </summary>
        /// <param name="userTableName"></param>
        /// <returns></returns>
        internal LTrie GetTable(string userTableName)
        {
            string tableName = GetUserTableNameAsString(userTableName);

            //TODO pattern based mapping If table doesn't exist we create it with properties which could be supplied after db init as regex theme.



            //Schema protocol: 2 bytes - protocol version, other data
            //For protocol 1: first 8 bytes will be TheFileName, starting from db10000-dbN (0-N ulong). up to 10000 are reserved for dbreeze.

            //Table names are UTF-8 based, no limits

            ulong     fileName = 0;
            OpenTable otl      = null;

            _sync_openTablesHolder.EnterUpgradeableReadLock();
            try
            {
                _openTablesHolder.TryGetValue(tableName, out otl);

                if (otl != null)
                {
                    //Try to increase usage and return LTrie
                    otl.Add();
                    return(otl.Trie);
                }


                //Probably table Exists in db but not in openTablesHolder

                _sync_openTablesHolder.EnterWriteLock();
                try
                {
                    //UpgradeableRead recheck
                    _openTablesHolder.TryGetValue(tableName, out otl);

                    if (otl != null)
                    {
                        //Try to increase usage and return LTrie
                        otl.Add();
                        return(otl.Trie);
                    }



                    byte[] btTableName = GetUserTableNameAsByte(userTableName);

                    //Trying to get fileName from cache
                    fileName = this.cachedTableNames.GetFileName(tableName);
                    // LTrieRow row = null;
                    bool tableExists = false;

                    if (fileName == 0)
                    {
                        LTrieRow row = LTrie.GetKey(btTableName, false, false);


                        if (row.Exists)
                        {
                            tableExists = true;

                            byte[] fullValue = row.GetFullValue(false);
                            //Can be parsed different. First protocol version is 1
                            ushort schemeProtocol = fullValue.Substring(0, 2).To_UInt16_BigEndian();

                            switch (schemeProtocol)
                            {
                            case 1:
                                fileName = fullValue.Substring(2, 8).To_UInt64_BigEndian();
                                break;

                            default:
                                throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.SCHEME_FILE_PROTOCOL_IS_UNKNOWN);
                            }
                        }
                        else
                        {
                            tableExists = false;
                            //Creating new table.

                            //Checking table name validity

                            //this will throw exception, if not valid
                            DbUserTables.UserTableNameIsOk(userTableName);


                            //Creating such table and renewing LastFileNumber counter

                            //Adding to LastFileNumber
                            LastFileNumber++;


                            ////Deleting physical files related to the table, if they existed - normally they should not
                            //DeleteAllReleatedTableFiles(Path.Combine(Engine.MainFolder, LastFileNumber.ToString()));

                            byte[] lft = LastFileNumber.To_8_bytes_array_BigEndian();

                            //Writing this number to Schema file
                            LTrie.Add(Encoding.UTF8.GetBytes(LastFileNumberKeyName), lft);

                            //Creating table self and writing to Schema file

                            LTrie.Add(btTableName,
                                      new byte[] { 0, 1 } //Protocol version 1
                                      .Concat(lft));    //Number of the file

                            //Committing both records
                            LTrie.Commit();

                            fileName = LastFileNumber;

                            this.cachedTableNames.Add(tableName, fileName);
                        }
                    }
                    else
                    {
                        tableExists = true;
                    }

                    //Creating LTrie, adding it to _openTablesHolder

                    //Seeting up Trie TableName, OTHER SETTINGS

                    TrieSettings ts      = new TrieSettings();
                    IStorage     storage = null;


                    ////Checking if default Flusg Disk behaviour was overriden
                    //ts.DiskFlushBehaviour = Engine.Configuration.DiskFlushBehaviour;
                    ////Checking if we have alternative DiskFlush behaviour
                    //foreach (var pattern in Engine.Configuration.AlternativeDiskFlushBehaviour)
                    //{
                    //    //pattern.Key
                    //    if (DbUserTables.PatternsIntersect(pattern.Key, userTableName))
                    //    {

                    //        ts.DiskFlushBehaviour = pattern.Value;
                    //        break;
                    //    }
                    //}

                    string alternativeTableLocation = String.Empty;

                    if (CheckAlternativeTableLocationsIntersections(userTableName, out alternativeTableLocation))
                    {
                        ts.StorageWasOverriden = true;

                        if (alternativeTableLocation == String.Empty)
                        {
                            ts.AlternativeTableStorageType = DBreezeConfiguration.eStorage.MEMORY;

                            storage = new StorageLayer(Path.Combine(Engine.MainFolder, fileName.ToString()), ts, Engine.Configuration);
                        }
                        else
                        {
                            ts.AlternativeTableStorageType   = DBreezeConfiguration.eStorage.DISK;
                            ts.AlternativeTableStorageFolder = alternativeTableLocation;

                            DirectoryInfo diAlt = new DirectoryInfo(alternativeTableLocation);
                            if (!diAlt.Exists)
                            {
                                diAlt.Create();
                            }

                            if (!tableExists)
                            {
                                //Deleting physical files related to the table, if they existed - normally they should not
                                DeleteAllReleatedTableFiles(Path.Combine(ts.AlternativeTableStorageFolder, LastFileNumber.ToString()));
                            }

                            storage = new StorageLayer(Path.Combine(ts.AlternativeTableStorageFolder, fileName.ToString()), ts, Engine.Configuration);
                        }
                    }
                    else
                    {
                        if (!tableExists)
                        {
                            //Deleting physical files related to the table, if they existed - normally they should not
                            DeleteAllReleatedTableFiles(Path.Combine(Engine.MainFolder, LastFileNumber.ToString()));
                        }

                        storage = new StorageLayer(Path.Combine(Engine.MainFolder, fileName.ToString()), ts, Engine.Configuration);
                    }

                    //storage = new StorageLayer(Path.Combine(Engine.MainFolder, fileName.ToString()), ts, Engine.Configuration);

                    LTrie trie = new LTrie(storage);

                    //Setting LTrie user table name
                    trie.TableName = userTableName;

                    //_openTablesHolder.Add(tableName, trie);

                    //Automatically increased usage in OpenTable constructor
                    _openTablesHolder.Add(tableName, new OpenTable(trie));

                    return(trie);
                }
                catch (System.Exception ex)
                {
                    //CASCADE
                    throw ex;
                }
                finally
                {
                    _sync_openTablesHolder.ExitWriteLock();
                }
            }
            catch (Exception ex)
            {
                throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.SCHEME_GET_TABLE_WRITE_FAILED, tableName, ex);
            }
            finally
            {
                _sync_openTablesHolder.ExitUpgradeableReadLock();
            }
        }
コード例 #5
0
        /// <summary>
        /// Access synchronizer.
        /// All calls of the WRITE LOCATOR come over this function.
        /// </summary>
        /// <param name="transactionThreadId"></param>
        /// <param name="tablesNames"></param>
        public void RegisterWriteTablesForTransaction(int transactionThreadId, List <string> tablesNames, bool calledBySynchronizer)
        {
            //in every transaction unit we got a list of reserved for WRITING tables

            //if we have in tablesNames one of the tables which is in this list we have to stop the thread with mre
            bool            toWaitTillTransactionIsFinished = false;
            bool            breakOuterLoop  = false;
            TransactionUnit transactionUnit = null;
            bool            deadlock        = false;

            Exception innerException = null;

            while (true)    //loop till thread will get full access to write tables
            {
                toWaitTillTransactionIsFinished = false;
                breakOuterLoop = false;
                deadlock       = false;

                //only tables required for writing or read-commited will have to go over this fast bottleneck
                lock (_sync_dl)
                {
                    _sync_transactions.EnterReadLock();
                    try
                    {
                        this._transactions.TryGetValue(transactionThreadId, out transactionUnit);

                        if (transactionUnit == null)
                        {
                            return; //transaction doesn't exist anymore, gracefully goes out
                        }
                        if (!calledBySynchronizer)
                        {
                            //Here we are in case if Registrator is called by WriteTableCall, so we check intersections
                            //Between reserved Write tables and current table using patterns intersections technique.
                            //If they intersect we let the thread to proceed
                            if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tablesNames))
                            {
                                return;
                            }
                            //Help for the programmer on the early stage to see problem with the possible deadlock
                            if (_engine.Configuration.NotifyAhead_WhenWriteTablePossibleDeadlock)
                            {
                                if (transactionUnit.TransactionWriteTablesCount > 0)
                                {
                                    throw new Exception("Put table \"" + tablesNames.FirstOrDefault() + "\" into tran.SynchronizeTables statement, because it will be modified");
                                }
                            }
                        }


                        //iterating over all open transactions except self, finding out if desired tables are locked by other threads.
                        foreach (var tu in this._transactions.Where(r => r.Value.TransactionThreadId != transactionThreadId))
                        {
                            foreach (string tableName in tu.Value.GetTransactionWriteTablesNames())
                            {
                                //if (tablesNames.Contains(tableName))
                                if (DbUserTables.TableNamesContains(tablesNames, tableName))
                                {
                                    //
                                    //++++++++++++++ here we can register all tables which are waiting for write lock release
                                    transactionUnit.AddTransactionWriteTablesAwaitingReservation(tablesNames);

                                    //++++++++++++++ if thread, who has locked this table has another table in a "waiting for reservation" blocked by this thread - it's a deadlock
                                    //if (transactionUnit.GetTransactionWriteTablesNames().Intersect(tu.Value.GetTransactionWriteTablesAwaitingReservation()).Count() > 0)
                                    if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tu.Value.GetTransactionWriteTablesAwaitingReservation()))
                                    {
                                        //we got deadlock, we will stop this transaction with an exception
                                        deadlock = true;
                                    }

                                    //other thread has reserved table for the transaction we have to wait
                                    toWaitTillTransactionIsFinished = true;
                                    breakOuterLoop = true;

                                    if (!deadlock)
                                    {
                                        ThreadsGator.CloseGate();  //closing gate only if no deadlock situation
                                        //mreWriteTransactionLock.Reset();   //setting to signalled only in non-deadlock case
                                    }

                                    break;
                                }
                            }

                            if (breakOuterLoop)
                            {
                                break;
                            }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        innerException = ex;
                        //this.UnregisterTransaction(transactionThreadId);

                        //throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_TABLE_WRITE_REGISTRATION_FAILED,ex);
                    }
                    finally
                    {
                        _sync_transactions.ExitReadLock();
                    }

                    if (innerException != null)
                    {
                        this.UnregisterTransaction(transactionThreadId);
                        throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_TABLE_WRITE_REGISTRATION_FAILED, innerException);
                    }

                    //if(true) this thread owns all table for modification lock
                    if (!toWaitTillTransactionIsFinished)
                    {
                        //+++++++++++ Here we can clear all table names in the waiting reservation queue
                        transactionUnit.ClearTransactionWriteTablesAwaitingReservation(tablesNames);

                        //we have to reserve for our transaction all tables
                        foreach (var tbn in tablesNames)
                        {
                            transactionUnit.AddTransactionWriteTable(tbn, null);
                        }

                        return;
                    }
                }//end of lock


                if (deadlock)
                {
                    this.UnregisterTransaction(transactionThreadId);

                    throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_IN_DEADLOCK);
                }

                if (toWaitTillTransactionIsFinished)
                {
                    //blocking thread which requires busy tables for writing, till they are released
                    //ThreadsGator.PutGateHere(20000);    //every 20 second (or by Gate open we give a chance to re-try, for safety reasons of hanged threads, if programmer didn't dispose DBreeze process after the programm end)
                    ThreadsGator.PutGateHere();
                    //mreWriteTransactionLock.WaitOne();
                }
            }
        }