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 !"); } }