Ejemplo n.º 1
0
        /// <summary>
        /// Transactional Write procedure.
        /// </summary>
        public void Save()
        {
            var info = TransactionContext.GetTransactionInfo();

            if (this.logger.IsEnabled(LogLevel.Debug))
            {
                this.logger.Debug("Write {0}", info);
            }

            if (info.IsReadOnly)
            {
                // For obvious reasons...
                throw new OrleansReadOnlyViolatedException(info.TransactionId);
            }

            Rollback();

            var copiedValue = this.transactionCopy[info.TransactionId];

            //
            // Validation
            //

            if (this.version.TransactionId > info.TransactionId || this.highestReadTransactionId >= info.TransactionId)
            {
                // Prevent cycles. Wait-die
                throw new OrleansTransactionWaitDieException(info.TransactionId);
            }

            TransactionalResourceVersion nextVersion = TransactionalResourceVersion.Create(info.TransactionId,
                                                                                           this.version.TransactionId == info.TransactionId ? this.version.WriteNumber + 1 : 1);

            //
            // Update Transaction Context
            //
            info.RecordWrite(transactionalResource, this.version, this.metadata.StableVersion.TransactionId);

            //
            // Modify the State
            //
            if (!this.log.ContainsKey(info.TransactionId))
            {
                LogRecord <TState> r = new LogRecord <TState>();
                this.log[info.TransactionId] = r;
            }

            LogRecord <TState> logRecord = this.log[info.TransactionId];

            logRecord.NewVal  = copiedValue;
            logRecord.Version = nextVersion;
            this.value        = copiedValue;
            this.version      = nextVersion;

            this.transactionCopy.Remove(info.TransactionId);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Transactional Read procedure.
        /// </summary>
        private TState GetState()
        {
            var info = TransactionContext.GetTransactionInfo();

            Rollback();

            if (this.transactionCopy.TryGetValue(info.TransactionId, out TState state))
            {
                return(state);
            }

            if (this.logger.IsEnabled(LogLevel.Debug))
            {
                this.logger.Debug("GetState {0}", info);
            }

            if (!TryGetVersion(info.TransactionId, out TState readState, out TransactionalResourceVersion readVersion))
            {
                // This can only happen if old versions are gone due to checkpointing.
                throw new OrleansTransactionVersionDeletedException(info.TransactionId);
            }

            if (info.IsReadOnly && readVersion.TransactionId > this.metadata.StableVersion.TransactionId)
            {
                throw new OrleansTransactionUnstableVersionException(info.TransactionId);
            }

            info.RecordRead(transactionalResource, readVersion, this.metadata.StableVersion.TransactionId);

            this.highestReadTransactionId = Math.Max(this.highestReadTransactionId, info.TransactionId - 1);

            TState copy = this.copier.DeepCopy(readState);

            if (!info.IsReadOnly)
            {
                this.transactionCopy[info.TransactionId] = copy;
            }

            return(copy);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Transactional Read procedure.
        /// </summary>
        private TState GetState()
        {
            var info = TransactionContext.GetTransactionInfo();

            if (this.logger.IsVerbose2)
            {
                this.logger.Verbose2("Read {0}", info);
            }

            Restore();

            if (this.transactionCopy.TryGetValue(info.TransactionId, out TState state))
            {
                return(state);
            }

            if (!TryGetVersion(info.TransactionId, out TState readState, out TransactionalResourceVersion readVersion))
            {
                // This can only happen if old versions are gone due to checkpointing.
                throw new OrleansTransactionVersionDeletedException(info.TransactionId);
            }

            if (info.IsReadOnly && readVersion.TransactionId > this.stableVersion)
            {
                throw new OrleansTransactionUnstableVersionException(info.TransactionId);
            }

            info.RecordRead(transactionalResource, readVersion, this.storage.State.StableVersion);

            writeLowerBound = Math.Max(writeLowerBound, info.TransactionId - 1);

            TState copy = this.copier.DeepCopy(readState);

            if (!info.IsReadOnly)
            {
                this.transactionCopy[info.TransactionId] = copy;
            }

            return(copy);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Read the current state.
        /// </summary>
        public Task <TResult> PerformRead <TResult>(Func <TState, TResult> operation)
        {
            if (detectReentrancy)
            {
                throw new LockRecursionException("cannot perform a read operation from within another operation");
            }

            var info = (TransactionInfo)TransactionContext.GetTransactionInfo();

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"StartRead {info}");
            }

            info.Participants.TryGetValue(thisParticipant, out var recordedaccesses);

            // schedule read access to happen under the lock
            return(EnterLock <TResult>(info.TransactionId, info.Priority, recordedaccesses, true,
                                       new Task <TResult>(() =>
            {
                // check if our record is gone because we expired while waiting
                if (!currentGroup.TryGetValue(info.TransactionId, out var record))
                {
                    throw new OrleansTransactionLockAcquireTimeoutException(info.TransactionId.ToString());
                }

                // merge the current clock into the transaction time stamp
                record.Timestamp = MergeAndReadClock(info.TimeStamp);

                if (record.State == null)
                {
                    GetMostRecentState(out record.State, out record.SequenceNumber);
                }

                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"update-lock read v{record.SequenceNumber} {record.TransactionId} {record.Timestamp:o}");
                }

                // record this read in the transaction info data structure
                info.RecordRead(thisParticipant, record.Timestamp);

                // perform the read
                TResult result = default(TResult);
                try
                {
                    detectReentrancy = true;

                    result = operation(record.State);
                }
                finally
                {
                    if (logger.IsEnabled(LogLevel.Trace))
                    {
                        logger.Trace($"EndRead {info} {result} {record.State}");
                    }

                    detectReentrancy = false;
                }

                return result;
            })));
        }
