コード例 #1
0
        private DataSampling DeleteEntriesPerf(ReplicatedTable rtable, List <CustomerEntity> entries)
        {
            var stats = new DataSampling("[D]elete");

            foreach (var entry in entries)
            {
                TableOperation retrieveOperation = TableOperation.Retrieve <CustomerEntity>(entry.PartitionKey, entry.RowKey);
                TableResult    retrieveResult    = rtable.Execute(retrieveOperation);

                Assert.IsNotNull(retrieveResult, "retrieveResult = null");
                Assert.AreEqual((int)HttpStatusCode.OK, retrieveResult.HttpStatusCode, "retrieveResult.HttpStatusCode mismatch");
                Assert.IsNotNull((CustomerEntity)retrieveResult.Result, "Retrieve: customer = null");

                // Delete entity
                var customer = (CustomerEntity)retrieveResult.Result;
                Assert.IsTrue(customer._rtable_Version == 2, "entry was updated once, version should be 2");

                TableOperation deleteOperation = TableOperation.Delete(customer);

                TableResult deleteResult;
                var         watch = System.Diagnostics.Stopwatch.StartNew();
                {
                    deleteResult = rtable.Execute(deleteOperation);
                }
                watch.Stop();
                stats.AddPoint(watch.ElapsedMilliseconds);

                Assert.IsNotNull(deleteResult, "deleteResult = null");
                Assert.AreEqual((int)HttpStatusCode.NoContent, deleteResult.HttpStatusCode, "deleteResult.HttpStatusCode mismatch");
            }

            return(stats);
        }
コード例 #2
0
        private DataSampling UpdateEntriesPerf(ReplicatedTable rtable, List <CustomerEntity> entries)
        {
            var stats = new DataSampling("[U]pdate");

            foreach (var entry in entries)
            {
                TableOperation retrieveOperation = TableOperation.Retrieve <CustomerEntity>(entry.PartitionKey, entry.RowKey);
                TableResult    retrieveResult    = rtable.Execute(retrieveOperation);

                Assert.IsNotNull(retrieveResult, "retrieveResult = null");
                Assert.AreEqual((int)HttpStatusCode.OK, retrieveResult.HttpStatusCode, "retrieveResult.HttpStatusCode mismatch");
                Assert.IsNotNull((CustomerEntity)retrieveResult.Result, "Retrieve: customer = null");

                // Update entity
                var customer = (CustomerEntity)retrieveResult.Result;
                customer.Email = string.Format("{0}.{1}@email.com", entry.PartitionKey, entry.RowKey);

                TableOperation updateOperation = TableOperation.Replace(customer);

                TableResult updateResult;
                var         watch = System.Diagnostics.Stopwatch.StartNew();
                {
                    updateResult = rtable.Execute(updateOperation);
                }
                watch.Stop();
                stats.AddPoint(watch.ElapsedMilliseconds);

                Assert.IsNotNull(updateResult, "updateResult = null");
                Assert.AreEqual((int)HttpStatusCode.NoContent, updateResult.HttpStatusCode, "updateResult.HttpStatusCode mismatch");
            }

            return(stats);
        }
コード例 #3
0
        public void LinqQueriesDontReturnPhysicallyDeletedEntries()
        {
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName";
            string lastName  = "LastName";

            /*
             * 1 - insert entries
             */
            for (int i = 0; i < 10; i++)
            {
                var customer = new CustomerEntity(firstName + i, lastName + i);

                TableOperation operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }

            /*
             * 2 - delete entries #2 and #4
             */
            foreach (var i in new int[] { 2, 4 })
            {
                TableOperation operation       = TableOperation.Retrieve <CustomerEntity>(firstName + i, lastName + i);
                TableResult    retrievedResult = rtable.Execute(operation);

                var customer = (CustomerEntity)retrievedResult.Result;

                TableOperation deleteOperation = TableOperation.Delete(customer);
                TableResult    deleteResult    = rtable.Execute(deleteOperation);

                Assert.IsNotNull(deleteResult, "deleteResult = null");
                Assert.AreEqual((int)HttpStatusCode.NoContent, deleteResult.HttpStatusCode, "deleteResult.HttpStatusCode mismatch");
            }

            /*
             * 3 - CreateQuery doesn't return entries #2 and #4
             */
            foreach (var customer in rtable.CreateReplicatedQuery <CustomerEntity>().AsEnumerable())
            {
                int id = int.Parse(customer.PartitionKey.Replace(firstName, ""));

                Assert.AreNotEqual(id, 2, "entry #2 should have been deleted");
                Assert.AreNotEqual(id, 4, "entry #4 should have been deleted");
            }

            /*
             * 4 - ExecuteQuery doesn't return entries #2 and #4
             */
            foreach (var customer in rtable.ExecuteQuery <CustomerEntity>(new TableQuery <CustomerEntity>()))
            {
                int id = int.Parse(customer.PartitionKey.Replace(firstName, ""));

                Assert.AreNotEqual(id, 2, "entry #2 should have been deleted");
                Assert.AreNotEqual(id, 4, "entry #4 should have been deleted");
            }
        }
コード例 #4
0
        private DataSampling RetrieveEntriesPerf(ReplicatedTable rtable, List <CustomerEntity> entries)
        {
            var stats = new DataSampling("[R]etrieve");

            foreach (var entry in entries)
            {
                TableOperation retrieveOperation = TableOperation.Retrieve <CustomerEntity>(entry.PartitionKey, entry.RowKey);

                TableResult retrieveResult = null;
                var         watch          = System.Diagnostics.Stopwatch.StartNew();
                {
                    retrieveResult = rtable.Execute(retrieveOperation);
                }
                watch.Stop();
                stats.AddPoint(watch.ElapsedMilliseconds);

                Assert.IsNotNull(retrieveResult, "retrieveResult = null");
                Assert.AreEqual((int)HttpStatusCode.OK, retrieveResult.HttpStatusCode, "retrieveResult.HttpStatusCode mismatch");

                var customer = (CustomerEntity)retrieveResult.Result;
                Assert.IsNotNull(customer, "Retrieve: customer = null");

                Assert.IsTrue(customer._rtable_Version == 1, "new entry should have version 1");
            }

            return(stats);
        }
コード例 #5
0
        private void UpdateCustomer(CustomerEntity customer, ReplicatedTable repTable)
        {
            TableOperation operation    = TableOperation.Replace(customer);
            TableResult    updateResult = repTable.Execute(operation);

            Assert.IsNotNull(updateResult, "updateResult = null");
            Assert.AreEqual((int)HttpStatusCode.NoContent, updateResult.HttpStatusCode, "updateResult.HttpStatusCode mismatch");
        }
コード例 #6
0
        private void InsertCustormer(CustomerEntity customer, ReplicatedTable repTable)
        {
            TableOperation operation = TableOperation.Insert(customer);
            TableResult    result    = repTable.Execute(operation);

            Assert.AreNotEqual(null, result, "result = null");
            Assert.AreEqual((int)HttpStatusCode.NoContent, result.HttpStatusCode, "result.HttpStatusCode mismatch");
            Assert.AreNotEqual(null, result.Result, "result.Result = null");
        }
