コード例 #1
0
        // Code an OP_TableLock instruction for each table locked by the statement (configured by calls to sqlite3TableLock()).
        static void codeTableLocks(Parse pParse)
        {
            var pVdbe = sqlite3GetVdbe(pParse);

            Debug.Assert(pVdbe != null); // sqlite3GetVdbe cannot fail: VDBE already allocated
            for (int i = 0; i < pParse.nTableLock; i++)
            {
                TableLock p  = pParse.aTableLock[i];
                int       p1 = p.iDb;
                sqlite3VdbeAddOp4(pVdbe, OP_TableLock, p1, p.iTab, p.isWriteLock,
                                  p.zName, P4_STATIC);
            }
        }
コード例 #2
0
        public TableLock GetTableLock(string tableName, int recordId)
        {
            TableLock result    = null;
            string    userLogin = _userService.GetCurrentUser();

            lock (safeLock)
            {
                result = tableLocks.FirstOrDefault(p => p.TableName == tableName.ToLower() &&
                                                   p.RecordKey == recordId && p.LockDate > DateTime.Now.AddSeconds(-1 * _settings.LockExpireDuration)
                                                   //&& p.UserLogin != userLogin
                                                   );
            }
            return(result);
        }
コード例 #3
0
        public void RemoveLock(TableLock tableLock)
        {
            lock (_changeLocker)
            {
                if (tableLock.LockType == LockType.Update)
                {
                    _hasMonopolLock = false;
                }

                if (_tableLocksQueue.Count > 0 && !_hasMonopolLock)
                {
                    _tableLocksQueue.TryDequeue(out var newLock);
                    newLock.Notify.Set();
                    _hasMonopolLock = newLock.LockType == LockType.Update;
                }
            }
        }
コード例 #4
0
        public static void MergeMask(AdoTransaction adoTransaction)
        {
            try
            {
                ServerMarketData.maskLock.AcquireReaderLock(CommonTimeout.LockWait);

                TableLock tableLock = (TableLock)ServerMarketData.maskLocks[MarkThree.Guardian.Server.Environment.UserName];
                adoTransaction.LockRequests.AddWriterLock(tableLock);
            }
            finally
            {
                if (ServerMarketData.maskLock.IsReaderLockHeld)
                {
                    ServerMarketData.maskLock.ReleaseReaderLock();
                }
            }

            adoTransaction.LockRequests.AddWriterLock(ServerMarketData.maskLock);
        }
コード例 #5
0
        public override bool TryAcquireLock(string correlationId, string key, long timeToLive)
        {
            try
            {
                if (_table == null)
                {
                    _logger.Error(correlationId, $"TryAcquireLock: Lock storage table is not initialized.");
                    return(false);
                }

                var operation = TableOperation.Retrieve <TableLock>(correlationId, key);
                var record    = _table.ExecuteAsync(operation).Result;
                var tableLock = record.Result as TableLock;

                if (tableLock != null)
                {
                    if (tableLock.Expired > DateTime.UtcNow)
                    {
                        _logger.Trace(correlationId, $"TryAcquireLock: Key = '{key}' has been already locked and not expired.");
                        return(false);
                    }

                    _logger.Trace(correlationId, $"TryAcquireLock: Locked key = '{key}' expired.");
                }

                var lockRecord = new TableLock(correlationId, key, timeToLive);

                var insertOrReplaceOperation = TableOperation.InsertOrReplace(lockRecord);
                _table.ExecuteAsync(insertOrReplaceOperation).Wait();

                _logger.Trace(correlationId, $"TryAcquireLock: Set Key = '{key}' to 'lock' state; it will be expired at {lockRecord.Expired.ToString()} UTC.");
                return(true);
            }
            catch (Exception exception)
            {
                _logger.Error(correlationId, exception, $"TryAcquireLock: Failed to acquire lock for key = '{key}'.");
                return(false);
            }
        }
コード例 #6
0
        public void SetTableLock(string tableName, int recordId)
        {
            string userLogin = _userService.GetCurrentUser();

            lock (safeLock)
            {
                var existingLock = tableLocks.FirstOrDefault(p => p.TableName.ToLower() == tableName.ToLower() && p.RecordKey == recordId);
                if (existingLock == null)
                {
                    TableLock tableLock = new TableLock();
                    tableLock.LockDate  = DateTime.Now;
                    tableLock.RecordKey = recordId;
                    tableLock.TableName = tableName?.ToLower();
                    tableLock.UserName  = _sharedDataManager.GetUserName();
                    tableLock.UserLogin = _userService.GetCurrentUser();
                    tableLocks.Add(tableLock);
                }
                else
                {
                    existingLock.LockDate = DateTime.Now;
                }
            }
        }
