Search in sources :

Example 1 with UnsafeGossipHelper

use of org.apache.cassandra.distributed.impl.UnsafeGossipHelper in project cassandra by apache.

the class CASTest method testSuccessfulWriteDuringRangeMovementFollowedByConflicting.

/**
 * Successful write during range movement not witnessed by write after range movement
 *
 *  - Range moves from {1, 2, 3} to {2, 3, 4}; witnessed by X (not by !X)
 *  -  !X: Prepare and Propose to {1, 2}
 *  - Range movement witnessed by !X
 *  - Any: Prepare and Propose to {3, 4}
 */
@Ignore
@Test
public void testSuccessfulWriteDuringRangeMovementFollowedByConflicting() throws Throwable {
    try (Cluster cluster = Cluster.create(4, config -> config.set("write_request_timeout", REQUEST_TIMEOUT).set("cas_contention_timeout", CONTENTION_TIMEOUT))) {
        cluster.schemaChange("CREATE KEYSPACE " + KEYSPACE + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};");
        cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v1 int, v2 int, PRIMARY KEY (pk, ck))");
        // make it so {4} is bootstrapping, and this has not propagated to other nodes yet
        for (int i = 1; i <= 4; ++i) cluster.get(1).acceptsOnInstance(UnsafeGossipHelper::removeFromRing).accept(cluster.get(4));
        cluster.get(4).acceptsOnInstance(UnsafeGossipHelper::addToRingBootstrapping).accept(cluster.get(4));
        int pk = pk(cluster, 1, 2);
        // {1} promises and accepts on !{3} => {1, 2}; commits on !{2, 3} => {1}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_COMMIT_REQ.id).from(1).to(2, 3).drop();
        assertRows(cluster.coordinator(1).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1) VALUES (?, 1, 1) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(true));
        // finish topology change
        for (int i = 1; i <= 4; ++i) cluster.get(i).acceptsOnInstance(UnsafeGossipHelper::addToRingNormal).accept(cluster.get(4));
        // {3} reads from !{2} => {3, 4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(3).to(2).drop();
        assertRows(cluster.coordinator(3).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v2) VALUES (?, 1, 2) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(false, pk, 1, 1, null));
    // TODO: repair and verify base table state
    }
}
Also used : UnsafeGossipHelper(org.apache.cassandra.distributed.impl.UnsafeGossipHelper) Cluster(org.apache.cassandra.distributed.Cluster) Ignore(org.junit.Ignore) Test(org.junit.Test)

Example 2 with UnsafeGossipHelper

use of org.apache.cassandra.distributed.impl.UnsafeGossipHelper in project cassandra by apache.

the class CASTest method testIncompleteWriteFollowedBySuccessfulWriteWithStaleRingDuringRangeMovementFollowedByWrite.

/**
 * During a range movement, a CAS may fail leaving side effects that are not witnessed by another operation
 * being performed with stale ring information.
 * This is a particular special case of stale ring information sequencing, which probably would be resolved
 * by fixing each of the more isolated cases (but is unique, so deserving of its own test case).
 * See CASSANDRA-15745
 *
 *  - Range moves from {1, 2, 3} to {2, 3, 4}; witnessed by X (not by !X)
 *  -   X: Prepare to {2, 3, 4}
 *  -   X: Propose to {4}
 *  -  !X: Prepare and Propose to {1, 2}
 *  - Range move visible by !X
 *  - Any: Prepare and Propose to {3, 4}
 */
@Ignore
@Test
public void testIncompleteWriteFollowedBySuccessfulWriteWithStaleRingDuringRangeMovementFollowedByWrite() throws Throwable {
    try (Cluster cluster = Cluster.create(4, config -> config.set("write_request_timeout", REQUEST_TIMEOUT).set("cas_contention_timeout", CONTENTION_TIMEOUT))) {
        cluster.schemaChange("CREATE KEYSPACE " + KEYSPACE + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};");
        cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v1 int, v2 int, PRIMARY KEY (pk, ck))");
        // make it so {4} is bootstrapping, and this has not propagated to other nodes yet
        for (int i = 1; i <= 4; ++i) cluster.get(1).acceptsOnInstance(UnsafeGossipHelper::removeFromRing).accept(cluster.get(4));
        cluster.get(4).acceptsOnInstance(UnsafeGossipHelper::addToRingBootstrapping).accept(cluster.get(4));
        int pk = pk(cluster, 1, 2);
        // {4} promises and accepts on !{1} => {2, 3, 4}; commits on !{1, 2, 3} => {4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(4).to(1).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(4).to(1, 2, 3).drop();
        try {
            cluster.coordinator(4).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1) VALUES (?, 1, 1) IF NOT EXISTS", ConsistencyLevel.QUORUM, pk);
            Assert.assertTrue(false);
        } catch (RuntimeException wrapped) {
            Assert.assertEquals("Operation timed out - received only 1 responses.", wrapped.getCause().getMessage());
        }
        // {1} promises and accepts on !{3} => {1, 2}; commits on !{2, 3} => {1}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_COMMIT_REQ.id).from(1).to(2, 3).drop();
        assertRows(cluster.coordinator(1).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v2) VALUES (?, 1, 2) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(true));
        // finish topology change
        for (int i = 1; i <= 4; ++i) cluster.get(i).acceptsOnInstance(UnsafeGossipHelper::addToRingNormal).accept(cluster.get(4));
        // {3} reads from !{2} => {3, 4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(3).to(2).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(3).to(2).drop();
        assertRows(cluster.coordinator(3).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v2) VALUES (?, 1, 2) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(false, 5, 1, null, 2));
    }
}
Also used : UnsafeGossipHelper(org.apache.cassandra.distributed.impl.UnsafeGossipHelper) Cluster(org.apache.cassandra.distributed.Cluster) Ignore(org.junit.Ignore) Test(org.junit.Test)