コード例 #7
0
        private CustomerEntity RetrieveCustomer(string firstName, string lastName, ReplicatedTable repTable)
        {
            TableOperation operation       = TableOperation.Retrieve <CustomerEntity>(firstName, lastName);
            TableResult    retrievedResult = repTable.Execute(operation);

            Assert.AreNotEqual(null, retrievedResult, "retrievedResult = null");
            Assert.AreEqual((int)HttpStatusCode.OK, retrievedResult.HttpStatusCode, "retrievedResult.HttpStatusCode mismatch");
            Assert.AreNotEqual(null, retrievedResult.Result, "retrievedResult.Result = null");

            return((CustomerEntity)retrievedResult.Result);
        }
コード例 #8
0
        private DataSampling CreateEntriesStats(ReplicatedTable rtable, List <CustomerEntity> entries)
        {
            var stats = new DataSampling("[C]reate");

            foreach (var entry in entries)
            {
                TableOperation insertOperation = TableOperation.Insert(entry);

                TableResult insertResult = null;
                var         watch        = System.Diagnostics.Stopwatch.StartNew();
                {
                    insertResult = rtable.Execute(insertOperation);
                }
                watch.Stop();
                stats.AddPoint(watch.ElapsedMilliseconds);

                Assert.IsNotNull(insertResult, "insertResult = null");
                Assert.AreEqual((int)HttpStatusCode.NoContent, insertResult.HttpStatusCode, "insertResult.HttpStatusCode mismatch");
            }

            return(stats);
        }
コード例 #9
0
        public void A00InsertOrReplaceCallConflictingWithDeleteOnTheHead()
        {
            this.rtableWrapper = RTableWrapperForSampleRTableEntity.GetRTableWrapper(this.repTable);

            string entityPartitionKey = "jobType-DelayInsertOrReplaceWhileDelete";
            string entityRowKey       = "jobType-DelayInsertOrReplaceWhileDelete";

            this.ForceDeleteEntryFromStorageTablesDirectly(entityPartitionKey, entityRowKey);

            // Insert one entry
            Console.WriteLine("Inserting entry ...");
            var entry = new SampleRTableEntity(entityPartitionKey, entityRowKey, "insert message");

            this.rtableWrapper.InsertRow(entry);

            // 1 - Launch an Upsert task in wait mode ...
            bool deleteLockedHead = false;

            TableResult upsertResult = null;
            var         upsertTask   = Task.Run(() =>
            {
                while (!deleteLockedHead)
                {
                    Thread.Sleep(5);
                }

                try
                {
                    entry     = new SampleRTableEntity(entityPartitionKey, entityRowKey, "upsert message");
                    var table = new ReplicatedTable(this.repTable.TableName, this.configurationService);

                    TableOperation upserOperation = TableOperation.InsertOrReplace(entry);

                    Console.WriteLine("Upsert started ...");
                    upsertResult = table.Execute(upserOperation);
                    Console.WriteLine("Upsert completed with HttpStatus={0}", upsertResult == null ? "NULL" : upsertResult.HttpStatusCode.ToString());
                }
                catch (AggregateException ex)
                {
                    Console.WriteLine(ex);
                }
            });


            string accountNameToTamper = this.rtableTestConfiguration.StorageInformation.AccountNames[0];

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            int delayInMs = 5;

            // Delay behavior
            ProxyBehavior[] behaviors = new[]
            {
                DelayBehaviors.DelayAllResponsesIf(
                    delayInMs,
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    Console.WriteLine(body);

                    // Delete locking the head response
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"_rtable_Operation\":\"Replace\"") &&
                        body.Contains("\"_rtable_RowLock\":true") &&
                        body.Contains("\"_rtable_Tombstone\":true"))
                    {
                        // Signal upsert we locked the head, so it can continue ...
                        deleteLockedHead = true;
                        return(true);
                    }

                    return(false);
                })),
            };

            // Launch a delete
            using (new HttpMangler(false, behaviors))
            {
                try
                {
                    var table = new ReplicatedTable(this.repTable.TableName, this.configurationService);

                    // Retrieve entity
                    TableOperation retrieveOperation = TableOperation.Retrieve <SampleRTableEntity>(entry.PartitionKey, entry.RowKey);
                    TableResult    retrieveResult    = table.Execute(retrieveOperation);

                    Assert.IsNotNull(retrieveResult, "retrieveResult = null");
                    Assert.AreEqual((int)HttpStatusCode.OK, retrieveResult.HttpStatusCode, "retrieveResult.HttpStatusCode mismatch");
                    Assert.IsNotNull((SampleRTableEntity)retrieveResult.Result, "Retrieve: customer = null");


                    // Delete entity
                    TableOperation deleteOperation = TableOperation.Delete((SampleRTableEntity)retrieveResult.Result);
                    TableResult    deleteResult    = table.Execute(deleteOperation);

                    Assert.IsNotNull(deleteResult, "deleteResult = null");
                    Assert.AreEqual((int)HttpStatusCode.NoContent, deleteResult.HttpStatusCode, "deleteResult.HttpStatusCode mismatch");
                }
                catch (AggregateException ex)
                {
                    Console.WriteLine(ex);
                }
            }

            // wait on upsert to finish ...
            upsertTask.Wait();

            // Upsert suceeded?
            Assert.AreEqual((int)HttpStatusCode.NoContent, upsertResult.HttpStatusCode, "Upsert expected to suceed!");
            SampleRTableEntity upsertedEntity = this.rtableWrapper.ReadEntity(entityPartitionKey, entityRowKey);

            Assert.NotNull(upsertedEntity, "upsert should succeed");
            Assert.AreEqual(upsertedEntity.Message, "upsert message", "upsert should succeeded");
        }
