docs: public MigrationReport migrate(Map<String, String> nodeMapping) {
  
          
          KieSession current = null;
          KieSession tobe = null;      
          TransactionManager txm = null;
          boolean transactionOwner = false;
          InternalRuntimeManager currentManager = (InternalRuntimeManager) RuntimeManagerRegistry.get().getManager(migrationSpec.getDeploymentId());
          InternalRuntimeManager toBeManager = (InternalRuntimeManager) RuntimeManagerRegistry.get().getManager(migrationSpec.getToDeploymentId());
  
          boolean migrateExecutorJobs = ((SimpleRuntimeEnvironment)currentManager.getEnvironment()).getEnvironmentTemplate().get("ExecutorService") != null;
          validate(migrateExecutorJobs);
          Map<Long, List<TimerInstance>> timerMigrated = null;
          try {
  
              // collect and cancel any active timers before migration
              timerMigrated = cancelActiveTimersBeforeMigration(currentManager);
  
              // start transaction to secure consistency of the migration			
              txm = TransactionManagerFactory.get().newTransactionManager(currentManager.getEnvironment().getEnvironment());
              transactionOwner = txm.begin();
  
              org.kie.api.definition.process.Process toBeProcess = toBeManager.getEnvironment().getKieBase().getProcess(migrationSpec.getToProcessId());
  
              String auditPu = currentManager.getDeploymentDescriptor().getAuditPersistenceUnit();
  
              EntityManagerFactory emf = EntityManagerFactoryManager.get().getOrCreate(auditPu);
              EntityManager em = emf.createEntityManager();
  
              try {
                  // update variable instance log information with new deployment id and process id
                  Query varLogQuery = em.createQuery("update VariableInstanceLog set externalId = :depId, processId = :procId where processInstanceId = :procInstanceId");
                  varLogQuery
                             .setParameter("depId", migrationSpec.getToDeploymentId())
                             .setParameter("procId", migrationSpec.getToProcessId())
                             .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
  
                  int varsUpdated = varLogQuery.executeUpdate();
                  report.addEntry(Type.INFO, "Variable instances updated = " + varsUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
  
                  // update node instance log information with new deployment id and process id
                  Query nodeLogQuery = em.createQuery("update NodeInstanceLog set externalId = :depId, processId = :procId where processInstanceId = :procInstanceId");
                  nodeLogQuery
                              .setParameter("depId", migrationSpec.getToDeploymentId())
                              .setParameter("procId", migrationSpec.getToProcessId())
                              .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
  
                  int nodesUpdated = nodeLogQuery.executeUpdate();
                  report.addEntry(Type.INFO, "Node instances updated = " + nodesUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
  
                  // update process instance log with new deployment and process id
                  Query pInstanceLogQuery = em.createQuery(
                                                           "update ProcessInstanceLog set externalId = :depId, processId = :procId, processName = :procName, processVersion= :procVersion where processInstanceId = :procInstanceId");
                  pInstanceLogQuery
                                   .setParameter("depId", migrationSpec.getToDeploymentId())
                                   .setParameter("procId", migrationSpec.getToProcessId())
                                   .setParameter("procName", toBeProcess.getName())
                                   .setParameter("procVersion", toBeProcess.getVersion())
                                   .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
  
                  int pInstancesUpdated = pInstanceLogQuery.executeUpdate();
                  report.addEntry(Type.INFO, "Process instances updated = " + pInstancesUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
  
                  try {
                      // update task audit instance log with new deployment and process id
                      Query taskVarLogQuery = em.createQuery("update TaskVariableImpl set processId = :procId where processInstanceId = :procInstanceId");
                      taskVarLogQuery
                                     .setParameter("procId", migrationSpec.getToProcessId())
                                     .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
  
                      int taskVarUpdated = taskVarLogQuery.executeUpdate();
                      report.addEntry(Type.INFO, "Task variables updated = " + taskVarUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
                  } catch (Throwable e) {
                      logger.warn("Unexpected error during migration", e);
                      report.addEntry(Type.WARN, "Cannot update task variables (added in version 6.3) due to " + e.getMessage());
                  }
  
                  // update task audit instance log with new deployment and process id
                  Query auditTaskLogQuery = em.createQuery("update AuditTaskImpl set deploymentId = :depId, processId = :procId where processInstanceId = :procInstanceId");
                  auditTaskLogQuery
                                   .setParameter("depId", migrationSpec.getToDeploymentId())
                                   .setParameter("procId", migrationSpec.getToProcessId())
                                   .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
  
                  int auditTaskUpdated = auditTaskLogQuery.executeUpdate();
                  report.addEntry(Type.INFO, "Task audit updated = " + auditTaskUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
  
                  // update task  instance log with new deployment and process id
                  Query taskLogQuery = em.createQuery("update TaskImpl set deploymentId = :depId, processId = :procId where processInstanceId = :procInstanceId");
                  taskLogQuery
                              .setParameter("depId", migrationSpec.getToDeploymentId())
                              .setParameter("procId", migrationSpec.getToProcessId())
                              .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
  
                  int taskUpdated = taskLogQuery.executeUpdate();
                  report.addEntry(Type.INFO, "Tasks updated = " + taskUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
  
                  try {
                      // update context mapping info with new deployment
                      Query contextInfoQuery = em.createQuery("update ContextMappingInfo set ownerId = :depId where contextId = :procInstanceId");
                      contextInfoQuery
                                      .setParameter("depId", migrationSpec.getToDeploymentId())
                                      .setParameter("procInstanceId", migrationSpec.getProcessInstanceId().toString());
  
                      int contextInfoUpdated = contextInfoQuery.executeUpdate();
                      report.addEntry(Type.INFO, "Context info updated = " + contextInfoUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
                  } catch (Throwable e) {
                      logger.warn("Unexpected error during migration", e);
                      report.addEntry(Type.WARN, "Cannot update context mapping owner (added in version 6.2) due to " + e.getMessage());
                  }
                  
                  if (migrateExecutorJobs) {
                      // update request info/executor with new deployment id
                      Query executorRequestQuery = em.createQuery("update RequestInfo set deploymentId = :depId where processInstanceId = :procInstanceId and status in ('ERROR')");
                      executorRequestQuery
                                  .setParameter("depId", migrationSpec.getToDeploymentId())                            
                                  .setParameter("procInstanceId", migrationSpec.getProcessInstanceId());
      
                      int executorRequestsUpdated = executorRequestQuery.executeUpdate();
                      report.addEntry(Type.INFO, "Executor Jobs updated = " + executorRequestsUpdated + " for process instance id " + migrationSpec.getProcessInstanceId());
                  }
                  current = JPAKnowledgeService.newStatefulKnowledgeSession(currentManager.getEnvironment().getKieBase(), null, currentManager.getEnvironment().getEnvironment());
                  tobe = toBeManager.getEnvironment().getKieBase().newKieSession();
                  upgradeProcessInstance(current, tobe, migrationSpec.getProcessInstanceId(), migrationSpec.getToProcessId(), nodeMapping, em, toBeManager.getIdentifier());
  
                  if (!timerMigrated.isEmpty()) {
                      rescheduleTimersAfterMigration(toBeManager, timerMigrated);
                  }
                  em.flush();
              } finally {
                  em.clear();
                  em.close();
              }
  
              txm.commit(transactionOwner);
              report.addEntry(Type.INFO, "Migration of process instance (" + migrationSpec.getProcessInstanceId() + ") completed successfully to process " + migrationSpec.getToProcessId());
              report.setSuccessful(true);
              report.setEndDate(new Date());
          } catch (Throwable e) {
              txm.rollback(transactionOwner);
              logger.error("Unexpected error during migration", e);
              // put back timers (if there are any) in case of rollback
              if (timerMigrated != null && !timerMigrated.isEmpty()) {
                  rescheduleTimersAfterMigration(currentManager, timerMigrated);
              }
              report.addEntry(Type.ERROR, "Migration of process instance (" + migrationSpec.getProcessInstanceId() + ") failed due to " + e.getMessage());
  
          } finally {
              if (current != null) {
                  try {
                      current.destroy();
                  } catch (SessionNotFoundException e) {
                      // in case of rollback session might not exist
                  }
              }
  
              if (tobe != null) {
  
                  try {
                      tobe.destroy();
                  } catch (SessionNotFoundException e) {
                      // in case of rollback session might not exist
                  }
              }
          }
  
          return report;
      }