Example 3 with UnsafeGossipHelper

use of org.apache.cassandra.distributed.impl.UnsafeGossipHelper in project cassandra by apache.

the class CASTest method testSucccessfulWriteDuringRangeMovementFollowedByRead.

/**
 * Successful write during range movement, not witnessed by read after range movement.
 * Very similar to {@link #testConflictingWritesWithStaleRingInformation}.
 *
 *  - Range moves from {1, 2, 3} to {2, 3, 4}; witnessed by X (not by !X)
 *  -  !X: Prepare and Propose to {1, 2}
 *  - Range movement witnessed by !X
 *  - Any: Prepare and Read from {3, 4}
 */
@Ignore
@Test
public void testSucccessfulWriteDuringRangeMovementFollowedByRead() throws Throwable {
    try (Cluster cluster = Cluster.create(4, config -> config.set("write_request_timeout", REQUEST_TIMEOUT).set("cas_contention_timeout", CONTENTION_TIMEOUT))) {
        cluster.schemaChange("CREATE KEYSPACE " + KEYSPACE + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};");
        cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v int, PRIMARY KEY (pk, ck))");
        // make it so {4} is bootstrapping, and this has not propagated to other nodes yet
        for (int i = 1; i <= 4; ++i) cluster.get(1).acceptsOnInstance(UnsafeGossipHelper::removeFromRing).accept(cluster.get(4));
        cluster.get(4).acceptsOnInstance(UnsafeGossipHelper::addToRingBootstrapping).accept(cluster.get(4));
        int pk = pk(cluster, 1, 2);
        // {1} promises and accepts on !{3} => {1, 2}; commmits on !{2, 3} => {1}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_COMMIT_REQ.id).from(1).to(2, 3).drop();
        assertRows(cluster.coordinator(1).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v) VALUES (?, 1, 1) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(true));
        // finish topology change
        for (int i = 1; i <= 4; ++i) cluster.get(i).acceptsOnInstance(UnsafeGossipHelper::addToRingNormal).accept(cluster.get(4));
        // {3} reads from !{2} => {3, 4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(3).to(2).drop();
        assertRows(cluster.coordinator(3).execute("SELECT * FROM " + KEYSPACE + ".tbl WHERE pk = ?", ConsistencyLevel.SERIAL, pk), row(pk, 1, 1));
    }
}
Also used : UnsafeGossipHelper(org.apache.cassandra.distributed.impl.UnsafeGossipHelper) Cluster(org.apache.cassandra.distributed.Cluster) Ignore(org.junit.Ignore) Test(org.junit.Test)

Example 4 with UnsafeGossipHelper

use of org.apache.cassandra.distributed.impl.UnsafeGossipHelper in project cassandra by apache.

the class CASTest method testSuccessfulWriteBeforeRangeMovement.

/**
 * Failed write (by node that did not yet witness a range movement via gossip) is witnessed later as successful
 * conflicting with another successful write performed by a node that did witness the range movement
 * Prepare, Propose and Commit A to {1, 2}
 * Range moves to {2, 3, 4}
 * Prepare and Propose B (=> !A) to {3, 4}
 */
