/*
*
* * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.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.
* *
* * For more information: http://www.orientechnologies.com
*
*/
package com.orientechnologies.orient.server.distributed.impl.task;
import com.orientechnologies.orient.core.command.OCommandDistributedReplicateRequest;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.server.OServer;
import com.orientechnologies.orient.server.distributed.*;
import com.orientechnologies.orient.server.distributed.ODistributedServerLog.DIRECTION;
import com.orientechnologies.orient.server.distributed.task.OAbstractReplicatedTask;
import com.orientechnologies.orient.server.distributed.task.ORemoteTask;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Task to manage the end of distributed transaction when no fix is needed (OFixTxTask) and all the locks must be released. Locks
* are necessary to prevent concurrent modification of records before the transaction is finished. <br>
* This task uses the same partition keys used by TxTask to avoid synchronizing all the worker threads (and queues).
*
* @author Luca Garulli (l.garulli--at--orientechnologies.com)
*
*/
public class OCompleted2pcTask extends OAbstractReplicatedTask {
private static final long serialVersionUID = 1L;
public static final int FACTORYID = 8;
private ODistributedRequestId requestId;
private boolean success;
private List<ORemoteTask> fixTasks = new ArrayList<ORemoteTask>();
private int[] partitionKey;
public OCompleted2pcTask() {
partitionKey = ALL;
}
public OCompleted2pcTask(final ODistributedRequestId iRequestId, final boolean iSuccess, final int[] partitionKey) {
this.requestId = iRequestId;
this.success = iSuccess;
this.partitionKey = partitionKey != null ? partitionKey : ALL;
}
/**
* This task uses the same partition keys used by TxTask to avoid synchronizing all the worker threads (and queues).
*/
@Override
public int[] getPartitionKey() {
return partitionKey;
}
public void addFixTask(final ORemoteTask fixTask) {
fixTasks.add(fixTask);
}
@Override
public Object execute(final ODistributedRequestId msgId, final OServer iServer, ODistributedServerManager iManager,
final ODatabaseDocumentInternal database) throws Exception {
ODistributedServerLog.debug(this, iManager.getLocalNodeName(), getNodeSource(), DIRECTION.IN,
"%s transaction db=%s originalReqId=%s...", (success ? "Committing" : fixTasks.isEmpty() ? "Rolling back" : "Fixing"),
database.getName(), requestId, requestId);
ODatabaseRecordThreadLocal.INSTANCE.set(database);
// UNLOCK ALL LOCKS ACQUIRED IN TX
final ODistributedDatabase ddb = iManager.getMessageService().getDatabase(database.getName());
if (ddb == null)
throw new ODatabaseException(
"Database '" + database.getName() + " is not available on server '" + iManager.getLocalNodeName() + "'");
final ODistributedTxContext pRequest = ddb.popTxContext(requestId);
try {
if (success) {
// COMMIT
if (pRequest != null)
pRequest.commit();
else {
// UNABLE TO FIND TX CONTEXT
ODistributedServerLog.debug(this, iManager.getLocalNodeName(), getNodeSource(), DIRECTION.IN,
"Error on committing distributed transaction %s db=%s", requestId, database.getName());
return Boolean.FALSE;
}
} else if (fixTasks.isEmpty()) {
// ROLLBACK
if (pRequest != null)
pRequest.rollback(database);
else {
// UNABLE TO FIND TX CONTEXT
ODistributedServerLog.debug(this, iManager.getLocalNodeName(), getNodeSource(), DIRECTION.IN,
"Error on rolling back distributed transaction %s db=%s", requestId, database.getName());
return Boolean.FALSE;
}
} else {
// FIX TRANSACTION CONTENT
if (pRequest != null)
pRequest.fix(database, fixTasks);
else {
// UNABLE TO FIX TX CONTEXT
ODistributedServerLog.debug(this, iManager.getLocalNodeName(), getNodeSource(), DIRECTION.IN,
"Error on fixing distributed transaction %s db=%s", requestId, database.getName());
return Boolean.FALSE;
}
}
} finally {
if (pRequest != null)
pRequest.destroy();
}
return Boolean.TRUE;
}
@Override
public OCommandDistributedReplicateRequest.QUORUM_TYPE getQuorumType() {
return OCommandDistributedReplicateRequest.QUORUM_TYPE.NONE;
}
@Override
public void toStream(final DataOutput out) throws IOException {
requestId.toStream(out);
out.writeBoolean(success);
out.writeInt(fixTasks.size());
for (ORemoteTask task : fixTasks) {
out.writeByte(task.getFactoryId());
task.toStream(out);
}
out.writeInt(partitionKey.length);
for (int pk : partitionKey)
out.writeInt(pk);
}
@Override
public void fromStream(final DataInput in, ORemoteTaskFactory taskFactory) throws IOException {
requestId = new ODistributedRequestId();
requestId.fromStream(in);
success = in.readBoolean();
final int tasksSize = in.readInt();
for (int i = 0; i < tasksSize; ++i) {
final ORemoteTask task = taskFactory.createTask(in.readByte());
task.fromStream(in, taskFactory);
fixTasks.add(task);
}
final int pkSize = in.readInt();
partitionKey = new int[pkSize];
for (int i = 0; i < pkSize; ++i)
partitionKey[i] = in.readInt();
}
/**
* Computes the timeout according to the transaction size.
*
* @return
*/
@Override
public long getDistributedTimeout() {
return OGlobalConfiguration.DISTRIBUTED_CRUD_TASK_SYNCH_TIMEOUT.getValueAsLong();
}
@Override
public String getName() {
return "tx-completed";
}
@Override
public int getFactoryId() {
return FACTORYID;
}
@Override
public String toString() {
return getName() + " origReqId: " + requestId + " type: "
+ (success ? "commit" : (fixTasks.isEmpty() ? "rollback" : "fix (" + fixTasks.size() + " ops) [" + fixTasks + "]"));
}
public List<ORemoteTask> getFixTasks() {
return fixTasks;
}
}