コード例 #7
0
        public void AddLock(TableLock tableLock)
        {
            lock (_changeLocker)
            {
                if (tableLock.LockType == LockType.Update)
                {
                    if (!_hasMonopolLock)
                    {
                        _hasMonopolLock = true;
                        tableLock.Notify.Set();
                    }

                    else
                    {
                        _tableLocksQueue.Enqueue(tableLock);
                    }
                }
                else
                {
                    tableLock.Notify.Set();
                }
            }
        }
コード例 #8
0
        /// <summary>
        /// Initializes the Globa Data Model.
        /// </summary>
        static ServerDataModel()
        {
            // IMPORTANT CONCEPT: The RowVersion object keeps track of the 'age' of the server dataset.  When rows are added,
            // updated and deleted, the row version is incremented.  There is a single, monotonically incrementing value used for
            // the entire DataSet.  We initialize the database with a value of 1, that allows the client to use a value of zero
            // when initializing.  A requested row version of zero will guarantee that all the contents of the database are
            // returned to the client.
            ServerDataModel.rowVersion = new RowVersion();

            // The persistent storage device used by this server is specified in this custom configuration section.
            PersistentStoreSection persistentStoreSection =
                (PersistentStoreSection)ConfigurationManager.GetSection("persistentStoreSection");
            PersistentStoreInfo persistentStoreInfo = persistentStoreSection[PersistentStore];

            if (persistentStoreInfo == null)
            {
                throw new Exception("There is no persistent storage device defined for this server.");
            }
            SqlConnection sqlConnection = new SqlConnection(persistentStoreInfo.ConnectionString);

            try
            {
                // Make sure all the tables are locked before populating them.
                foreach (TableLock tableLock in ServerDataModel.TableLocks)
                {
                    tableLock.AcquireWriterLock(CommonTimeout.LockWait);
                }

                // IMPORTANT CONCEPT: These filters are used for security and performance.  They remove elements that a user has
                // no right to view on the local client.  They must also guarantee referential integrity if a record is removed.
                // That is, if you have a filter to remove an element, there must be another filter to guarantee the children of
                // that element are not returned to the client.
                ServerDataModel.Object.UserFilter   = new RowFilterDelegate(FilterObject);
                ServerDataModel.Security.UserFilter = new RowFilterDelegate(FilterSecurity);
                ServerDataModel.Price.UserFilter    = new RowFilterDelegate(FilterPrice);
                ServerDataModel.Currency.UserFilter = new RowFilterDelegate(FilterCurrency);
                ServerDataModel.Debt.UserFilter     = new RowFilterDelegate(FilterDebt);
                ServerDataModel.Equity.UserFilter   = new RowFilterDelegate(FilterEquity);

                // A view is needed for all the tables so we can search the records according to 'age'.  The 'RowVersion' is an
                // indication of the relative age of an record.  When a client requests a refresh of it's data model, we will
                // return any records that are younger than the oldest record on the client.  To find these records efficiently, we
                // need this view on the table.
                foreach (Table table in ServerDataModel.Tables)
                {
                    table.DefaultView.Sort           = "RowVersion DESC";
                    table.DefaultView.RowStateFilter = DataViewRowState.OriginalRows;
                }

                // Open a connection to the server and read all the tables into the data model.
                sqlConnection.Open();

                // Constraints are disabled so the data can be loaded in table-by-table from the persistent store.
                ServerDataModel.EnforceConstraints = false;

                // This will keep track of the largest row number in the persistent data store.  This maximum row version will
                // become the seed value for the system-wide row version that is assigned to every row that is added, updated or
                // deleted.
                long maxRowVersion = 0;

                // Read each of the persistent tables from the relational database store.
                foreach (Table table in ServerDataModel.Tables)
                {
                    if (table.IsPersistent)
                    {
                        // Every record has an 'Archived' bit which indicates whether the record has been deleted from the active
                        // database.  This bit is used here to discard records that shouldn't be included in the active database.
                        // While it would be possible to simply select all the records where the 'Archived' bit is clear, they are
                        // added to the table and then deleted in order to keep the identity columns of the ADO tables synchronized
                        // with the indices of the persistent SQL tables.  This section of code will construct a statement that
                        // will select all the persistent columns from the relational database.
                        string columnList = "IsArchived, IsDeleted";
                        foreach (Column column in table.Columns)
                        {
                            if (column.IsPersistent)
                            {
                                columnList += (columnList == string.Empty ? string.Empty : ", ") + "\"" + column.ColumnName + "\"";
                            }
                        }
                        string selectCommand = String.Format("select {0} from \"{1}\"", columnList, table.TableName);

                        // Execute the command to read the table.
                        SqlCommand sqlCommand = new SqlCommand(selectCommand);
                        sqlCommand.Connection = sqlConnection;
                        SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();

                        // IMPORTANT CONCEPT:  The persistent database can store many deleted and archived rows.  To minimize the
                        // amount of memory needed to read these tables, the rows are reused as they are read when the row has been
                        // deleted or archived.  Active rows will be added to the data model and a new row will be created the next
                        // time a row from that table is read.
                        Row row = null;

                        // Read each record from the table into the ADO database.
                        while (sqlDataReader.Read())
                        {
                            // Create a new record (unless the row is being reused because it was deleted or archived).
                            if (row == null)
                            {
                                row = (Row)table.NewRow();
                            }

                            // Read all records from the database, throw away the ones that are archived.  This will keep the
                            // identity columns in the ADO database from repeating any of the key elements in the SQL database.
                            bool isArchived = false;
                            bool isDeleted  = false;
                            for (int column = 0; column < sqlDataReader.FieldCount; column++)
                            {
                                string columnName = sqlDataReader.GetName(column);

                                if (columnName == "IsArchived")
                                {
                                    isArchived = (bool)sqlDataReader.GetValue(column);
                                }
                                if (columnName == "IsDeleted")
                                {
                                    isDeleted = (bool)sqlDataReader.GetValue(column);
                                }

                                DataColumn destinationColumn = table.Columns[columnName];
                                if (destinationColumn != null)
                                {
                                    row[destinationColumn] = sqlDataReader.GetValue(column);
                                }
                            }

                            // IMPORTANT CONCEPT: The initial system-wide row version used by the in-memory data model to keep
                            // track of the age of a record will be the maximum row version read from the persistent store
                            // (including deleted and archived rows).
                            maxRowVersion = row.RowVersion > maxRowVersion ? row.RowVersion : maxRowVersion;

                            // Add active rows to the ADO table, reuse deleted and archived rows for the next record read.
                            if (!isArchived && !isDeleted)
                            {
                                table.Rows.Add(row);
                                row = null;
                            }
                        }

                        // This is the end of reading a table.  Close out the reader, accept the changes and move on to the next
                        // table in the DataSet.
                        sqlDataReader.Close();
                        table.AcceptChanges();
                    }
                }

                // This is the row version that will be used for all inserted, modified and deleted records.
                rowVersion.Set(maxRowVersion);

                // Once all the tables have been read, the constraints can be enforced again.  This is where any Relational
                // Integrity problems will kick out.
                ServerDataModel.EnforceConstraints = true;

                // These masks are used to dynamically filter the data that is returned to the client.
                ServerDataModel.masks     = new Hashtable();
                ServerDataModel.maskLocks = new Hashtable();
                ServerDataModel.maskLock  = new TableLock("Mask.Master");

                // Run through each of the users and create a mask for their pricing data.
                foreach (ServerDataModel.UserRow userRow in ServerDataModel.User.Rows)
                {
                    // UserRow.UserName must match MarkThree.Quasar.Server.Environment.UserName which is always lower case.
                    string  maskName = string.Format("Mask.{0}", userRow.UserName.ToLower());
                    DataSet maskSet  = new DataSet(maskName);
                    ServerDataModel.masks[userRow.UserName.ToLower()]     = maskSet;
                    ServerDataModel.maskLocks[userRow.UserName.ToLower()] = new TableLock(maskName);

                    // The mask has a single table with the security identifier in it.  Any price that matches the security
                    // identifier is returned to the client.
                    DataTable  priceTable       = maskSet.Tables.Add("Price");
                    DataColumn securityIdColumn = priceTable.Columns.Add("SecurityId", typeof(int));
                    priceTable.PrimaryKey = new DataColumn[] { securityIdColumn };
                }
            }
            catch (ConstraintException constraintException)
            {
                // Write out the exact location of the error.
                foreach (DataTable dataTable in ServerDataModel.Tables)
                {
                    foreach (DataRow dataRow in dataTable.Rows)
                    {
                        if (dataRow.HasErrors)
                        {
                            EventLog.Error("Error in '{0}': {1}", dataRow.Table.TableName, dataRow.RowError);
                        }
                    }
                }

                // Rethrow the exception.
                throw constraintException;
            }
            catch (SqlException sqlException)
            {
                // Write out the exact location of the error.
                foreach (SqlError sqlError in sqlException.Errors)
                {
                    EventLog.Error(sqlError.Message);
                }

                // Rethrow the exception.
                throw sqlException;
            }
            finally
            {
                // Release all of the write locks.
                foreach (TableLock tableLock in ServerDataModel.TableLocks)
                {
                    if (tableLock.IsWriterLockHeld)
                    {
                        tableLock.ReleaseWriterLock();
                    }
                }

                // Make sure the sqlConnection is closed, even when an exception happens.
                sqlConnection.Close();
            }
        }