public async Task <bool> Transact(GrainTransactionData <TDeltaState> transaction, int index)
        {
            // setting TransactionId locks record as pending, on silo failure OnActivateAsync recovers it
            State.TransactionId    = transaction.TransactionId;
            State.TransactionDelta = transaction.Values[index].Item2;
            TransferTimestamp      = DateTime.Now.ToOADate();
            if (!Commit(State.TransactionDelta))
            {
                return(false);
            }
            if (State.Reminder == null)
            {
                // the reminder ensures that recovery will occur on silo failure
                // once a reminder is set, it can be reused for multiple transactions
                string name = "TransactionGrain" + this.GetPrimaryKey();
                // make sure the reminder occurs before the TransactionStatusPool purges status for this transaction
                TimeSpan WAKEUP_PERIOD = TimeSpan.FromMinutes(TransactionStatusPoolHelper.PURGE_PERIOD_MINUTES / 2);
                State.Reminder = await this.RegisterOrUpdateReminder(name, WAKEUP_PERIOD, WAKEUP_PERIOD);
            }
            await base.WriteStateAsync();

            int nextindex = index + 1;

            if (nextindex >= transaction.Values.Count)
            {
                // we have reached the end of the transaction, record that it's complete then unwind the stack
                var transactionStatus = GrainFactory.GetGrain <ITransactionStatusPool>(TransactionStatusPoolHelper.GetHash(State.TransactionId));
                await transactionStatus.SetComplete(State.TransactionId);
            }
            else
            {
                bool exceptionThrown = false;
                try
                {
                    if (!await transaction.Values[nextindex].Item1.Transact(transaction, nextindex))
                    {
                        // a member considers this transaction illegal, rollback
                        Rollback(State.TransactionDelta);
                        State.TransactionId = default(Guid);
                        return(false);
                    }
                }
                catch
                {
                    exceptionThrown = true;
                }

                if (exceptionThrown)
                {
                    // if an exception is thrown we are left in a state where it is unclear if the transaction completed
                    // use the TransactionStatus grain to ensure that all grains either commit or roll back atomically
                    await CheckForPendingTransaction();
                }
            }

            // clear transaction delay persisting for performance till ReceiveReminder
            // OnActivateAsync will check TransactionStatus on silo crash
            IsTransactionPersistenceRequired = true;
            State.TransactionId = default(Guid);
            return(true);
        }
        private async Task CheckForPendingTransaction()
        {
            if (State.TransactionId != default(Guid))
            {
                var  transactionStatus = GrainFactory.GetGrain <ITransactionStatusPool>(TransactionStatusPoolHelper.GetHash(State.TransactionId));
                bool success           = await transactionStatus.IsComplete(State.TransactionId);

                if (success)
                {
                    State.TransactionId = default(Guid);
                }
                else
                {
                    Console.WriteLine("Rolling back the transaction because it never completed.");
                    Rollback(State.TransactionDelta);
                    State.TransactionId = default(Guid);
                }
            }
        }