コード例 #10
0
        public void ReadingUncommittedDataFromReplicaIdentifiedByReadTailIndexWontThrow()
        {
            ReplicatedTableConfigurationServiceV2 configServiceOne, configServiceTwo;

            // setup:        Acc0    Acc1
            //  stale view = [H] ->  [T]
            //  new view   = [H]
            SetupStaleViewAndNewView(out configServiceOne, out configServiceTwo);

            long staleViewId  = configServiceOne.GetTableView(this.repTable.TableName).ViewId;
            long latestViewId = configServiceTwo.GetTableView(this.repTable.TableName).ViewId;

            string firstName = "FirstName01";
            string lastName  = "LastName01";

            /*
             * 1 - WorkerOne => inserts an entry in stale View
             */
            var workerOne = new ReplicatedTable(this.repTable.TableName, configServiceOne);
            var workerTwo = new ReplicatedTable(this.repTable.TableName, configServiceTwo);

            var customer = new CustomerEntity(firstName, lastName);

            customer.Email = "***";
            InsertCustormer(customer, workerOne);

            string accountNameToTamper = this.rtableTestConfiguration.StorageInformation.AccountNames[1];

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            TableResult    oldUpdateResult       = null;
            CustomerEntity customerSeenByNewView = null;

            // Delay behavior
            ProxyBehavior[] behaviors =
            {
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    session.oRequest.FailSession((int)HttpStatusCode.ServiceUnavailable, "ServerBusy", "");
                }),
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Fail Lock to Tail by stale view
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"Email\":\"workerOne\"") &&
                        body.Contains(string.Format("\"_rtable_ViewId\":\"{0}\"", staleViewId)) &&
                        body.Contains("\"_rtable_RowLock\":true"))
                    {
                        return(true);
                    }

                    return(false);
                })),
            };

            /*
             * 2 - WorkerOne => update an entry using the stale View
             */
            using (new HttpMangler(false, behaviors))
            {
                customer       = RetrieveCustomer(firstName, lastName, workerOne);
                customer.Email = "workerOne";

                TableOperation operation = TableOperation.Replace(customer);
                oldUpdateResult = workerOne.Execute(operation);
            }


            // Expected behavior:

            // Thread_1 (stale ViewId) fails to commit to Tail
            Assert.IsNotNull(oldUpdateResult, "oldUpdateResult = null");
            Assert.AreEqual((int)HttpStatusCode.ServiceUnavailable, oldUpdateResult.HttpStatusCode, "oldUpdateResult.HttpStatusCode mismatch");
            Console.WriteLine("Update in stale View Succeeded with HttpStatus={0}", oldUpdateResult.HttpStatusCode);

            // Thread_1 (stale ViewId): Reads the entry => Succeeds eventhough the row is "uncommitted".
            //                          "ReadTailIndex = [Head]" means [Head] is treated as [Tail] for Reads.
            //                           => row is assumed "committed"
            customer = RetrieveCustomer(firstName, lastName, workerOne);
            Assert.IsNotNull(customer, "customer = null");
            Assert.AreEqual("workerOne", customer.Email, "customer.Email mismatch");

            // Thread_2 (new ViewId): Reads the entry => Succeed since reading from Head
            customerSeenByNewView = RetrieveCustomer(firstName, lastName, workerTwo);
            Assert.IsNotNull(customerSeenByNewView, "customerSeenByNewView = null");
            Assert.AreEqual("workerOne", customer.Email, "customer.Email mismatch");
        }
コード例 #11
0
        public void WhenReadFromTailFailsWithServiceUnavailableWeReadFromHeadAndSucceed()
        {
            View view = this.configurationWrapper.GetWriteView();

            Assert.IsTrue(view.Chain.Count > 1, "expects at least 2 replicas!");

            // Insert one entry
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName01";
            string lastName  = "LastName01";

            var            customer  = new CustomerEntity(firstName, lastName);
            TableOperation operation = TableOperation.Insert(customer);

            rtable.Execute(operation);


            // Using xstore modify the row in each replica individually ... so we know, later, which replica RTable will retrieve from
            for (int replicaIndex = 0; replicaIndex < this.cloudTableClients.Count; replicaIndex++)
            {
                CloudTableClient tableClient = this.cloudTableClients[replicaIndex];
                CloudTable       table       = tableClient.GetTableReference(this.repTable.TableName);

                TableOperation retrieveOperation = TableOperation.Retrieve <CustomerEntity>(firstName, lastName);
                TableResult    retrieveResult    = table.Execute(retrieveOperation);

                customer       = (CustomerEntity)retrieveResult.Result;
                customer.Email = replicaIndex.ToString(); // Head = 0
                // ...  = 1
                // Tail = 2

                TableOperation updateOperation = TableOperation.Replace(customer);
                TableResult    updateResult    = table.Execute(updateOperation);

                Assert.IsNotNull(updateResult, "updateResult = null");
                Console.WriteLine("updateResult.HttpStatusCode = {0}", updateResult.HttpStatusCode);
                Assert.AreEqual((int)HttpStatusCode.NoContent, updateResult.HttpStatusCode, "updateResult.HttpStatusCode mismatch");
            }

            string accountNameToNotTamper = this.rtableTestConfiguration.StorageInformation.AccountNames[0];

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToNotTamper={0}", accountNameToNotTamper);

            // Delay behavior
            ProxyBehavior[] behaviors =
            {
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    session.oRequest.FailSession((int)HttpStatusCode.ServiceUnavailable, "ServerBusy", "");
                }),
                    (session =>
                {
                    if (session.HTTPMethodIs("Get"))
                    {
                        // Fail Get from all replicas, except Head
                        if (!session.hostname.Contains(accountNameToNotTamper + "."))
                        {
                            return(true);
                        }
                    }

                    return(false);
                })),
            };

            using (new HttpMangler(false, behaviors))
            {
                operation = TableOperation.Retrieve <CustomerEntity>(firstName, lastName);
                TableResult retrievedResult = repTable.Execute(operation);

                Assert.AreNotEqual(null, retrievedResult, "retrievedResult = null");
                Assert.AreEqual((int)HttpStatusCode.OK, retrievedResult.HttpStatusCode, "retrievedResult.HttpStatusCode mismatch");
                Assert.AreNotEqual(null, retrievedResult.Result, "retrievedResult.Result = null");

                customer = (CustomerEntity)retrievedResult.Result;
                Assert.AreEqual(customer.Email, (0).ToString(), "we should have read the row from Head");
            }
        }
コード例 #12
0
        public void LinqQueriesAreServedFromHeadWhenReadViewTailIndexIsSet()
        {
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName";
            string lastName  = "LastName";

            int dataSize = 5;

            /*
             * 1 - insert entries
             */
            for (int i = 0; i < dataSize; i++)
            {
                var customer = new CustomerEntity(firstName + i, lastName + i);
                customer.Email = "***";

                TableOperation operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            // Identify the Tail account
            View view = this.configurationWrapper.GetWriteView();

            Assert.IsTrue(view.Chain.Count > 1, "expects at least one replica!");

            string accountNameToTamper = view.Chain.Last().Item1.StorageAccountName;

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            // We will fail requests to update a row in Tail
            ProxyBehavior[] behaviors =
            {
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    session.oRequest.FailSession((int)HttpStatusCode.ServiceUnavailable, "ServerBusy", "");
                }),
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Fail Lock to Tail by stale view
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"_rtable_RowLock\":true"))
                    {
                        return(true);
                    }

                    return(false);
                })),
            };

            using (new HttpMangler(false, behaviors))
            {
                /*
                 * 2 - update entries
                 */
                for (int i = 0; i < dataSize; i++)
                {
                    TableOperation operation       = TableOperation.Retrieve <CustomerEntity>(firstName + i, lastName + i);
                    TableResult    retrievedResult = rtable.Execute(operation);

                    var customer = (CustomerEntity)retrievedResult.Result;
                    customer.Email = "updated";

                    TableOperation replaceOperation = TableOperation.Replace(customer);
                    TableResult    replaceResult    = rtable.Execute(replaceOperation);

                    Assert.IsNotNull(replaceResult, "replaceResult = null");
                    Assert.AreEqual((int)HttpStatusCode.ServiceUnavailable, replaceResult.HttpStatusCode, "replaceResult.HttpStatusCode mismatch");
                }
            }


            SetReadViewTailIndex(0);
            Assert.AreEqual(0, this.configurationService.GetTableView(this.repTable.TableName).ReadTailIndex, "ReadTailIndex should be 0!!!");


            /*
             * 3 - CreateQuery is served from [Head], data should be new
             */
            foreach (var customer in rtable.CreateReplicatedQuery <CustomerEntity>().AsEnumerable())
            {
                Assert.AreEqual(customer.Email, "updated", "expected new data");
            }


            /*
             * 4 - ExecuteQuery is served from [Head], data should be new
             */
            foreach (var customer in rtable.ExecuteQuery <CustomerEntity>(new TableQuery <CustomerEntity>()))
            {
                Assert.AreEqual(customer.Email, "updated", "expected new data");
            }
        }
