Esempio n. 1
0
 /// <summary>
 /// Proper constructor.
 /// </summary>
 /// <param name="id">Store transaction id.</param>
 /// <param name="isReadOnly">True, if the store transaction is read only.</param>
 /// <param name="owner"></param>
 /// <param name="replicatorTransaction"></param>
 /// <param name="tracer"></param>
 /// <param name="enableStrict2PL">Indicates whether strict 2PL is enabled.</param>
 public StoreTransaction(
     long id,
     bool isReadOnly,
     IStoreTransactionProvider owner,
     ReplicatorTransactionBase replicatorTransaction,
     string tracer,
     bool enableStrict2PL)
 {
     this.id         = id;
     this.isReadOnly = isReadOnly;
     this.tracer     = tracer;
     this.owner      = owner;
     this.replicatorTransactionBase = replicatorTransaction;
     this.enableStrict2PL           = enableStrict2PL;
 }
        /// <summary>
        /// Locks for write.
        /// </summary>
        public async Task <StateManagerLockContext> LockForWriteAsync(
            Uri key,
            long stateProviderId,
            TransactionBase transaction,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            Utility.Assert(
                key != null,
                "{0}: LockForWriteAsync: key != null. SPID: {1}",
                this.traceType,
                stateProviderId);
            Utility.Assert(
                stateProviderId != 0,
                "{0}: LockForWriteAsync: stateProviderId is zero. Key: {1} SPID: {2}",
                this.traceType,
                key,
                stateProviderId);
            Utility.Assert(
                transaction != null,
                "{0}: LockForWriteAsync: transaction is null. Key: {1} SPID: {2}",
                this.traceType,
                key,
                stateProviderId);

            FabricEvents.Events.StateProviderMetadataManagerLockForWrite(
                this.TraceType,
                key.OriginalString,
                stateProviderId,
                transaction.Id);

            StateManagerLockContext lockContext = null;

            var incrementIndex = 0;

            while (true)
            {
                lockContext = this.keylocks.GetOrAdd(key, k => new StateManagerLockContext(k, stateProviderId, this));

                // If this transaction already has a write lock on the key lock, then do not acquire the lock, instead just increment the grantor count
                if (lockContext.LockMode == StateManagerLockMode.Write &&
                    this.DoesTransactionContainKeyLock(transaction.Id, key))
                {
                    // It is safe to do this grantor count increment outside the lock since this transaction already holds this lock.
                    lockContext.GrantorCount++;
                    break;
                }
                else if (lockContext.LockMode == StateManagerLockMode.Read &&
                         this.DoesTransactionContainKeyLock(transaction.Id, key))
                {
                    // If this transaction has a read lock on the key lock, then release read lock and acquire the write lock.
                    // SM only supports read committed so it is okay to release this lock.
                    lockContext.Release(transaction.Id);

                    // Increment grantor count by 2 to account for both the operations, but after lock acquisition.
                    incrementIndex = 2;
                }
                else
                {
                    incrementIndex = 1;
                }

                // With the removal of the global lock, when a lock is removed during an inflight transaction this exception can happen.
                // It can be solved by either ref counting or handling disposed exception, since this is a rare situation and ref counting
                // needs to be done every time. Gopalk has acked this recommendation.
                try
                {
                    await lockContext.AcquireWriteLockAsync(timeout, cancellationToken).ConfigureAwait(false);

                    lockContext.GrantorCount = lockContext.GrantorCount + incrementIndex;
                    break;
                }
                catch (ObjectDisposedException)
                {
                    // Ignore and continue
                }
                catch (Exception e)
                {
                    FabricEvents.Events.MetadataManagerAcquireWriteLockException(
                        this.TraceType,
                        key.OriginalString,
                        transaction.Id,
                        e.Message,
                        e.StackTrace);
                    throw;
                }
            }

            HashSet <Uri> keyLocks = this.InflightTransactions.GetOrAdd(transaction.Id, k => new HashSet <Uri>(AbsoluteUriEqualityComparer.Comparer));

            keyLocks.Add(key);

            return(lockContext);
        }
