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");
            }
        }
        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");
            }
        }
        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'");
            }
        }
        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");
            }
        }
        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");
            }
        }
Пример #6
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();
            }
        }
Пример #7
0
        private IEnumerable <CustomerEntity> GetCustomerEntities(ReplicatedTable rtable)
        {
            ReplicatedTableQuery <CustomerEntity> query = rtable.CreateReplicatedQuery <CustomerEntity>();

            return(query.AsEnumerable());
        }