コード例 #13
0
        public void CaseNoRepair_ReadRowFromNewViewCutOffTailUpdateRow()
        {
            string firstName = "FirstName";
            string lastName  = "LastName";

            TableOperation operation;
            TableResult    result;
            CustomerEntity customer;

            // - View has 2 replicas ?
            View view = this.configurationWrapper.GetWriteView();

            Assert.IsTrue(view.Chain.Count == 2, "expects 2 replicas only!");


            // 1 - [None]->[RW]
            ReplicatedTableConfiguration config;

            this.configurationService.RetrieveConfiguration(out config);
            ReplicatedTableConfigurationStore viewConfg = config.GetView(view.Name);

            viewConfg.ReplicaChain[0].Status = ReplicaStatus.None;
            viewConfg.ViewId++;
            this.configurationService.UpdateConfiguration(config);

            // *** - Insert entries in old viewId
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            for (int i = 0; i < 3; i++)
            {
                customer = new CustomerEntity(firstName + i, lastName + i);

                operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            // 2 - Insert Head => [WO]->[RW]
            this.configurationService.RetrieveConfiguration(out config);
            viewConfg = config.GetView(view.Name);
            viewConfg.ReplicaChain[0].Status = ReplicaStatus.WriteOnly;
            viewConfg.ViewId++;
            this.configurationService.UpdateConfiguration(config);

            // *** - Insert entries in new viewId
            rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);
            for (int i = 10; i < 13; i++)
            {
                customer = new CustomerEntity(firstName + i, lastName + i);

                operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            // => Read old entry - from Tail -
            int entryId = 10;

            operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
            result    = rtable.Execute(operation);


            // 3 - Cut-off Tail without repairing replicas
            configurationService.RetrieveConfiguration(out config);
            viewConfg = config.GetView(view.Name);
            viewConfg.ReplicaChain[0].Status = ReplicaStatus.ReadWrite;
            viewConfg.ReplicaChain[1].Status = ReplicaStatus.None;
            viewConfg.ViewId++;
            this.configurationService.UpdateConfiguration(config);

            // => Update the row
            customer       = (CustomerEntity)result.Result;
            customer.Email = "*****@*****.**";
            operation      = TableOperation.Replace(customer);
            result         = rtable.Execute(operation);
            Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.NoContent, "Update customer failed");
        }
コード例 #14
0
        public void LinqQueriesDontReturnRowsWithHighViewIdWhenFlagIgnoreHigherViewIdRowsIsSet()
        {
            TableOperation operation;
            TableResult    result;
            CustomerEntity customer;


            /*
             * Set config viewId to 5
             */
            long staleViewId = 5;

            SetConfigViewIdAndIgnoreHigherViewIdRowsFlag(staleViewId, false);
            Assert.AreEqual(staleViewId, this.configurationService.GetTableView(this.repTable.TableName).ViewId, "View should be 5!!!");


            // Insert entries in stale viewId 5
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName";
            string lastName  = "LastName";

            for (int i = 0; i < 10; i++)
            {
                customer = new CustomerEntity(firstName + i, lastName + i);

                operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            /*
             * Set config new viewId to 6
             */
            long newViewId = 6;

            SetConfigViewIdAndIgnoreHigherViewIdRowsFlag(newViewId, false);
            Assert.AreEqual(newViewId, this.configurationService.GetTableView(this.repTable.TableName).ViewId, "View should be 6!!!");


            // Update entry #5 and #8 in new viewId 6
            foreach (int entryId in new int[] { 5, 8 })
            {
                operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
                result    = rtable.Execute(operation);

                Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.OK && (CustomerEntity)result.Result != null, "Retrieve customer failed");

                customer       = (CustomerEntity)result.Result;
                customer.Email = "*****@*****.**";

                operation = TableOperation.Replace(customer);
                result    = rtable.Execute(operation);

                Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.NoContent, "Update customer failed");
            }


            /*
             * Simulate a stale client => Set config viewId back to 5
             *                            and Set 'IgnoreHigherViewIdRows' flag so we ignore rows with higher viewIds
             */
            SetConfigViewIdAndIgnoreHigherViewIdRowsFlag(staleViewId, true);
            Assert.AreEqual(staleViewId, this.configurationService.GetTableView(this.repTable.TableName).ViewId, "View should be 5!!!");

            try
            {
                // Check Retrieve of row #5 and #8 returns NotFound
                foreach (int entryId in new int[] { 5, 8 })
                {
                    operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
                    var retrievedResult = rtable.Execute(operation);

                    Assert.AreNotEqual(null, retrievedResult, "retrievedResult = null");
                    Assert.AreEqual((int)HttpStatusCode.NotFound, retrievedResult.HttpStatusCode, "retrievedResult.HttpStatusCode mismatch");
                }
            }
            catch (ReplicatedTableStaleViewException)
            {
                Assert.Fail("Retrieve() is expected to NotFound the row, but got RTableStaleViewException !");
            }

            /*
             * stale client using LINQ: CreateReplicatedQuery
             */
            foreach (var entry in rtable.CreateReplicatedQuery <CustomerEntity>().AsEnumerable())
            {
                int id = int.Parse(entry.PartitionKey.Replace(firstName, ""));

                Assert.AreNotEqual(id, 5, "row #5 should not be returned");
                Assert.AreNotEqual(id, 8, "row #8 should not be returned");

                Assert.AreEqual(entry._rtable_ViewId, staleViewId, "CreateReplicatedQuery: entry viewId should be '5'");
            }

            /*
             * stale client using LINQ: ExecuteQuery
             */
            foreach (var entry in rtable.ExecuteQuery <CustomerEntity>(new TableQuery <CustomerEntity>()))
            {
                int id = int.Parse(entry.PartitionKey.Replace(firstName, ""));

                Assert.AreNotEqual(id, 5, "row #5 should not be returned");
                Assert.AreNotEqual(id, 8, "row #8 should not be returned");

                Assert.AreEqual(entry._rtable_ViewId, staleViewId, "CreateReplicatedQuery: entry viewId should be '5'");
            }
        }
