public void TestThrowIfChainIsNotValid() { Exception exception; var conf = new ReplicatedTableConfigurationStoreAccessor(); /* RULE 1: * ======= * Read replicas rule: * - [R] replicas are contiguous from Tail backwards * - [R] replica count >= 1 */ /* RULE 2: * ======= * Write replicas rule: * - [W] replicas are contiguous from Head onwards * - [W] replica count = 0 or = ChainLength */ #region *** RULE 1 : [R] -> [R] ... [R] -> [R] (at least one Readable and no Writtable replicas) *** // Empty chain is ok! conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ThrowIfChainIsNotValid("view1"); // - Chain not empty and not ([R] replica count >= 1) conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.WriteOnly, }); exception = TestHelper.ExpectedException <Exception>(() => conf.ThrowIfChainIsNotValid("view1"), "we need to have at least one Readable replica"); Assert.IsTrue(exception.Message == string.Format("View:\'{0}\' has invalid Read chain:\'{1}\' !!!", "view1", "W")); // One ReadOnly replicas conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ThrowIfChainIsNotValid("view1"); // Only ReadOnly replicas conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ThrowIfChainIsNotValid("view1"); // [R] replicas not contiguous from Tail backward conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.WriteOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); exception = TestHelper.ExpectedException <Exception>(() => conf.ThrowIfChainIsNotValid("view1"), "Replicas must be not Writable or All are Wraitable"); Assert.IsTrue(exception.Message == string.Format("View:\'{0}\' has invalid Read chain:\'{1}\' !!!", "view1", "RWRR")); // [R] replicas not contiguous from Tail backward conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.WriteOnly, }); exception = TestHelper.ExpectedException <Exception>(() => conf.ThrowIfChainIsNotValid("view1"), "Replicas must be not Writable or All are Wraitable"); Assert.IsTrue(exception.Message == string.Format("View:\'{0}\' has invalid Read chain:\'{1}\' !!!", "view1", "RW")); #endregion #region *** RULE 2 : [W] -> [W] ... [RW] -> [RW] (at least one Readable and all Writtable replica) *** // 1 Writable replica, but the chain length is 4 and all Readable conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadWrite, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, }); exception = TestHelper.ExpectedException <Exception>(() => conf.ThrowIfChainIsNotValid("view1"), "Replicas must be not Writable or All are Wraitable"); Assert.IsTrue(exception.Message == string.Format("View:\'{0}\' has invalid Write chain:\'{1}\' !!!", "view1", "RWRR")); // All replica are ReadWritable conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadWrite, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadWrite, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadWrite, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadWrite, }); conf.ThrowIfChainIsNotValid("view1"); #endregion }
public void TestEnableReadWriteOnReplica() { var conf = new ReplicatedTableConfigurationStoreAccessor(); // we assume the replica chain abides by RTable requirements: // RULE 1 : [R] -> [R] ... [R] -> [R] (at least one Readable and no Writtable replicas) // RULE 2 : [W] -> [W] ... [RW] -> [RW] (at least one Readable and all Writtable replica) // - Empty replica , nothing to do ... conf.ViewId = 6; conf.EnableReadWriteOnReplica("view1", "replicaX"); Assert.IsTrue(conf.ViewId == 6); Assert.IsFalse(conf.ReplicaChain.Any()); // Not the head of the chain: => nothing to do [R1 -> R2*] conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ViewId = 6; conf.EnableReadWriteOnReplica("view1", "R2"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.None); // the head of the chain, but not WriteOnly mode => nothing to do [R1 -> R2*] conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ViewId = 6; conf.EnableReadWriteOnReplica("view1", "R1"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.None); // variant: the head of the chain, but not WriteOnly mode => nothing to do [R1* -> R2*] conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ViewId = 6; conf.EnableReadWriteOnReplica("view1", "R1"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.ReadOnly); // variant: the head of the chain, and WriteOnly mode => make RW [R1* -> R2*] conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.WriteOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); conf.ViewId = 6; conf.EnableReadWriteOnReplica("view1", "R1"); Assert.IsTrue(conf.ViewId == 7); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.ReadWrite); }
public void TestMoveReplicaToHeadAndSetViewToReadOnly() { var conf = new ReplicatedTableConfigurationStoreAccessor(); // we assume the replica chain abides by RTable requirements: // RULE 1 : [R] -> [R] ... [R] -> [R] (at least one Readable and no Writtable replicas) // RULE 2 : [W] -> [W] ... [RW] -> [RW] (at least one Readable and all Writtable replica) // RULE 1: conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R3", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R4", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); // - Replica not found => nothing to do [R1 -> R2* -> R3 -> R4*] conf.ViewId = 6; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "replicaX"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // - Replica found and was None => move replica to head [R3 -> R1 -> R2* -> R4*] conf.ViewId = 6; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R3"); Assert.IsTrue(conf.ViewId == 7); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R3"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // - Replica found but was ReadOnly => make replica None and move it to head [R4 -> R3 -> R1 -> R2*] conf.ViewId = 9; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R4"); Assert.IsTrue(conf.ViewId == 10); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R4"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 3); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // RULE 2: conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.WriteOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R3", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R4", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); // - Replica not found => nothing to do [R1 -> R2* -> R3 -> R4*] conf.ViewId = 5; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "replicaX"); Assert.IsTrue(conf.ViewId == 5); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 2); // - Replica found and was None // But this call fails becasuse we can't change replica R2 from WriteOnly to ReadOnly ! // preserve the chain as is [R1 -> R2* -> R3 -> R4*] Exception exception; conf.ViewId = 5; exception = TestHelper.ExpectedException <Exception>(() => conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R3"), "we can't change replica from WriteOnly to ReadOnly"); Assert.IsTrue(exception.Message == string.Format("View:\'{0}\' : can't set a WriteOnly replica to ReadOnly !!!", "view1")); Assert.IsTrue(conf.ViewId == 5); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 2); // - Replica found and was RW // But this call fails becasuse we can't change replica R2 from WriteOnly to ReadOnly ! // preserve the chain as is [R1 -> R2* -> R3 -> R4*] conf.ViewId = 5; exception = TestHelper.ExpectedException <Exception>(() => conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R4"), "we can't change replica from WriteOnly to ReadOnly"); Assert.IsTrue(exception.Message == string.Format("View:\'{0}\' : can't set a WriteOnly replica to ReadOnly !!!", "view1")); Assert.IsTrue(conf.ViewId == 5); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 2); // - Replica found and was WO => preserve the chain as is [R2 -> R1 -> R3 -> R4*] conf.ViewId = 5; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R2"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R2"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 3); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // - Replica found and was WriteOnly => move the replica to the head [R2 -> R3 -> R1 -> R4*] conf.ViewId = 9; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R2"); Assert.IsTrue(conf.ViewId == 10); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R2"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 3); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // RULE 2: variant ... conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R3", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R4", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); // - Replica found and was None => move the replica to the head [R3 -> R1 -> R2* -> R4*] conf.ViewId = 5; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R3"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R3"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsReadable()) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // RULE 2: variant ... conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R3", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R4", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); // - Replica found and was RW => move the replica to the head [R2 -> R1 -> R3 -> R4*] conf.ViewId = 5; conf.MoveReplicaToHeadAndSetViewToReadOnly("view1", "R2"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R2"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 3); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsReadable()) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); }
public void TestEnableWriteOnReplicas() { var conf = new ReplicatedTableConfigurationStoreAccessor(); // we assume the replica chain abides by RTable requirements: // RULE 1 : [R] -> [R] ... [R] -> [R] (at least one Readable and no Writtable replicas) // RULE 2 : [W] -> [W] ... [RW] -> [RW] (at least one Readable and all Writtable replica) // - Empty replica , nothing to do ... conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "replicaX"); Assert.IsTrue(conf.ViewId == 6); Assert.IsFalse(conf.ReplicaChain.Any()); // RULE 1: conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R3", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R4", Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); // - Replica not found => nothing to do [R1 -> R2* -> R3 -> R4*] conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "replicaX"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // - Replica found but not at the head => nothing to do [R1 -> R2* -> R3 -> R4*] conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "R2"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "R3"); Assert.IsTrue(conf.ViewId == 6); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 0); // - Replica found at the head => enable writting on all replicas [R1* -> R2* -> R3 -> R4*] conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "R1"); Assert.IsTrue(conf.ViewId == 7); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.WriteOnly); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 3); // RULE 2: // for example [R1(none) -> R2(wo) -> R3(none) -> R4(rw)] which is a valid chain // in reality, we would not end-up calling this function with such chain, ... that is enforced by the business logic (i.e. TurnOn replica). // this function doesn't control how or in which order it is being called. // so here we are just checking the expected behavior. // at the end, we should endup with a valid chain as well. conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.WriteOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R3", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R4", Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); // - Replica found at the head => enable writting on all replicas [R1(w) -> R2(rw) -> R3(none) -> R4(rw)] conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "R1"); Assert.IsTrue(conf.ViewId == 7); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.WriteOnly); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 2); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 3); //- Single-replica scenario [R1(none) -> R2(none)] conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R1", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { StorageAccountName = "R2", Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); // - Replica found at the head => enable RW on the replica [R1(rw) -> R2(none)] conf.ViewId = 6; conf.EnableWriteOnReplicas("view1", "R1"); Assert.IsTrue(conf.ViewId == 7); Assert.IsTrue(conf.ReplicaChain[0].StorageAccountName == "R1"); Assert.IsTrue(conf.ReplicaChain[0].Status == ReplicaStatus.ReadWrite); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.None) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadOnly) == 0); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.ReadWrite) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.IsWritable()) == 1); }
public void TestSanitizeWithCurrentView() { var conf = new ReplicatedTableConfigurationStoreAccessor { ViewId = 0, }; var view = new View("view empty") { ViewId = 9, }; // - conf view is 0, and we pass a view which is empty // => conf view is set to 1 DateTime old = conf.Timestamp; Thread.Sleep(100); Assert.IsTrue(conf.ViewId == 0); Assert.IsTrue(view.IsEmpty); conf.SanitizeWithCurrentView(view); Assert.IsTrue(conf.ViewId == 1 && conf.Timestamp > old); // - conf view != 0, and we pass a view which is empty // => conf view keeps its value conf.ViewId = 3; old = conf.Timestamp; Thread.Sleep(100); Assert.IsTrue(conf.ViewId != 0); Assert.IsTrue(view.IsEmpty); conf.SanitizeWithCurrentView(view);// the head of the chain, but not WriteOnly mode => nothing to do [R1 -> R2*] Assert.IsTrue(conf.ViewId == 3 && conf.Timestamp > old); // - conf view is 0, and we pass a view not empty // => conf view is set to the view.viewId + 1 conf.ViewId = 0; view.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(new ReplicaInfo(), null)); old = conf.Timestamp; Thread.Sleep(100); Assert.IsTrue(conf.ViewId == 0); Assert.IsFalse(view.IsEmpty); conf.SanitizeWithCurrentView(view); Assert.IsTrue(conf.ViewId == view.ViewId + 1 && conf.Timestamp > old); // - conf view != 0, and we pass a view not empty // => conf view keeps its value conf.ViewId = 6; view.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(new ReplicaInfo(), null)); old = conf.Timestamp; Thread.Sleep(100); Assert.IsTrue(conf.ViewId != 0); Assert.IsFalse(view.IsEmpty); conf.SanitizeWithCurrentView(view); Assert.IsTrue(conf.ViewId == 6 && conf.Timestamp > old); // - Now add replicas to the config // we assume the replica chain abides by RTable requirements: // RULE 1 : [R] -> [R] ... [R] -> [R] (at least one Readable and no Writtable replicas) // RULE 2 : [W] -> [W] ... [RW] -> [RW] (at least one Readable and all Writtable replica) // RULE 1: conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadOnly, ViewInWhichAddedToChain = 3, }); // - only WriteOnly replica should be updated conf.ViewId = 6; conf.SanitizeWithCurrentView(view); Assert.IsTrue(conf.ReplicaChain.Count(r => r.ViewInWhichAddedToChain == 3) == 4); // RULE 2: conf.ReplicaChain.Clear(); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.WriteOnly, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.None, ViewInWhichAddedToChain = 3, }); conf.ReplicaChain.Add(new ReplicaInfo { Status = ReplicaStatus.ReadWrite, ViewInWhichAddedToChain = 3, }); // - only WriteOnly replica should be updated conf.ViewId = 6; conf.SanitizeWithCurrentView(view); Assert.IsTrue(conf.ReplicaChain.Count(r => r.Status == ReplicaStatus.WriteOnly && r.ViewInWhichAddedToChain == 6) == 1); Assert.IsTrue(conf.ReplicaChain.Count(r => r.ViewInWhichAddedToChain == 3) == 3); }