Esempio n. 3
0
        /// <summary>
        /// The apply async.
        /// </summary>
        /// <param name="lsn">
        /// The lsn.
        /// </param>
        /// <param name="transactionBase">
        /// The transaction base.
        /// </param>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <param name="applyContext">
        /// The apply context.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        Task <object> IStateProvider2.ApplyAsync(
            long lsn,
            TransactionBase transactionBase,
            OperationData data,
            ApplyContext applyContext)
        {
            // Get the operation.
            var operation = Operation.Deserialize(data[0]);

            // Resume the existing transaction for this operation or start a transaction for this operation.
            bool             applied;
            OperationContext context;
            DatabaseTransaction <TKey, TValue> tx;

            if (IsPrimaryOperation(applyContext) && this.inProgressOperations.TryGetValue(operation.Id, out context))
            {
                applied = true;
                tx      = context.DatabaseTransaction;
                tx.Resume();
            }
            else
            {
                // The operation has not yet been applied and therefore a transaction has not been initiated.
                applied = false;
                tx      = this.tables.CreateTransaction();
            }

            /*var part = this.replicator.StatefulPartition;
             * var operationString = JsonConvert.SerializeObject(operation, SerializationSettings.JsonConfig);
             * Trace.TraceInformation(
             *  $"[{this.partitionId}/{this.replicator.InitializationParameters.ReplicaId} r:{part.ReadStatus} w:{part.WriteStatus}] ApplyAsync(lsn: {lsn}, tx: {transactionBase.Id}, op: {operationString} (length: {data?.Length ?? 0}), context: {applyContext})");
             */
            try
            {
                // If the operation has not yet been applied, apply it.
                if (!applied)
                {
                    //Trace.TraceInformation($"{applyContext} Apply {operationString}");
                    operation.Apply(tx.Table);
                }

                //Trace.TraceInformation($"{applyContext} Commit {operationString}");
                tx.Commit();
            }
            catch (Exception exception)
            {
                tx.Rollback();

                return(Task.FromException <object>(exception));
            }
            finally
            {
                if (IsPrimaryOperation(applyContext))
                {
                    this.inProgressOperations.TryRemove(operation.Id, out context);
                }

                tx.Dispose();
            }

            return(Task.FromResult(default(object)));
        }
        /// <summary>
        /// Locks for read.
        /// </summary>
        public async Task <StateManagerLockContext> LockForReadAsync(
            Uri key,
            long stateProviderId,
            TransactionBase transaction,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            Utility.Assert(
                key != null,
                "{0}: LockForReadAsync: key != null. SPID: {1}",
                this.traceType,
                stateProviderId);
            Utility.Assert(
                stateProviderId != 0,
                "{0}: LockForReadAsync: stateProviderId is zero. Key: {1} SPID: {2}",
                this.traceType,
                key,
                stateProviderId);
            Utility.Assert(
                transaction != null,
                "{0}: LockForReadAsync: transaction is null. Key: {1} SPID: {2}",
                this.traceType,
                key,
                stateProviderId);

            StateManagerLockContext lockContext = null;

            FabricEvents.Events.StateProviderMetadataManagerLockForRead(
                this.TraceType,
                key.OriginalString,
                stateProviderId,
                transaction.Id);

            while (true)
            {
                lockContext = this.keylocks.GetOrAdd(key, k => new StateManagerLockContext(k, stateProviderId, this));

                // If this transaction already has a write lock on the key lock, then do not acquire the lock, instead increment the grantor count.
                // This check is needed only if the transaction is holding this key in the write mode, in the read mode it can acquire the read lock again
                // as it will not cause a deadlock.
                if (lockContext.LockMode == StateManagerLockMode.Write &&
                    this.DoesTransactionContainKeyLock(transaction.Id, key))
                {
                    // It is safe to do this grantor count increment outside the lock since this transaction already holds a write lock.
                    // This lock is just treated as a write lock.
                    lockContext.GrantorCount++;
                    break;
                }

                // With the removal of the global lock, when a lock is removed during an inflight transaction this exception can happen.
                // It can be solved by either ref counting or handling disposed exception, since this is a rare situation and ref counting
                // needs to be done every time. Gopalk has acked this recommendation.
                try
                {
                    await lockContext.AcquireReadLockAsync(timeout, cancellationToken).ConfigureAwait(false);

                    break;
                }
                catch (ObjectDisposedException)
                {
                    // Ignore and continue
                }
                catch (Exception e)
                {
                    FabricEvents.Events.MetadataManagerAcquireReadLockException(
                        this.TraceType,
                        key.OriginalString,
                        transaction.Id,
                        e.Message,
                        e.StackTrace);
                    throw;
                }
            }

            HashSet <Uri> keyLocks = this.InflightTransactions.GetOrAdd(transaction.Id, k => new HashSet <Uri>(AbsoluteUriEqualityComparer.Comparer));

            keyLocks.Add(key);

            return(lockContext);
        }