コード例 #15
0
        public void LinqQueriesThrowAfterDetectingStaleViewWhenThrowOnStaleViewInLinqQueryFlagIsSet()
        {
            TableOperation operation;
            TableResult    result;
            CustomerEntity customer;


            /*
             * Set config viewId to 5
             */
            long staleViewId = 5;

            SetConfigViewIdAndIgnoreHigherViewIdRowsFlag(staleViewId, false);
            Assert.AreEqual(staleViewId, this.configurationService.GetTableView(this.repTable.TableName).ViewId, "View should be 5!!!");


            // Insert entries in stale viewId 5
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName";
            string lastName  = "LastName";

            for (int i = 0; i < 10; i++)
            {
                customer = new CustomerEntity(firstName + i, lastName + i);

                operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            /*
             * Set config new viewId to 6
             */
            long newViewId = 6;

            SetConfigViewIdAndIgnoreHigherViewIdRowsFlag(newViewId, false);
            Assert.AreEqual(newViewId, this.configurationService.GetTableView(this.repTable.TableName).ViewId, "View should be 6!!!");


            // Update entry #5 in new viewId 6
            int entryId = 5;

            operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
            result    = rtable.Execute(operation);

            Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.OK && (CustomerEntity)result.Result != null, "Retrieve customer failed");

            customer       = (CustomerEntity)result.Result;
            customer.Email = "*****@*****.**";

            operation = TableOperation.Replace(customer);
            result    = rtable.Execute(operation);

            Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.NoContent, "Update customer failed");


            /*
             * Simulate a stale client => Set config viewId back to 5
             */
            SetConfigViewIdAndIgnoreHigherViewIdRowsFlag(staleViewId, false);
            Assert.AreEqual(staleViewId, this.configurationService.GetTableView(this.repTable.TableName).ViewId, "View should be 5!!!");

            try
            {
                // Check Retrieve of row #5 throws stale view as expected
                operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
                rtable.Execute(operation);

                Assert.Fail("Retrieve() is expected to get an RTableStaleViewException but did not get it.");
            }
            catch (ReplicatedTableStaleViewException ex)
            {
                Assert.IsTrue(ex.ErrorCode == ReplicatedTableViewErrorCodes.ViewIdSmallerThanEntryViewId);
                Assert.IsTrue(ex.Message.Contains(string.Format("current _rtable_ViewId {0} is smaller than _rtable_ViewId of existing row {1}", staleViewId, newViewId)), "Got unexpected exception message");
            }


            // Enable throwing on stale view detection
            rtable.ThrowOnStaleViewInLinqQueryFlag = true;

            /*
             * stale client using LINQ: CreateReplicatedQuery
             */
            try
            {
                foreach (var entry in rtable.CreateReplicatedQuery <CustomerEntity>().AsEnumerable())
                {
                    int id = int.Parse(entry.PartitionKey.Replace(firstName, ""));
                    Assert.IsTrue(id != entryId, "we should throw on entry #5");

                    Assert.AreEqual(entry._rtable_ViewId, staleViewId, "CreateReplicatedQuery: entry viewId should be '5'");
                }
            }
            catch (ReplicatedTableStaleViewException ex)
            {
                Assert.IsTrue(ex.ErrorCode == ReplicatedTableViewErrorCodes.ViewIdSmallerThanEntryViewId);
                Assert.IsTrue(ex.Message.Contains(string.Format("current _rtable_ViewId {0} is smaller than _rtable_ViewId of existing row {1}", staleViewId, newViewId)), "Got unexpected exception message");
            }

            /*
             * stale client using LINQ: ExecuteQuery
             */
            try
            {
                foreach (var entry in rtable.ExecuteQuery <CustomerEntity>(new TableQuery <CustomerEntity>()))
                {
                    int id = int.Parse(entry.PartitionKey.Replace(firstName, ""));
                    Assert.IsTrue(id != entryId, "we should throw on entry #5");

                    Assert.AreEqual(entry._rtable_ViewId, staleViewId, "ExecuteQuery: entry viewId should be '5'");
                }
            }
            catch (ReplicatedTableStaleViewException ex)
            {
                Assert.IsTrue(ex.ErrorCode == ReplicatedTableViewErrorCodes.ViewIdSmallerThanEntryViewId);
                Assert.IsTrue(ex.Message.Contains(string.Format("current _rtable_ViewId {0} is smaller than _rtable_ViewId of existing row {1}", staleViewId, newViewId)), "Got unexpected exception message");
            }
        }
コード例 #16
0
        public void LinqQueriesDontReturnEntriesWithTombstone()
        {
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName";
            string lastName  = "LastName";

            /*
             * 1 - insert entries
             */
            for (int i = 0; i < 10; i++)
            {
                var customer = new CustomerEntity(firstName + i, lastName + i);

                TableOperation operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            // Identify the Tail account
            View view = this.configurationWrapper.GetWriteView();

            Assert.IsTrue(view.Chain.Count > 1, "expects at least one replica!");

            string accountNameToTamper = view.Chain.Last().Item1.StorageAccountName;

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            // We will fail requests to delete a row in Tail
            ProxyBehavior[] behaviors =
            {
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    session.oRequest.FailSession((int)HttpStatusCode.ServiceUnavailable, "ServerBusy", "");
                }),
                    (session =>
                {
                    // Commit to Tail i.e. physically delete the row
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("DELETE"))
                    {
                        return(true);
                    }

                    return(false);
                })),
            };

            using (new HttpMangler(false, behaviors))
            {
                /*
                 * 2 - delete entries #2 and #4
                 */
                foreach (var i in new int[] { 2, 4 })
                {
                    TableOperation operation       = TableOperation.Retrieve <CustomerEntity>(firstName + i, lastName + i);
                    TableResult    retrievedResult = rtable.Execute(operation);

                    var customer = (CustomerEntity)retrievedResult.Result;

                    TableOperation deleteOperation = TableOperation.Delete(customer);
                    TableResult    deleteResult    = rtable.Execute(deleteOperation);

                    Assert.IsNotNull(deleteResult, "deleteResult = null");
                    Assert.AreEqual((int)HttpStatusCode.ServiceUnavailable, deleteResult.HttpStatusCode, "deleteResult.HttpStatusCode mismatch");
                }
            }


            // Verify, Retrieve doesn't return entries #2 and #4
            int deleteId = 2;
            var result   = rtable.Execute(TableOperation.Retrieve <CustomerEntity>(firstName + deleteId, lastName + deleteId));

            Assert.IsNotNull(result, "result = null");
            Assert.AreEqual((int)HttpStatusCode.NotFound, result.HttpStatusCode, "result.HttpStatusCode mismatch");

            deleteId = 4;
            result   = rtable.Execute(TableOperation.Retrieve <CustomerEntity>(firstName + deleteId, lastName + deleteId));
            Assert.IsNotNull(result, "result = null");
            Assert.AreEqual((int)HttpStatusCode.NotFound, result.HttpStatusCode, "result.HttpStatusCode mismatch");


            /*
             * 3 - CreateQuery doesn't return entries #2 and #4
             */
            foreach (var customer in rtable.CreateReplicatedQuery <CustomerEntity>().AsEnumerable())
            {
                int id = int.Parse(customer.PartitionKey.Replace(firstName, ""));
                Assert.AreNotEqual(id, 2, "entry #2 should have been deleted");
                Assert.AreNotEqual(id, 4, "entry #4 should have been deleted");
            }


            /*
             * 4 - ExecuteQuery doesn't return entries #2 and #4
             */
            foreach (var customer in rtable.ExecuteQuery <CustomerEntity>(new TableQuery <CustomerEntity>()))
            {
                int id = int.Parse(customer.PartitionKey.Replace(firstName, ""));
                Assert.AreNotEqual(id, 2, "entry #2 should have been deleted");
                Assert.AreNotEqual(id, 4, "entry #4 should have been deleted");
            }
        }
