@Test
  public void test5CacheUpdatedMultipleDbs() {
    database1 = new ODatabaseDocumentTx(url).open("admin", "admin");
    database2 = new ODatabaseDocumentTx(url).open("admin", "admin");

    // Create docA in db1
    database1.begin(TXTYPE.OPTIMISTIC);
    ODocument vDocA_db1 = database1.newInstance();
    vDocA_db1.field(NAME, "docA");
    database1.save(vDocA_db1);
    database1.commit();

    // Keep the ID.
    ORID vDocA_Rid = vDocA_db1.getIdentity().copy();

    // Update docA in db2
    database2.begin(TXTYPE.OPTIMISTIC);
    ODocument vDocA_db2 = database2.load(vDocA_Rid);
    vDocA_db2.field(NAME, "docA_v2");
    database2.save(vDocA_db2);
    database2.commit();

    // Later... read docA with db1.
    database1.begin(TXTYPE.OPTIMISTIC);
    ODocument vDocA_db1_later = database1.load(vDocA_Rid, null, true);
    Assert.assertEquals(vDocA_db1_later.field(NAME), "docA_v2");
    database1.commit();

    database1.close();
    database2.close();
  }
  @Test
  public void test3RollbackWithCopyCacheStrategy() throws IOException {
    database1 = new ODatabaseDocumentTx(url).open("admin", "admin");
    database2 = new ODatabaseDocumentTx(url).open("admin", "admin");

    database1.getLevel2Cache().setStrategy(STRATEGY.COPY_RECORD);

    // Create docA.
    ODocument vDocA_db1 = database1.newInstance();
    vDocA_db1.field(NAME, "docA");
    database1.save(vDocA_db1);

    // Keep the IDs.
    ORID vDocA_Rid = vDocA_db1.getIdentity().copy();

    database2.begin(TXTYPE.OPTIMISTIC);
    try {
      // Get docA and update in db2 transaction context
      ODocument vDocA_db2 = database2.load(vDocA_Rid);
      vDocA_db2.field(NAME, "docA_v2");
      database2.save(vDocA_db2);

      database1.begin(TXTYPE.OPTIMISTIC);
      try {
        vDocA_db1.field(NAME, "docA_v3");
        database1.save(vDocA_db1);
        database1.commit();
      } catch (OConcurrentModificationException e) {
        Assert.fail("Should not failed here...");
      }
      Assert.assertEquals(vDocA_db1.field(NAME), "docA_v3");

      // Will throw OConcurrentModificationException
      database2.commit();
      Assert.fail("Should throw OConcurrentModificationException");
    } catch (OConcurrentModificationException e) {
      database2.rollback();
    }

    // Force reload all (to be sure it is not a cache problem)
    database1.close();
    database2.close();
    database2 = new ODatabaseDocumentTx(url).open("admin", "admin");

    // docB should be in the last state : "docA_v3"
    ODocument vDocB_db2 = database2.load(vDocA_Rid);
    Assert.assertEquals(vDocB_db2.field(NAME), "docA_v3");

    database1.close();
    database2.close();
  }
  private void createExceptionRecords(int count, boolean batch) throws Exception {
    ODatabaseDocumentTx database = null;
    try {
      database = ODatabaseDocumentPool.global().acquire(getDatabaseURL(), "admin", "admin");
      database.begin();
      for (int x = 0; x < count; x++) {
        String ID = UUID.randomUUID().toString();
        ODocument document = database.newInstance("ExceptionRecord");
        document.field("id", ID);
        document.field("data", DATA);
        document.field("status", "original");
        document.field("approved", false);
        document.field("groupNonException", false);
        document.field("comment", "");
        document.field("jobId", "1");
        document.field("dataflowName", "Error_Handling_V5");
        document.field("username", "admin");
        document.field("timestamp", new Date().getTime());
        document.field("stageLabel", "Exception Monitor");
        document.field("groupColumn", "");
        document.field("lastModifiedBy", "admin");
        document.field("lastModified", new Date());
        database.save(document);

        createExceptionRecordVersion(database, ID, "1");
        if (batch && (x % BATCH_SIZE) == 0) {
          System.out.println("Committing batch of " + BATCH_SIZE);
          database.commit();
          database.begin();
        }
      }
      database.commit();
    } catch (Throwable e) {
      database.rollback();
      throw e;
    } finally {
      if (database != null) {
        database.close();
      }
    }
  }
    private ODocument createRecord(ODatabaseDocumentTx database, int i) {
      final String uniqueId = serverId + "-" + threadId + "-" + i;

      ODocument person =
          new ODocument("Person")
              .fields(
                  "id",
                  UUID.randomUUID().toString(),
                  "name",
                  "Billy" + uniqueId,
                  "surname",
                  "Mayes" + uniqueId,
                  "birthday",
                  new Date(),
                  "children",
                  uniqueId);
      database.save(person);

      Assert.assertTrue(person.getIdentity().isPersistent());

      return person;
    }
  private void createExceptionRecordVersion(ODatabaseDocumentTx database, String id, String version)
      throws Exception {
    try {
      int numRetries = 0;
      while (numRetries < NUM_RETRIES) {
        try {
          ODocument document = database.newInstance("ExceptionRecordVersion");
          document.field("key-exceptionId", id);
          document.field("key-version", version);
          document.field("jobId", "1");
          document.field("dataflowName", "Error_Handling_V5");
          document.field("exceptionVersion-version", DATA);
          database.save(document);

          break;
        } catch (Throwable e) {
          logger.log(Level.SEVERE, "********************************");
          logger.log(
              Level.SEVERE,
              "Create Iteration="
                  + numRetries
                  + ", id="
                  + id
                  + ", version="
                  + version
                  + ", "
                  + e.toString(),
              e);
          logger.log(Level.SEVERE, "********************************");
          if (numRetries++ == NUM_RETRIES) {
            throw e;
          }
        }
      }
    } finally {
    }
  }
  @Test
  public void test1RollbackOnConcurrentException() throws IOException {
    database1 = new ODatabaseDocumentTx(url).open("admin", "admin");
    database2 = new ODatabaseDocumentTx(url).open("admin", "admin");

    database1.begin(TXTYPE.OPTIMISTIC);

    // Create docA.
    ODocument vDocA_db1 = database1.newInstance();
    vDocA_db1.field(NAME, "docA");
    database1.save(vDocA_db1);

    // Create docB.
    ODocument vDocB_db1 = database1.newInstance();
    vDocB_db1.field(NAME, "docB");
    database1.save(vDocB_db1);

    database1.commit();

    // Keep the IDs.
    ORID vDocA_Rid = vDocA_db1.getIdentity().copy();
    ORID vDocB_Rid = vDocB_db1.getIdentity().copy();

    ORecordVersion vDocA_version = OVersionFactory.instance().createUntrackedVersion();
    ORecordVersion vDocB_version = OVersionFactory.instance().createUntrackedVersion();

    database2.begin(TXTYPE.OPTIMISTIC);
    try {
      // Get docA and update in db2 transaction context
      ODocument vDocA_db2 = database2.load(vDocA_Rid);
      vDocA_db2.field(NAME, "docA_v2");
      database2.save(vDocA_db2);

      // Concurrent update docA via database1 -> will throw OConcurrentModificationException at
      // database2.commit().
      database1.begin(TXTYPE.OPTIMISTIC);
      try {
        vDocA_db1.field(NAME, "docA_v3");
        database1.save(vDocA_db1);
        database1.commit();
      } catch (OConcurrentModificationException e) {
        Assert.fail("Should not failed here...");
      }
      Assert.assertEquals(vDocA_db1.field(NAME), "docA_v3");
      // Keep the last versions.
      // Following updates should failed and reverted.
      vDocA_version = vDocA_db1.getRecordVersion();
      vDocB_version = vDocB_db1.getRecordVersion();

      // Update docB in db2 transaction context -> should be rollbacked.
      ODocument vDocB_db2 = database2.load(vDocB_Rid);
      vDocB_db2.field(NAME, "docB_UpdatedInTranscationThatWillBeRollbacked");
      database2.save(vDocB_db2);

      // Will throw OConcurrentModificationException
      database2.commit();
      Assert.fail("Should throw OConcurrentModificationException");
    } catch (OConcurrentModificationException e) {
      database2.rollback();
    }

    // Force reload all (to be sure it is not a cache problem)
    database1.close();
    database2.getStorage().close();
    database2 = new ODatabaseDocumentTx(url).open("admin", "admin");

    ODocument vDocA_db2 = database2.load(vDocA_Rid);
    Assert.assertEquals(vDocA_db2.field(NAME), "docA_v3");
    Assert.assertEquals(vDocA_db2.getRecordVersion(), vDocA_version);

    // docB should be in the first state : "docB"
    ODocument vDocB_db2 = database2.load(vDocB_Rid);
    Assert.assertEquals(vDocB_db2.field(NAME), "docB");
    Assert.assertEquals(vDocB_db2.getRecordVersion(), vDocB_version);

    database1.close();
    database2.close();
  }