use of com.orientechnologies.orient.server.distributed.conflict.ODistributedConflictResolver in project orientdb by orientechnologies.
the class OConflictResolverDatabaseRepairer method repairRecords.
private boolean repairRecords(final ODatabaseDocumentInternal db, final List<ORecordId> rids) {
final ODistributedConfiguration dCfg = dManager.getDatabaseConfiguration(databaseName);
final int maxAutoRetry = OGlobalConfiguration.DISTRIBUTED_CONCURRENT_TX_MAX_AUTORETRY.getValueAsInteger();
final int autoRetryDelay = OGlobalConfiguration.DISTRIBUTED_CONCURRENT_TX_AUTORETRY_DELAY.getValueAsInteger();
final ODistributedRequestId requestId = new ODistributedRequestId(dManager.getLocalNodeId(), dManager.getNextMessageIdCounter());
final ODistributedDatabase localDistributedDatabase = dManager.getMessageService().getDatabase(databaseName);
final ODistributedTxContext ctx = localDistributedDatabase.registerTxContext(requestId);
try {
// ACQUIRE LOCKS WITH A LARGER TIMEOUT
ODistributedTransactionManager.acquireMultipleRecordLocks(this, dManager, localDistributedDatabase, rids, maxAutoRetry, autoRetryDelay, null, ctx, 2000);
try {
final Set<String> clusterNames = new HashSet();
for (ORecordId rid : rids) clusterNames.add(db.getClusterNameById(rid.getClusterId()));
final Collection<String> involvedServers = dCfg.getServers(clusterNames);
final Set<String> nonLocalServers = new HashSet<String>(involvedServers);
nonLocalServers.remove(dManager.getLocalNodeName());
if (nonLocalServers.isEmpty())
return true;
// CREATE LOCAL RESULT
final OTxTaskResult localResult = new OTxTaskResult();
for (ORecordId rid : rids) {
final OStorageOperationResult<ORawBuffer> res;
if (rid.getClusterPosition() > -1)
res = db.getStorage().readRecord(rid, null, true, false, null);
else
res = null;
if (res != null)
localResult.results.add(res.getResult());
else
localResult.results.add(null);
}
ODistributedServerLog.debug(this, dManager.getLocalNodeName(), involvedServers.toString(), ODistributedServerLog.DIRECTION.OUT, "Auto repairing records %s on servers %s (reqId=%s)...", rids, involvedServers, requestId);
// CREATE TX TASK
final ORepairRecordsTask tx = new ORepairRecordsTask();
for (ORecordId rid : rids) tx.add(new OReadRecordTask(rid));
ODistributedResponse response = dManager.sendRequest(databaseName, clusterNames, nonLocalServers, tx, requestId.getMessageId(), ODistributedRequest.EXECUTION_MODE.RESPONSE, localResult, null);
// MAP OF OCompletedTxTask SERVER/RECORDS. RECORD == NULL MEANS DELETE
final Map<String, OCompleted2pcTask> repairMap = new HashMap<String, OCompleted2pcTask>(rids.size());
for (String server : involvedServers) {
final OCompleted2pcTask completedTask = new OCompleted2pcTask(requestId, false, tx.getPartitionKey());
repairMap.put(server, completedTask);
}
try {
if (response != null) {
final Object payload = response.getPayload();
if (payload instanceof Map) {
final Map<String, Object> map = (Map<String, Object>) payload;
// BROWSE FROM LOCAL RESULT
for (int i = 0; i < localResult.results.size(); ++i) {
final Map<Object, List<String>> groupedResult = new HashMap<Object, List<String>>();
final ORecordId rid = rids.get(i);
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof Throwable) {
// ABORT IT
ODistributedServerLog.info(this, dManager.getLocalNodeName(), entry.getKey(), ODistributedServerLog.DIRECTION.IN, "Error on auto repairing record %s on servers %s (error=%s)", rid, entry.getKey(), entry.getValue());
return false;
}
final OTxTaskResult v = (OTxTaskResult) entry.getValue();
final Object remoteValue = v.results.get(i);
List<String> group = groupedResult.get(remoteValue);
if (group == null) {
group = new ArrayList<String>();
groupedResult.put(remoteValue, group);
}
group.add(entry.getKey());
}
if (groupedResult.size() == 1)
// NO CONFLICT, SKIP IT
continue;
ODocument config = null;
// EXECUTE THE CONFLICT RESOLVE PIPELINE: CONTINUE UNTIL THE WINNER IS NOT NULL (=RESOLVED)
Object winner = null;
Map<Object, List<String>> candidates = groupedResult;
for (ODistributedConflictResolver conflictResolver : conflictResolvers) {
final ODistributedConflictResolver.OConflictResult conflictResult = conflictResolver.onConflict(databaseName, db.getClusterNameById(rid.getClusterId()), rid, dManager, candidates, config);
winner = conflictResult.winner;
if (winner != null)
// FOUND WINNER
break;
candidates = conflictResult.candidates;
}
if (winner == null)
// NO WINNER, SKIP IT
continue;
for (Map.Entry<Object, List<String>> entry : groupedResult.entrySet()) {
final Object value = entry.getKey();
final List<String> servers = entry.getValue();
for (String server : servers) {
ODistributedServerLog.debug(this, dManager.getLocalNodeName(), server, ODistributedServerLog.DIRECTION.OUT, "Preparing fix for record %s on servers %s, value=%s...", rid, server, winner);
if (!winner.equals(value)) {
final OCompleted2pcTask completedTask = repairMap.get(server);
if (winner instanceof ORawBuffer && value instanceof ORawBuffer) {
// UPDATE THE RECORD
final ORawBuffer winnerRecord = (ORawBuffer) winner;
completedTask.addFixTask(new OFixUpdateRecordTask(rid, winnerRecord.buffer, ORecordVersionHelper.setRollbackMode(winnerRecord.version), winnerRecord.recordType));
} else if (winner instanceof ORecordNotFoundException && value instanceof ORawBuffer) {
// DELETE THE RECORD
completedTask.addFixTask(new OFixCreateRecordTask(rid, -1));
} else if (value instanceof Throwable) {
// MANAGE EXCEPTION
}
}
}
}
}
}
}
} finally {
int repaired = 0;
for (Map.Entry<String, OCompleted2pcTask> entry : repairMap.entrySet()) {
final String server = entry.getKey();
final OCompleted2pcTask task = entry.getValue();
repaired += task.getFixTasks().size();
if (dManager.getLocalNodeName().equals(server))
// EXECUTE IT LOCALLY
dManager.executeOnLocalNode(requestId, task, db);
else {
// EXECUTE REMOTELY
final List<String> servers = new ArrayList<String>();
servers.add(server);
// FILTER ONLY THE SERVER ONLINE
dManager.getAvailableNodes(servers, databaseName);
if (!servers.isEmpty()) {
response = dManager.sendRequest(databaseName, clusterNames, servers, task, dManager.getNextMessageIdCounter(), ODistributedRequest.EXECUTION_MODE.RESPONSE, null, null);
}
}
}
if (repaired == 0)
ODistributedServerLog.debug(this, dManager.getLocalNodeName(), involvedServers.toString(), ODistributedServerLog.DIRECTION.OUT, "Auto repairing completed. No fix is needed (reqId=%s)", repaired, requestId);
else
ODistributedServerLog.info(this, dManager.getLocalNodeName(), involvedServers.toString(), ODistributedServerLog.DIRECTION.OUT, "Auto repairing completed. Sent %d fix messages for %d records (reqId=%s)", repaired, rids.size(), requestId);
}
} finally {
// RELEASE LOCKS AND REMOVE TX CONTEXT
localDistributedDatabase.popTxContext(requestId);
ctx.destroy();
}
} catch (Throwable e) {
ODistributedServerLog.debug(this, dManager.getLocalNodeName(), null, ODistributedServerLog.DIRECTION.NONE, "Error executing auto repairing (error=%s, reqId=%s)", e.toString(), requestId);
return false;
}
return true;
}
Aggregations