コード例 #17
0
        public void RepairTableWontRepairRowsWithHigherViewIdWhenIgnoreHigherViewIdRowsFlagIsSet()
        {
            TableOperation operation;
            TableResult    result;
            CustomerEntity customer;


            // - View has 2 replicas ?
            View view = this.configurationWrapper.GetWriteView();

            Assert.IsTrue(view.Chain.Count == 2, "expects 2 replicas only!");


            // - Remove the Head, [None]->[RW]
            RemoveHeadFromView(view.Name, 600, this.configurationService);


            // 1 - Insert entries in old viewId
            var rtable = new ReplicatedTable(this.repTable.TableName, this.configurationService);

            string firstName = "FirstName";
            string lastName  = "LastName";

            for (int i = 0; i < 10; i++)
            {
                customer = new CustomerEntity(firstName + i, lastName + i);

                operation = TableOperation.Insert(customer);
                rtable.Execute(operation);
            }


            // 2 - Increase viewId => so we can create rows with higher viewId
            //   - Update entry #5 and #8 in new viewId
            ReplicatedTableConfiguration    config;
            ReplicatedTableQuorumReadResult readStatus = this.configurationService.RetrieveConfiguration(out config);

            Assert.IsTrue(readStatus.Code == ReplicatedTableQuorumReadCode.Success);
            ReplicatedTableConfigurationStore viewConfg = config.GetView(view.Name);

            viewConfg.ViewId += 100;
            this.configurationService.UpdateConfiguration(config);

            foreach (int entryId in new int[] { 5, 8 })
            {
                operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
                result    = rtable.Execute(operation);

                Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.OK && (CustomerEntity)result.Result != null, "Retrieve customer failed");

                customer       = (CustomerEntity)result.Result;
                customer.Email = "*****@*****.**";

                operation = TableOperation.Replace(customer);
                result    = rtable.Execute(operation);

                Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.NoContent, "Update customer failed");
            }


            // 3 - Restore previous viewId, and,
            //   - Set 'IgnoreHigherViewIdRows' flag so we ignore rows with higher viewIds
            readStatus = this.configurationService.RetrieveConfiguration(out config);
            Assert.IsTrue(readStatus.Code == ReplicatedTableQuorumReadCode.Success);
            viewConfg         = config.GetView(view.Name);
            viewConfg.ViewId -= 100;
            config.SetIgnoreHigherViewIdRowsFlag(true);
            this.configurationService.UpdateConfiguration(config);

            try
            {
                // Check Retrieve of row #5 and #8 returns NotFound
                foreach (int entryId in new int[] { 5, 8 })
                {
                    operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
                    var retrievedResult = rtable.Execute(operation);

                    Assert.AreNotEqual(null, retrievedResult, "retrievedResult = null");
                    Assert.AreEqual((int)HttpStatusCode.NotFound, retrievedResult.HttpStatusCode, "retrievedResult.HttpStatusCode mismatch");
                }
            }
            catch (ReplicatedTableStaleViewException)
            {
                Assert.Fail("Retrieve() is expected to NotFound the row, but got RTableStaleViewException !");
            }


            // 4 - Now insert a Head [WO]->[RW]
            //   - Then, call RepairTable ...
            InsertHeadInView(view.Name, 600, this.configurationService);
            ReconfigurationStatus status = rtable.RepairTable(0, null);

            Assert.AreEqual(status, ReconfigurationStatus.PARTIAL_FAILURE, "rows with higher viewId should not be repaired");


            // 5 - Check rows with higher viewId still NotFound, even after repair ...
            try
            {
                // Check Retrieve of row #5 and #8 returns NotFound
                foreach (int entryId in new int[] { 5, 8 })
                {
                    operation = TableOperation.Retrieve <CustomerEntity>(firstName + entryId, lastName + entryId);
                    var retrievedResult = rtable.Execute(operation);

                    Assert.AreNotEqual(null, retrievedResult, "retrievedResult = null");
                    Assert.AreEqual((int)HttpStatusCode.NotFound, retrievedResult.HttpStatusCode, "retrievedResult.HttpStatusCode mismatch");
                }
            }
            catch (ReplicatedTableStaleViewException)
            {
                Assert.Fail("Retrieve() is expected to NotFound the row, but got RTableStaleViewException !");
            }
        }