@Ignore
@Test
public void testSuccessfulWriteBeforeRangeMovement() throws Throwable {
    try (Cluster cluster = Cluster.create(4, config -> config.set("write_request_timeout", REQUEST_TIMEOUT).set("cas_contention_timeout", CONTENTION_TIMEOUT))) {
        cluster.schemaChange("CREATE KEYSPACE " + KEYSPACE + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};");
        cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v1 int, v2 int, PRIMARY KEY (pk, ck))");
        // make it so {1} is unaware (yet) that {4} is an owner of the token
        cluster.get(1).acceptsOnInstance(UnsafeGossipHelper::removeFromRing).accept(cluster.get(4));
        int pk = pk(cluster, 1, 2);
        // {1} promises and accepts on !{3} => {1, 2}; commits on !{2,3} => {1}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_COMMIT_REQ.id).from(1).to(2, 3).drop();
        assertRows(cluster.coordinator(1).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1) VALUES (?, 1, 1) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(true));
        for (int i = 1; i <= 3; ++i) cluster.get(i).acceptsOnInstance(UnsafeGossipHelper::addToRingNormal).accept(cluster.get(4));
        // {4} reads from !{2} => {3, 4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(4).to(2).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(4).to(2).drop();
        assertRows(cluster.coordinator(4).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v2) VALUES (?, 1, 2) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(false, pk, 1, 1, null));
    }
}
Also used : UnsafeGossipHelper(org.apache.cassandra.distributed.impl.UnsafeGossipHelper) Cluster(org.apache.cassandra.distributed.Cluster) Ignore(org.junit.Ignore) Test(org.junit.Test)

Example 5 with UnsafeGossipHelper

use of org.apache.cassandra.distributed.impl.UnsafeGossipHelper in project cassandra by apache.

the class CASTest method testIncompleteWriteFollowedBySuccessfulWriteWithStaleRingDuringRangeMovementFollowedByRead.

/**
 * During a range movement, a CAS may fail leaving side effects that are not witnessed by another operation
 * being performed with stale ring information.
 * This is a particular special case of stale ring information sequencing, which probably would be resolved
 * by fixing each of the more isolated cases (but is unique, so deserving of its own test case).
 * See CASSANDRA-15745
 *
 *  - Range moves from {1, 2, 3} to {2, 3, 4}; witnessed by X (not by !X)
 *  -   X: Prepare to {2, 3, 4}
 *  -   X: Propose to {4}
 *  -  !X: Prepare and Propose to {1, 2}
 *  - Range move visible by !X
 *  - Any: Prepare and Read from {3, 4}
 */
@Ignore
@Test
public void testIncompleteWriteFollowedBySuccessfulWriteWithStaleRingDuringRangeMovementFollowedByRead() throws Throwable {
    try (Cluster cluster = Cluster.create(4, config -> config.set("write_request_timeout", REQUEST_TIMEOUT).set("cas_contention_timeout", CONTENTION_TIMEOUT))) {
        cluster.schemaChange("CREATE KEYSPACE " + KEYSPACE + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};");
        cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v1 int, v2 int, PRIMARY KEY (pk, ck))");
        // make it so {4} is bootstrapping, and this has not propagated to other nodes yet
        for (int i = 1; i <= 4; ++i) cluster.get(1).acceptsOnInstance(UnsafeGossipHelper::removeFromRing).accept(cluster.get(4));
        cluster.get(4).acceptsOnInstance(UnsafeGossipHelper::addToRingBootstrapping).accept(cluster.get(4));
        int pk = pk(cluster, 1, 2);
        // {4} promises and accepts on !{1} => {2, 3, 4}; commits on !{1, 2, 3} => {4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(4).to(1).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(4).to(1, 2, 3).drop();
        try {
            cluster.coordinator(4).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1) VALUES (?, 1, 1) IF NOT EXISTS", ConsistencyLevel.QUORUM, pk);
            Assert.assertTrue(false);
        } catch (RuntimeException wrapped) {
            Assert.assertEquals("Operation timed out - received only 1 responses.", wrapped.getCause().getMessage());
        }
        // {1} promises and accepts on !{3} => {1, 2}; commits on !{2, 3} => {1}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(1).to(3).drop();
        cluster.filters().verbs(PAXOS_COMMIT_REQ.id).from(1).to(2, 3).drop();
        assertRows(cluster.coordinator(1).execute("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v2) VALUES (?, 1, 2) IF NOT EXISTS", ConsistencyLevel.ONE, pk), row(true));
        // finish topology change
        for (int i = 1; i <= 4; ++i) cluster.get(i).acceptsOnInstance(UnsafeGossipHelper::addToRingNormal).accept(cluster.get(4));
        // {3} reads from !{2} => {3, 4}
        cluster.filters().verbs(PAXOS_PREPARE_REQ.id, READ_REQ.id).from(3).to(2).drop();
        cluster.filters().verbs(PAXOS_PROPOSE_REQ.id).from(3).to(2).drop();
        assertRows(cluster.coordinator(3).execute("SELECT * FROM " + KEYSPACE + ".tbl WHERE pk = ?", ConsistencyLevel.SERIAL, pk), row(pk, 1, null, 2));
    }
}
Also used : UnsafeGossipHelper(org.apache.cassandra.distributed.impl.UnsafeGossipHelper) Cluster(org.apache.cassandra.distributed.Cluster) Ignore(org.junit.Ignore) Test(org.junit.Test)

Aggregations

Cluster (org.apache.cassandra.distributed.Cluster)5 UnsafeGossipHelper (org.apache.cassandra.distributed.impl.UnsafeGossipHelper)5 Ignore (org.junit.Ignore)5 Test (org.junit.Test)5