/*
* Copyright 2015 OrientDB LTD (info--at--orientdb.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.orientechnologies.orient.server.distributed.scenariotest;
import com.orientechnologies.common.util.OCallable;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.orientechnologies.orient.server.distributed.ODistributedConfiguration;
import com.orientechnologies.orient.server.distributed.ServerRun;
import com.orientechnologies.orient.server.hazelcast.OHazelcastPlugin;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* It checks the consistency in the cluster with the following scenario: - 3 servers (writeQuorum=majority) - record r1 (version x)
* is present in full replica on all the servers - server3 is isolated (simulated by: shutdown + opening plocal db) - update of r1
* on server3 succeeds, so we have r1* on server3 - server3 joins the cluster (restart) - shutdown server1 (so quorum for CRUD
* operation on r1 will not be reached) - delete request for r1 on server3: - quorum not reached because r1* on server3 is not
* consistent with r1 on server2 (different values and versions) - delete operation is aborted on server2 and is rolled back on
* server3 (resurrection) - restart server1 (so quorum for CRUD operation on r1 will be reached) - check consistency: r1 is still
* present on server1 and server2, and r1* is present on server3. - delete request for r1 on server1: - quorum reached - check
* consistency: r1 is not present on server1 and server2, and r1* is not present on server3.
*
* @author Gabriele Ponzi
* @email <gabriele.ponzi--at--gmail.com>
*/
public class DeleteAndLazarusScenarioTest extends AbstractScenarioTest {
@Test
public void test() throws Exception {
maxRetries = 10;
init(SERVERS);
prepare(false);
execute();
}
@Override
public void executeTest() throws Exception {
/*
* Test with writeQuorum = majority
*/
banner("Test with writeQuorum = majority");
ODatabaseDocumentTx dbServer1 = poolFactory.get(getDatabaseURL(serverInstance.get(0)), "admin", "admin").acquire();
// changing configuration: readQuorum=2, autoDeploy=false
System.out.print("\nChanging configuration (autoDeploy=false)...");
ODocument cfg = null;
ServerRun server = serverInstance.get(2);
OHazelcastPlugin manager = (OHazelcastPlugin) server.getServerInstance().getDistributedManager();
ODistributedConfiguration databaseConfiguration = manager.getDatabaseConfiguration(getDatabaseName());
cfg = databaseConfiguration.getDocument();
System.out.println("\nConfiguration updated.");
// inserting record r1 and checking consistency on all the servers
try {
ODatabaseRecordThreadLocal.INSTANCE.set(dbServer1);
System.out.print("Inserting record r1...");
new ODocument("Person").fields("id", "R001", "firstName", "Luke", "lastName", "Skywalker").save();
System.out.println("Done.");
} catch (Exception e) {
e.printStackTrace();
fail("Record r1 not inserted!.");
}
waitForInsertedRecordPropagation("R001");
System.out.print("Checking consistency for record r1...");
ODocument r1onServer1 = retrieveRecord(getDatabaseURL(serverInstance.get(0)), "R001");
ODocument r1onServer2 = retrieveRecord(getDatabaseURL(serverInstance.get(1)), "R001");
ODocument r1onServer3 = retrieveRecord(getDatabaseURL(serverInstance.get(2)), "R001");
final ORecordId r1Rid = (ORecordId) r1onServer1.getIdentity();
assertEquals(r1onServer1.field("@version"), r1onServer2.field("@version"));
assertEquals(r1onServer1.field("id"), r1onServer2.field("id"));
assertEquals(r1onServer1.field("firstName"), r1onServer2.field("firstName"));
assertEquals(r1onServer1.field("lastName"), r1onServer2.field("lastName"));
assertEquals(r1onServer2.field("@version"), r1onServer3.field("@version"));
assertEquals(r1onServer2.field("id"), r1onServer3.field("id"));
assertEquals(r1onServer2.field("firstName"), r1onServer3.field("firstName"));
assertEquals(r1onServer2.field("lastName"), r1onServer3.field("lastName"));
System.out.println("\tDone.");
// initial version of the record r1
int initialVersion = r1onServer1.field("@version");
// isolating server3
System.out.println("Network fault on server3.\n");
simulateServerFault(serverInstance.get(2), "net-fault");
assertFalse(serverInstance.get(2).isActive());
// updating r1 in r1* on server3
banner("Updating r1* on server3 (isolated from the the cluster)");
ODatabaseDocumentTx dbServer3 = null;
try {
r1onServer3 = retrieveRecord(getPlocalDatabaseURL(serverInstance.get(2)), "R001");
dbServer3 = new ODatabaseDocumentTx(getPlocalDatabaseURL(serverInstance.get(2))).open("admin", "admin");
r1onServer3.field("firstName", "Darth");
r1onServer3.field("lastName", "Vader");
r1onServer3.save();
System.out.println(r1onServer3.getRecord().toString());
} catch (Exception e) {
e.printStackTrace();
fail();
}
// restarting server3
serverInstance.get(2).startServer(getDistributedServerConfiguration(serverInstance.get(SERVERS - 1)));
System.out.println("Server 3 restarted.");
assertTrue(serverInstance.get(2).isActive());
// reading r1* on server3
dbServer3 = poolFactory.get(getDatabaseURL(serverInstance.get(2)), "admin", "admin").acquire();
try {
r1onServer3 = retrieveRecord(getPlocalDatabaseURL(serverInstance.get(2)), "R001");
} catch (Exception e) {
e.printStackTrace();
} finally {
dbServer3.close();
}
// r1 was not modified both on server1 and server2
r1onServer1 = retrieveRecord(getDatabaseURL(serverInstance.get(0)), "R001");
r1onServer2 = retrieveRecord(getDatabaseURL(serverInstance.get(1)), "R001");
assertEquals(1, r1onServer1.field("@version"));
assertEquals("R001", r1onServer1.field("id"));
assertEquals("Luke", r1onServer1.field("firstName"));
assertEquals("Skywalker", r1onServer1.field("lastName"));
assertEquals(r1onServer1.field("@version"), r1onServer2.field("@version"));
assertEquals(r1onServer1.field("id"), r1onServer2.field("id"));
assertEquals(r1onServer1.field("firstName"), r1onServer2.field("firstName"));
assertEquals(r1onServer1.field("lastName"), r1onServer2.field("lastName"));
// checking we have different values for r1* on server3
assertEquals("R001", r1onServer3.field("id"));
assertEquals("Darth", r1onServer3.field("firstName"));
assertEquals("Vader", r1onServer3.field("lastName"));
assertEquals(initialVersion + 1, r1onServer3.field("@version"));
// shutdown server1
System.out.println("Network fault on server1.\n");
simulateServerFault(serverInstance.get(0), "net-fault");
assertFalse(serverInstance.get(0).isActive());
// delete request on server3 for r1*
dbServer3 = poolFactory.get(getDatabaseURL(serverInstance.get(2)), "admin", "admin").acquire();
try {
dbServer3.command(new OCommandSQL("delete from Person where @rid=#27:0")).execute();
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
dbServer3.close();
}
// restarting server1
serverInstance.get(0).startServer(getDistributedServerConfiguration(serverInstance.get(0)));
System.out.println("Server 1 restarted.");
assertTrue(serverInstance.get(0).isActive());
// r1 is still present both on server1 and server2
r1onServer1 = retrieveRecord(getDatabaseURL(serverInstance.get(0)), "R001");
r1onServer2 = retrieveRecord(getDatabaseURL(serverInstance.get(1)), "R001");
assertEquals(1, r1onServer1.field("@version"));
assertEquals("R001", r1onServer1.field("id"));
assertEquals("Luke", r1onServer1.field("firstName"));
assertEquals("Skywalker", r1onServer1.field("lastName"));
assertEquals(r1onServer1.field("@version"), r1onServer2.field("@version"));
assertEquals(r1onServer1.field("id"), r1onServer2.field("id"));
assertEquals(r1onServer1.field("firstName"), r1onServer2.field("firstName"));
assertEquals(r1onServer1.field("lastName"), r1onServer2.field("lastName"));
// r1* is still present on server3
r1onServer3 = retrieveRecord(getDatabaseURL(serverInstance.get(2)), "R001");
assertEquals(2, r1onServer3.field("@version"));
assertEquals("R001", r1onServer3.field("id"));
assertEquals("Darth", r1onServer3.field("firstName"));
assertEquals("Vader", r1onServer3.field("lastName"));
// delete request on server1 for r1
dbServer1 = poolFactory.get(getRemoteDatabaseURL(serverInstance.get(0)), "admin", "admin").acquire();
try {
Integer result = dbServer1.command(new OCommandSQL("delete from " + r1Rid)).execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
dbServer1.close();
}
// r1 is no more present neither on server1, server2 nor server3
r1onServer1 = retrieveRecord(getDatabaseURL(serverInstance.get(0)), "R001", true, new OCallable<ODocument, ODocument>() {
@Override
public ODocument call(ODocument doc) {
assertEquals(MISSING_DOCUMENT, doc);
return null;
}
});
r1onServer2 = retrieveRecord(getDatabaseURL(serverInstance.get(1)), "R001", true, new OCallable<ODocument, ODocument>() {
@Override
public ODocument call(ODocument doc) {
assertEquals(MISSING_DOCUMENT, doc);
return null;
}
});
r1onServer3 = retrieveRecord(getDatabaseURL(serverInstance.get(2)), "R001", true, new OCallable<ODocument, ODocument>() {
@Override
public ODocument call(ODocument doc) {
assertEquals(MISSING_DOCUMENT, doc);
return null;
}
});
}
@Override
public String getDatabaseName() {
return "distributed-lazarus";
}
}