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"); } }
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(); } }
private IEnumerable <CustomerEntity> GetCustomerEntities(ReplicatedTable rtable) { ReplicatedTableQuery <CustomerEntity> query = rtable.CreateReplicatedQuery <CustomerEntity>(); return(query.AsEnumerable()); }