コード例 #18
0
        public void A00TwoInsertOrReplaceCallsConflictingOnTheHead()
        {
            this.rtableWrapper = RTableWrapperForSampleRTableEntity.GetRTableWrapper(this.repTable);

            string entityPartitionKey = "jobType-DelayInsertOrReplaceRowHeadTest";
            string entityRowKey       = "jobId-DelayInsertOrReplaceRowHeadTest";

            this.ForceDeleteEntryFromStorageTablesDirectly(entityPartitionKey, entityRowKey);

            // Insert one entry
            Console.WriteLine("Inserting entry ...");
            var entry = new SampleRTableEntity(entityPartitionKey, entityRowKey, "insert message");

            this.rtableWrapper.InsertRow(entry);

            string accountNameToTamper = this.rtableTestConfiguration.StorageInformation.AccountNames[0];

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            int  delayInMs = 3000;
            bool firstWritterInitiatingCommit  = false;
            bool secondWritterTriedLockingHead = false;

            // Delay behavior
            ProxyBehavior[] behaviors = new[]
            {
                // Writter-1 tampering
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    int iter = 0;

                    // Signal Writter-2
                    firstWritterInitiatingCommit = true;

                    // Blobk commit to head ... until Writter-2 try to lock the head
                    while (!secondWritterTriedLockingHead)
                    {
                        Console.WriteLine("Writter-1 waiting on Writter-2 to try to lock the Head (#{0})", iter);
                        Thread.Sleep(delayInMs);

                        if (++iter > 10)
                        {
                            break;
                        }
                    }
                }),
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Writter-1 committing to head
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"_rtable_Operation\":\"Replace\"") &&
                        body.Contains("\"_rtable_RowLock\":false") &&
                        body.Contains("\"Message\":\"upsert message 0\"")
                        )
                    {
                        return(true);
                    }

                    return(false);
                })),

                // Writter-2 tampering
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    // Block till Writter-1 issues a commit to head
                    while (!firstWritterInitiatingCommit)
                    {
                        Console.WriteLine("Writter-2 waiting on Writter-1 to issue a commit to head");
                        Thread.Sleep(delayInMs);
                    }
                }),
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Writter-2 locking the head
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"_rtable_Operation\":\"Replace\"") &&
                        body.Contains("\"_rtable_RowLock\":true") &&
                        body.Contains("\"Message\":\"upsert message 1\"")
                        )
                    {
                        return(true);
                    }

                    return(false);
                })),

                // Delay Writter-2 lock-to-the-head's response, so Writter-1 can continue with its commit.
                DelayBehaviors.DelayAllResponsesIf(
                    delayInMs,
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Writter-2 locking the head response
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"_rtable_Operation\":\"Replace\"") &&
                        body.Contains("\"_rtable_RowLock\":true") &&
                        body.Contains("\"Message\":\"upsert message 1\"")
                        )
                    {
                        // Signal Writter-1 so it can continue with commit to head
                        secondWritterTriedLockingHead = true;
                        return(true);
                    }

                    return(false);
                })),
            };

            // Launch 2 concurrent Upserts
            var results = new TableResult[2];

            using (new HttpMangler(false, behaviors))
            {
                Parallel.For(0, 2, (index) =>
                {
                    entry = new SampleRTableEntity(entityPartitionKey, entityRowKey, string.Format("upsert message {0}", index));

                    try
                    {
                        var table = new ReplicatedTable(this.repTable.TableName, this.configurationService);

                        TableOperation operation = TableOperation.InsertOrReplace(entry);
                        results[index]           = table.Execute(operation);
                    }
                    catch (AggregateException ex)
                    {
                        Console.WriteLine(ex);
                    }
                });
            }

            // Writter-1 suceed?
            Assert.AreEqual((int)HttpStatusCode.NoContent, results[0].HttpStatusCode, "Writter-1 expected to suceed!");

            // Writter-2 suceeded?
            Assert.AreEqual((int)HttpStatusCode.NoContent, results[1].HttpStatusCode, "Writter-2 expected to suceed!");

            // Writter-2 upsert succeeded
            SampleRTableEntity upsertedEntity = this.rtableWrapper.ReadEntity(entityPartitionKey, entityRowKey);

            Assert.NotNull(upsertedEntity, "Writter-2 upsert succeeded");
            Assert.AreEqual(upsertedEntity.Message, string.Format("upsert message {0}", 1), "Writter-2 upsert succeeded");
        }
コード例 #19
0
        public void TableQueryableCreateQueryNoPartitionKey()
        {
            Thread.Sleep(10000);

            string          tableName   = this.GenerateRandomTableName();
            ReplicatedTable localRTable = new ReplicatedTable(tableName, this.configurationService);

            localRTable.CreateIfNotExists();
            RTableWrapperForSampleRTableEntity localRTableWrapper = RTableWrapperForSampleRTableEntity.GetRTableWrapper(localRTable);

            CloudTableClient tableClient = localRTable.GetTailTableClient();
            CloudTable       table       = tableClient.GetTableReference(localRTable.TableName);

            string pk = "0";

            try
            {
                try
                {
                    TableBatchOperation batch = new TableBatchOperation();

                    for (int j = 0; j < 10; j++)
                    {
                        BaseEntity ent = GenerateRandomEntity(pk);
                        ent.RowKey = string.Format("{0:0000}", j);
                        batch.Insert(ent);
                    }

                    localRTable.ExecuteBatch(batch);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception during test case init {0}", ex.ToString());
                    throw;
                }

                try
                {
                    pk = "1";
                    TableBatchOperation batch = new TableBatchOperation();

                    for (int j = 0; j < 10; j++)
                    {
                        BaseEntity ent = GenerateRandomEntity(pk);
                        ent.RowKey = string.Format("{0:0000}", j);
                        batch.Insert(ent);
                    }

                    localRTable.ExecuteBatch(batch);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception during test case init {0}", ex.ToString());
                    throw;
                }

                IQueryable <BaseEntity> tableQuery  = table.CreateQuery <BaseEntity>();
                IQueryable <BaseEntity> rtableQuery = localRTable.CreateQuery <BaseEntity>();

                var list = tableQuery.AsEnumerable();

                int tableCount  = 0;
                int rtableCount = 0;
                foreach (BaseEntity ent in list)
                {
                    tableCount++;
                }
                foreach (BaseEntity ent in rtableQuery.ToList())
                {
                    rtableCount++;

                    Assert.IsTrue(ent.ETag != ent._rtable_Version.ToString(), "ETag is not virtualized when using CreateQuery()");
                }

                Assert.IsTrue(tableCount == rtableCount, "Query counts are different");
                Assert.IsTrue(tableCount == 20, "Query counts are different");

                // But, with "CreateReplicatedQuery" ETag is virtualized
                IQueryable <BaseEntity> virtualizedRtableQuery = localRTable.CreateReplicatedQuery <BaseEntity>();

                foreach (BaseEntity ent in virtualizedRtableQuery.ToList())
                {
                    Assert.IsTrue(ent._rtable_Version == 0);
                    Assert.IsTrue(ent.ETag == ent._rtable_Version.ToString(), "ETag is virtualized when using CreateReplicatedQuery()");

                    ent.A += "`";

                    // Update should go fine since ETag is virtualized
                    TableOperation operation = TableOperation.Replace(ent);
                    TableResult    result    = localRTable.Execute(operation);
                    Assert.IsTrue(result != null && result.HttpStatusCode == (int)HttpStatusCode.NoContent);
                }

                virtualizedRtableQuery = localRTable.CreateReplicatedQuery <BaseEntity>();

                foreach (BaseEntity ent in virtualizedRtableQuery.ToList())
                {
                    Assert.IsTrue(ent._rtable_Version == 1);
                    Assert.IsTrue(ent.ETag == ent._rtable_Version.ToString(), "ETag is virtualized when using CreateReplicatedQuery()");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error during query processing: {0}", e.ToString());
            }
            finally
            {
                localRTable.DeleteIfExists();
            }
        }
コード例 #20
0
        public void TxnViewExpiresAndViewIdHasChanged()
        {
            // Insert an entry
            string entityPartitionKey = "jobType-ReplaceWhenTxnViewExpires";
            string entityRowKey       = "jobType-ReplaceWhenTxnViewExpires";

            this.ForceDeleteEntryFromStorageTablesDirectly(entityPartitionKey, entityRowKey);

            Console.WriteLine("Inserting entry ...");
            var entry = new SampleRTableEntity(entityPartitionKey, entityRowKey, "insert message");

            this.rtableWrapper.InsertRow(entry);

            // Retrieve the config ...
            ReplicatedTableConfiguration    config;
            ReplicatedTableQuorumReadResult readStatus = this.configurationService.RetrieveConfiguration(out config);

            Assert.IsTrue(readStatus.Code == ReplicatedTableQuorumReadCode.Success);

            // Reconfigure RTable config with a short LeaseDuration
            int leaseDurationInSec = 3;

            config.LeaseDuration = leaseDurationInSec;
            this.configurationService.UpdateConfiguration(config);

            string accountNameToTamper = this.rtableTestConfiguration.StorageInformation.AccountNames[0];

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            // Delay behavior
            ProxyBehavior[] behaviors = new[]
            {
                DelayBehaviors.DelayAllResponsesIf(
                    1,
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Delay lock of the Head response enough so the txView expires ...
                    // And, upload a new config with a new viewId
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"_rtable_Operation\":\"Replace\"") &&
                        body.Contains("\"_rtable_RowLock\":true"))
                    {
                        // Upload a new config with a new ViewId
                        View view = this.configurationService.GetTableView(this.repTable.TableName);
                        config.GetView(view.Name).ViewId++;
                        this.configurationService.UpdateConfiguration(config, false);

                        // ensure txView expires
                        Thread.Sleep(2 * leaseDurationInSec * 1000);
                        return(true);
                    }

                    return(false);
                })),
            };

            // Launch an Update
            using (new HttpMangler(false, behaviors))
            {
                try
                {
                    entry         = this.rtableWrapper.ReadEntity(entityPartitionKey, entityRowKey);
                    entry.Message = "update message";

                    var            table            = new ReplicatedTable(this.repTable.TableName, this.configurationService);
                    TableOperation replaceOperation = TableOperation.Replace(entry);

                    // Get a snapshot of txView before transaction starts ...
                    View viewBeforeTx = this.configurationService.GetTableView(this.repTable.TableName);

                    // Transaction will get delayed 2*LeaseDuration sec. and Config/ViewId will be changed
                    TableResult replaceResult = table.Execute(replaceOperation);

                    Assert.IsTrue(viewBeforeTx.IsExpired(), "txView expected to expire!");
                    Assert.AreNotEqual(this.configurationService.GetTableView(this.repTable.TableName).ViewId, viewBeforeTx.ViewId, "ViewId should have changed!");

                    Assert.IsNotNull(replaceResult, "upsertResult = null");
                    Assert.AreNotEqual((int)HttpStatusCode.NoContent, replaceResult.HttpStatusCode, "upsertResult.HttpStatusCode mismatch");
                }
                catch (ReplicatedTableStaleViewException ex)
                {
                    // ValidateTxnView is called form everywhere ... we may endup here too
                    switch (ex.ErrorCode)
                    {
                    case ReplicatedTableViewErrorCodes.ViewIdChanged:
                        break;

                    default:
                        Console.WriteLine(ex);
                        Assert.IsTrue(false);
                        break;
                    }
                }
            }
        }