Ejemplo n.º 5
0
        // to avoid code duplication, we call this with exactly one of update1, update2 non-null
        private Task <TResult> PerformUpdate <TResult>(Func <TState, TResult> update1, Action <TState> update2)
        {
            if (detectReentrancy)
            {
                throw new LockRecursionException("cannot perform an update operation from within another operation");
            }

            var info = (TransactionInfo)TransactionContext.GetTransactionInfo();

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"StartWrite {info}");
            }

            if (info.IsReadOnly)
            {
                throw new OrleansReadOnlyViolatedException(info.Id);
            }

            info.Participants.TryGetValue(thisParticipant, out var recordedaccesses);

            return(EnterLock <TResult>(info.TransactionId, info.Priority, recordedaccesses, false,
                                       new Task <TResult>(() =>
            {
                // check if we expired while waiting
                if (!currentGroup.TryGetValue(info.TransactionId, out var record))
                {
                    throw new OrleansTransactionLockAcquireTimeoutException(info.TransactionId.ToString());
                }

                // merge the current clock into the transaction time stamp
                record.Timestamp = MergeAndReadClock(info.TimeStamp);

                // link to the latest state
                if (record.State == null)
                {
                    GetMostRecentState(out record.State, out record.SequenceNumber);
                }

                // if this is the first write, make a deep copy of the state
                if (record.NumberWrites == 0)
                {
                    record.State = copier.DeepCopy(record.State);
                    record.SequenceNumber++;
                }

                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"update-lock write v{record.SequenceNumber} {record.TransactionId} {record.Timestamp:o}");
                }

                // record this write in the transaction info data structure
                info.RecordWrite(thisParticipant, record.Timestamp);

                // record this participant as a TM candidate
                if (info.TMCandidate != thisParticipant)
                {
                    int batchsize = CountBatchableOperationsAtTopOfCommitQueue();
                    if (info.TMCandidate == null || batchsize > info.TMBatchSize)
                    {
                        info.TMCandidate = thisParticipant;
                        info.TMBatchSize = batchsize;
                    }
                }

                // perform the write
                TResult result = default(TResult);
                try
                {
                    detectReentrancy = true;

                    if (update1 != null)
                    {
                        result = update1(record.State);
                    }
                    else if (update2 != null)
                    {
                        update2(record.State);
                    }
                    return result;
                }
                finally
                {
                    if (logger.IsEnabled(LogLevel.Trace))
                    {
                        logger.Trace($"EndWrite {info} {result} {record.State}");
                    }

                    detectReentrancy = false;
                }
            }
                                                          )));
        }