コード例 #21
0
        public void InsertInStaleViewConflictingWithInsertInNewView()
        {
            ReplicatedTableConfigurationServiceV2 configServiceOne, configServiceTwo;

            // setup:        Acc0       Acc1
            //  stale view = [None] ->  [RW]
            //  new view   = [WO]   ->  [RW]
            SetupStaleViewAndNewView(out configServiceOne, out configServiceTwo);

            long staleViewId  = configServiceOne.GetTableView(this.repTable.TableName).ViewId;
            long latestViewId = configServiceTwo.GetTableView(this.repTable.TableName).ViewId;

            string firstName = "FirstName01";
            string lastName  = "LastName01";

            var workerOne = new ReplicatedTable(this.repTable.TableName, configServiceOne);
            var workerTwo = new ReplicatedTable(this.repTable.TableName, configServiceTwo);

            string accountNameToTamper = this.rtableTestConfiguration.StorageInformation.AccountNames[1];

            Console.WriteLine("RunHttpManglerBehaviorHelper(): accountNameToTamper={0}", accountNameToTamper);

            TableResult oldInsertResult          = null;
            TableResult newInsertResult          = null;
            bool        triggerInsertWithNewView = false;
            bool        oldInsertResume          = false;

            // Start new newInsertTask in wait
            var newInsertTask = Task.Run(() =>
            {
                while (!triggerInsertWithNewView)
                {
                    Thread.Sleep(100);
                }

                /*
                 * 2 - Executes after step 1 below:
                 *     WorkerTwo => Insert a new row using new view
                 */
                var customer   = new CustomerEntity(firstName, lastName);
                customer.Email = "workerTwo";

                TableOperation operation = TableOperation.Insert(customer);
                newInsertResult          = workerTwo.Execute(operation);

                // Signal old Insert to resume
                oldInsertResume = true;
            });


            // Delay behavior
            ProxyBehavior[] behaviors =
            {
                TamperBehaviors.TamperAllRequestsIf(
                    (session =>
                {
                    // => trigger new insert to start
                    triggerInsertWithNewView = true;

                    // Delaying commit to the Tail by stale view Update
                    while (!oldInsertResume)
                    {
                        Thread.Sleep(100);
                    }
                }),
                    (session =>
                {
                    var body = session.GetRequestBodyAsString();

                    // Commit to Tail by stale view
                    if (session.hostname.Contains(accountNameToTamper + ".") &&
                        session.HTTPMethodIs("PUT") &&
                        body.Contains("\"Email\":\"workerOne\"") &&
                        body.Contains(string.Format("\"_rtable_ViewId\":\"{0}\"", staleViewId)) &&
                        body.Contains("\"_rtable_RowLock\":false"))
                    {
                        return(true);
                    }

                    return(false);
                })),
            };

            /*
             * 1 - WorkerOne => update an entry using the stale View
             */
            using (new HttpMangler(false, behaviors))
            {
                var customer = new CustomerEntity(firstName, lastName);
                customer.Email = "workerOne";

                TableOperation operation = TableOperation.Insert(customer);
                oldInsertResult = workerOne.Execute(operation);
            }

            // Wait on new Insert to finish
            newInsertTask.Wait();


            // Expected behavior:

            // Thread_1 (stale ViewId)
            Assert.IsNotNull(oldInsertResult, "oldInsertResult = null");
            Assert.AreEqual((int)HttpStatusCode.NoContent, oldInsertResult.HttpStatusCode, "oldInsertResult.HttpStatusCode mismatch");
            Console.WriteLine("Insert in stale View Succeeded with HttpStatus={0}", oldInsertResult.HttpStatusCode);

            // Thread_2 (new ViewId)
            Assert.IsNotNull(newInsertResult, "newInsertResult = null");
            Assert.AreEqual((int)HttpStatusCode.Conflict, newInsertResult.HttpStatusCode, "newUpdateResult.HttpStatusCode mismatch");
            Console.WriteLine("Insert in new View failed with HttpStatus={0}", newInsertResult.HttpStatusCode);

            var currCustomer = RetrieveCustomer(firstName, lastName, workerTwo);

            Assert.AreEqual(staleViewId, currCustomer._rtable_ViewId, "customer._rtable_ViewId mismatch");
            Assert.AreEqual("workerOne", currCustomer.Email, "customer.Email mismatch");

            Console.WriteLine("workerOne write Succeeded");
            Console.WriteLine("workerTwo got Conflict because row was lock.");
        }